You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 8 Next »

Grouper Solution to the CMU Billing Use Case

Namespace

First create a section of the folder namespace for this application, e.g.

edu:cmu:it:apps:billing

GSH (note, you could do this with UI/WS too):

gsh 0% grouperSession = GrouperSession.startRootSession();
gsh 1% billingFolder = new StemSave(grouperSession).assignName("edu:cmu:it:apps:billing").assignCreateParentStemsIfNotExist(true).save();

Grant that folder to the development team for this application, and for the service principal the application uses to access the deployment of Grouper.  I recommend making an admin group for this application, and adding that group to the the proper privilege lists.  e.g. edu:cmu:it:apps:billing:groups:admins

GSH (note, you could do this with UI/WS too):

gsh 2% billingAdmins = new GroupSave(grouperSession).assignName("edu:cmu:it:apps:billing:groups:admins").assignCreateParentStemsIfNotExist(true).save();
gsh 3% billingFolder.grantPriv(billingAdmins.toSubject(), NamingPrivilege.STEM);
gsh 4% billingFolder.grantPriv(billingAdmins.toSubject(), NamingPrivilege.CREATE);
gsh 5% tgd = SubjectFinder.findById("tgd", true);
gsh 6% billingAdmins.addMember(tgd);

Roles

The application would need the following roles:

  • edu:cmu:it:apps:billing:roles:universityBillingAdministrator
  • edu:cmu:it:apps:billing:roles:student
  • edu:cmu:it:apps:billing:roles:studentDelegate
  • edu:cmu:it:apps:billing:roles:localBillingAdministrator

GSH (note, you could do this with UI/WS too, though you might need GSH to switch types to Role)

gsh 7% universityBillingAdministratorRole = new GroupSave(grouperSession).assignName("edu:cmu:it:apps:billing:roles:universityBillingAdministrator").assignSaveMode(SaveMode.INSERT_OR_UPDATE).assignCreateParentStemsIfNotExist(true).assignTypeOfGroup(TypeOfGroup.role).save();
gsh 8% studentRole = new GroupSave(grouperSession).assignName("edu:cmu:it:apps:billing:roles:student").assignSaveMode(SaveMode.INSERT_OR_UPDATE).assignCreateParentStemsIfNotExist(true).assignTypeOfGroup(TypeOfGroup.role).save();
gsh 9% studentDelegateRole = new GroupSave(grouperSession).assignName("edu:cmu:it:apps:billing:roles:studentDelegate").assignSaveMode(SaveMode.INSERT_OR_UPDATE).assignCreateParentStemsIfNotExist(true).assignTypeOfGroup(TypeOfGroup.role).save();
gsh 10% localBillingAdministratorRole = new GroupSave(grouperSession).assignName("edu:cmu:it:apps:billing:roles:localBillingAdministrator").assignSaveMode(SaveMode.INSERT_OR_UPDATE).assignCreateParentStemsIfNotExist(true).assignTypeOfGroup(TypeOfGroup.role).save();

Note that security  for who can read those roles, or assign, would be given to the group edu.cmu.it.apps.billing.groups.admins.

GSH (note, you could do this with UI/WS too):

gsh 11% universityBillingAdministratorRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.READ);
gsh 12% universityBillingAdministratorRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.UPDATE);
gsh 13% studentRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.READ);
gsh 14% studentRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.UPDATE);
gsh 15% studentDelegateRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.READ);
gsh 16% studentDelegateRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.UPDATE);
gsh 17% localBillingAdministratorRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.READ);
gsh 18% localBillingAdministratorRole.grantPriv(billingAdmins.toSubject(), AccessPrivilege.UPDATE);

You might assign the student role to the cmu overall student group (e.g. edu:cmu:community:students).  Then as new students are provisioned into that group from the student system, they will automatically be assigned the role in this application.  And as students fall out of the overall students group, they will lose the role in this application.  If there are exceptions, you can make a group that is Grouper includeExclude so that you can have additions or restrictions from the system of record group.  The students group could be provisioned via the Grouper Loader, or other means.

GSH / SQL (note, the Grouper commands can be done with the UI / WS / client).  Create the students group, synced from a source system (simulated by using a static sql table)

Create a table for students (note, in reality this would be a view)

CREATE TABLE cmu_student (
  student_id varchar(50) NOT NULL,
  PRIMARY KEY  (student_id)
);

Populate with some students:

insert into cmu_student (student_id) values ('babl');
insert into cmu_student (student_id) values ('mchyzer');
insert into cmu_student (student_id) values ('babr');
insert into cmu_student (student_id) values ('babu');
commit;

Create a student group:

gsh 18% studentsGroup = new GroupSave(grouperSession).assignName("edu:cmu:community:students").assignSaveMode(SaveMode.INSERT_OR_UPDATE).assignCreateParentStemsIfNotExist(true).save();

Add a loader job to auto-populate students:

gsh 19% groupAddType("edu:cmu:community:students", "grouperLoader");
gsh 20% setGroupAttr("edu:cmu:community:students", "grouperLoaderType", "SQL_SIMPLE");
gsh 21% setGroupAttr("edu:cmu:community:students", "grouperLoaderDbName", "grouper");
gsh 22% setGroupAttr("edu:cmu:community:students", "grouperLoaderScheduleType", "CRON");
gsh 23% setGroupAttr("edu:cmu:community:students", "grouperLoaderQuartzCron", "0 0 7 * * ?");
gsh 24% setGroupAttr("edu:cmu:community:students", "grouperLoaderQuery", "SELECT student_id AS subject_id, 'jdbc' AS subject_source_id FROM cmu_student");

Run the loader one time to init:

gsh 25% studentsGroup = GroupFinder.findByName(grouperSession, "edu:cmu:community:students", true);
gsh 26% loaderRunOneJob(studentsGroup);

Kick off the job and run loader automatically so it runs daily:

[appadmin@i2midev1 bin]$ cd /opt/grouper/1.6.1/grouper.apiBinary-1.6.1/bin
[appadmin@i2midev1 bin]$ ./gsh.sh -loader > /tmp/grouper.1.6.1_loader.log 2>&1 &

See that the group has the people it should:

gsh 27% studentsGroup = GroupFinder.findByName(grouperSession, "edu:cmu:community:students", true);

gsh 28% studentsGroup.getMembers();
member: id='babl' type='person' source='jdbc' uuid='39ce9502bf2d4924bd920611b31832b3'
member: id='babr' type='person' source='jdbc' uuid='4117432df1244536bbc49562b988d7f8'
member: id='babu' type='person' source='jdbc' uuid='db2ec9441c2f42d3b42fcaf8f56d6217'
member: id='mchyzer' type='person' source='jdbc' uuid='b00465a3015547388caa3b422ea0c659'

Make the studentRole an includeExclude, and add the student group to the system of record part.  This means that generally students in the students group have the role, though you could ad hoc add or subtract from that withotu affecting the loaded group.  This can also be done with the UI, WS, or client

gsh 5% groupAddType("edu:cmu:it:apps:billing:roles:student", "addIncludeExclude");
gsh 6% addMember("edu:cmu:it:apps:billing:roles:student_systemOfRecord", "edu:cmu:community:students");

For the university wide billing administrator and the local billing administrator roles, you should probably create a Grouper restrictGroup where you look at what the person must be to have the role.  If what they must be is an active staff member, then setup the restrictGroup so that if the user ever stops being an activeStaff member, they automatically fall out of the university or local billing administrator role.  If it is wider, then make it an active CMU member, etc.

GSH / SQL (note, the Grouper commands can be done with the UI / WS / client).  Create the employees group, synced from a source system (simulated by using a static sql table)

Create a table for employees (note, in reality this would be a view)

CREATE TABLE cmu_employee (
  employee_id varchar(50) NOT NULL,
  PRIMARY KEY  (employee_id)
);

Populate with some employees:

insert into cmu_employee (employee_id) values ('elbl');
insert into cmu_employee (employee_id) values ('dousti');
insert into cmu_employee (employee_id) values ('elbr');
insert into cmu_employee (employee_id) values ('elbu');
commit;

Create an employee group:

gsh 18% employeesGroup = new GroupSave(grouperSession).assignName("edu:cmu:community:employees").assignSaveMode(SaveMode.INSERT_OR_UPDATE).assignCreateParentStemsIfNotExist(true).save();

Add a loader job to auto-populate students:

gsh 19% groupAddType("edu:cmu:community:employees", "grouperLoader");
gsh 20% setGroupAttr("edu:cmu:community:employees", "grouperLoaderType", "SQL_SIMPLE");
gsh 21% setGroupAttr("edu:cmu:community:employees", "grouperLoaderDbName", "grouper");
gsh 22% setGroupAttr("edu:cmu:community:employees", "grouperLoaderScheduleType", "CRON");
gsh 23% setGroupAttr("edu:cmu:community:employees", "grouperLoaderQuartzCron", "0 0 7 * * ?");
gsh 24% setGroupAttr("edu:cmu:community:employees", "grouperLoaderQuery", "SELECT employee_id AS subject_id, 'jdbc' AS subject_source_id FROM cmu_employee");

Run the loader one time to init:

gsh 25% employeesGroup = GroupFinder.findByName(grouperSession, "edu:cmu:community:employees", true);
gsh 26% loaderRunOneJob(employeesGroup);

Kick off the job and run loader automatically so it runs daily:

[appadmin@i2midev1 bin]$ cd /opt/grouper/1.6.1/grouper.apiBinary-1.6.1/bin
[appadmin@i2midev1 bin]$ ./gsh.sh -loader > /tmp/grouper.1.6.1_loader.log 2>&1 &

See that the group has the people it should:

gsh 27% employeesGroup = GroupFinder.findByName(grouperSession, "edu:cmu:community:employees", true);

gsh 28% employeesGroup.getMembers();
member: id='babl' type='person' source='jdbc' uuid='39ce9502bf2d4924bd920611b31832b3'
member: id='babr' type='person' source='jdbc' uuid='4117432df1244536bbc49562b988d7f8'
member: id='babu' type='person' source='jdbc' uuid='db2ec9441c2f42d3b42fcaf8f56d6217'
member: id='mchyzer' type='person' source='jdbc' uuid='b00465a3015547388caa3b422ea0c659'

Assignment to the universityBillingAdministrator role could have start and end dates.  Also, that group would be added to the privilege READ/UPDATE lists for that role and other roles.  This facilitates letting users who are universityBillingAdministrators assign that role or other roles to other users.  Note that each name in grouper has a system name and a display name, so the screens in the Grouper UI will make more sense to non-developers.

Permission resources

There are two tracks of permission resources, the ones specific to this application, and potentially the re-use the organization-wide org chart.  If the school has a university wide org chart of resources which matches the requirements, it can be re-used.  This is what Penn plans to do.

So the org chart looks like this:

edu.cmu.community.resources.orgs.UNIV.USCH.02XX.CGSP
edu.cmu.community.resources.orgs.UNIV.USCH.02XX.CGSP.CGSM
edu.cmu.community.resources.orgs.UNIV.USCH.02XX.CGSP.CGSM.0174
edu.cmu.community.resources.orgs.UNIV.USCH.02XX.CGSP.CGSM.2108

And there is a hierarchy where if you are assigned CGSP, then you also have the decendents of that node.

There is probably a group who is allowed to read and/or assign these privilege resources, and add the admins of this billing application to that group.

The second track of permission resources for the billing application is local permission resources.  There might only be one "permission definition" which holds all the security and properties for the privileges.  Or there could be one definition per resource type.  I dont know if there is READ and WRITE actions, but if so, that might help decide if there are multiple definitions.  If all resources have READ and WRITE, then you can use one definition.  If some have ASSIGN, and some have READ/WRITE, and some have READ, then you need multiple.

  • edu.cmu.it.apps.billing.permissionDefs.billingPermissions

This definition has multiple resource names associated with it, e.g.

  • edu.cmu.it.apps.billing.permissions.ownBills
  • edu.cmu.it.apps.billing.permissions.allBills
  • edu.cmu.it.apps.billing.permissions.canDelegateOwnBills

For the delegation, it is probably best to make these resources dynamically created as needed.  The space for them is the folder: edu.cmu.it.apps.billing.permissions.students, and the name of the resource would be the student id of the user.  An example would be: edu.cmu.it.apps.billing.permissions.students.12345678.  There are other ways to accomplish this, for example an attribute could have a freeform value which could hold the student ID, or a permission assignment could have an attribute assignment on top of it to qualify it.  That attribute could hold the free form value.  However, in this case I will just assume the billing application could create the permissions as needed.   So when a student wants to delegate the viewing of bills, they click the button on the screen to delegate, and:

  • The system sees if the user to be delegated to, has the role: edu.cmu.it.apps.billing.roles.studentDelegate
    • If not, assign that role to the uesr being delegated to
  • The system sees if the student delegating from (whose id is 12341234), has a permission resource in the right folder:
    • edu.cmu.it.apps.billing.permissions.students.12341234
    • If that resource doesnt exist, create it
  • The system will assign that resource to the user being delegated to in the context of the role: edu.cmu.it.apps.billing.roles.studentDelegate

When specifying a local billing administrator, the system wide admin would assign the role edu.cmu.it.apps.billing.roles.localBillingAdministrator to the user, and will assign READ to one of the orgs in the global shared org chart.

Note: a privilege resource assignment in Grouper can be marked with a delegation flag of T (true), F (false), or G (grant, it is true, and the recipient can grant the delegation flag to whom they delegate to).  Not sure if this is useful for this application or not.

Checking permissions

Currently as of 11/3/9, the only permission management interfaces from Grouper are SQL (definitely), and LDAP (theoreticall), but we will be building web services soon.  The application will either need to check permissions dynamically, or export all the relevant permissions periodically, or for a user on their login.  If there are SQL joins e.g. to list all the students someone can VIEW, then you might need the 2nd or third option to get all the data together.  The following decision making process needs to occur:

Can user X see bill for user Y:

  • If user X has permission: edu.cmu.it.apps.billing.permissions.viewAllBills in the role edu.cmu.it.apps.billing.roles.universityBillingAdministrator, then the answer is YES
  • If Y user is the same as X, and X has edu.cmu.it.apps.billing.permissions.viewOwnBills in the role edu.cmu.it.apps.billing.roles.student, then the answer is YES
  • If Y has the ID of 99887766, and X has the permission READ for edu.cmu.it.apps.billing.permissions.students.99887766 in the role edu.cmu.it.apps.billing.roles.studentDelegate, then the answer is YES
  • Find the orgs that user Y is associated with.  Assume it is org A:B:C, and D:E:F.  See if X can READ either edu.cmu.community.resources.orgs.UNIV.USCH.A.B.C or edu.cmu.community.resources.orgs.UNIV.USCH.D.E.F for the role: edu.cmu.it.apps.billing.roles.localBillingAdministrator.  If so, then the answer is YES
  • Else, the answer is no.
  • No labels