Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Designing authorization rules within an Authorization OSID Provider can provide visibility in who has access to what and simplify base service implementations. Working from the authorization evaluation perspective can solve the puzzle that is difficult to see through data attributesa difficult puzzle challenging for experienced architects. This is a case study of a project that tackles this issue.

...

The application programmer and OSID implementor collaborate to define an OsidRecord for these extra lists of Organizations. The application populates them from user input on a hold administrative screen. 

organizationIssueRecord

Holds are tested and they can be created or removed by anyone. The product owner tells the application programmer that these organizations should be checked so that only people inside the organization are allowed to perform these operations.

The application programmer scratches his head, and looks to see how he can figure out who belongs to what organization. He looks to the Personnel OSID to answer this question and sees that Persons are related to Organizations via Appointments and Positions. Dismayed at the bizarre complexity of the situation, shovels out the following code:

...

titleApplication Authorization

...

MethodgetHoldCreatorOrganizationIds
DescriptionGets the list of Organization Ids who can create Holds using this Issue.
Returnosid.id.IdListthe list of Organization Ids
CompliancemandatoryThis method must be implemented.
MethodgetHoldCreatorOrganizations
DescriptionGets the list of Organizations who can create Holds using this Issue.
Returnosid.personnel.OrganizationListthe list of Organizations
ErrorsOPERATION_FAILEDunable to complete request
CompliancemandatoryThis method must be implemented.
MethodgetHoldUpdaterOrganizationIds
DescriptionGets the list of Organization Ids who can update Holds of this Issue.
Returnosid.id.IdList the list of Organization Ids
CompliancemandatoryThis method must be implemented.
MethodgetHoldUpdaterOrganizations
DescriptionGets the list of Organizations who can update Holds of this Issue.
Returnosid.personnel.OrganizationList the list of Organizations
ErrorsOPERATION_FAILED unable to complete request
CompliancemandatoryThis method must be implemented.

The product owner tells the application programmer that these organizations should be checked so that only people inside the organization are allowed to perform these operations.

The application programmer scratches his head, and looks to see how he can figure out who belongs to what organization. He looks to the Personnel OSID to answer this question and sees that Persons are related to Organizations via Appointments and Positions. Dismayed at the bizarre complexity of the situation, shovels out the following code:

Code Block
titleApplication Authorization
boolean checkPlaceHold(org.osid.id.Id issueId, org.osid.id.Id agentId)
    throws org.osid.NotFoundException,
           org.osid.OperationFailedException,
           org.osid.PermissionDeniedException {
    org.osid.resource.ResourceAgentSession resourceAgentSession = resourceMgr.getResourceAgentSession();
 
    // I'll assume the resourceId is the same as the personId
    org.osid.id.Id resourceId = resourceAgentSession.getResourceIdByAgent(agentId);
 
    org.osid.hold.IssueLookupSession issueLookupSession = holdMgr.getIssueLookupSession();
    org.osid.hold.Issue issue = issueLookupSession.getIssue(issueId);
    
    // our local org data
    OrganizationHoldRecordOrganizationIssueRecord record = (OrganizationHoldRecordOrganizationIssueRecord) issue.getIssueRecord(organizationHoldRecordTypeorganizationIssueRecordType);
 
    // could they have made this any more difficult!
    org.osid.personnel.AppointmentLookupSession appointmentLookupSession = personnelMgr.getAppointmentLookupSession();
    appointmentLookupSession.useEffectiveAppointmentView();
    org.osid.personnel.PositionLookupSession positionLookupSession = personnelMgr.getPositionLookupSession();
    positionLookupSession.useEffectivePositionView();
 
    // get the positions of an org - blasted there's no way to get a list of people in an org!
    try (org.osid.id.IdList orgIds = record.getOrgIdsWhoCanPlaceHoldgetHoldCreatorOrganizationIds()) {
        while (orgIds.hasNext()) {
            org.osid.id.Id orgId = orgIds.getNextId();
            try (org.osid.personnel.PositionList positions = positionLookupSession.getPositionsForOrganization(orgId)) {
                while (positions.hasNext()) {
                    org.osid.personnel.Position position = positions.getNextPosition();
                    try (org.osid.personnel.AppintmentList appointments = appointmentLookupSession.getAppointmentsForPersonAndPosition(resourceId, position.getNextId()) {
                        if (appointments.hasNext()) {
                            return (true);
                        }
                    }
                }
            }
        }
    }
 
    return (false);
}            

...

Code Block
titleAuthorization In The Hold OSID Provider
public class HoldAdminSession
    extends net.okapia.osid.jamocha.adapter.hold.spi.AbatractAdapterHoldAdminSession
    implements org.osid.hold.HoldAdminSession
 
    private final org.osid.authorization.AuthorizationSession authzSession;
    private static final org.osid.id.Id CREATEHOLD_FUNCTION_ID;
    private static final org.osid.id.Id UPDATEHOLD_FUNCTION_ID;
    private static final org.osid.id.Id DELETEHOLD_FUNCTION_ID;
 
    HoldAdminSession(org.osid.hold.HoldAdminSession session, org.osid.authorization.AuthorizationSession authzSession) {
        super(session);
        this.authzSession = authzSession;
        return;
    }
    public org.osid.hold.HoldForm getHoldFormForCreate(org.osid.id.Id issueId, org.osid.id.Id resourceId, org.osid.type.Type[] recordTypes) {
        if (this.authzSession.isAuthorized(getAuthenticatedAgentId(), CREATEHOLD_FUNCTION_ID, issueId) {
            throw org.osid.PermissionDeniedException();
        }
        
        // wrap the form so we need can get the issueId on the way back in
        return (new HoldFormAdapter(super.getHoldFormForCreate(issueId, resourceId, recordTypes), issueId);
    }
 
    public org.osid.hold.Hold createHold(org.osid.hold.HoldForm form) {
        if (this.authzSession.isAuthorized(getAuthenticatedAgentId(), CREATEHOLD_FUNCTION_ID, getIssueId(form)) {
            throw org.osid.PermissionDeniedException();
        }
 
        return (super.createHold(form));
    }
    public org.osid.hold.HoldForm getHoldFormForUpdate(org.osid.id.Id holdId) {
        if (this.authzSession.isAuthorized(getAuthenticatedAgentId(), UPDATEHOLD_FUNCTION_ID, holdId) {
            throw org.osid.PermissionDeniedException();
        }
    
        // wrap the form so we need can get the issueId on the way back in
        return (new HoldFormAdapter(super.getHoldFormForUpdate(holdId), holdId);
    }
 
    public org.osid.hold.Hold updateHold(org.osid.hold.HoldForm form) {
        if (this.authzSession.isAuthorized(getAuthenticatedAgentId(), UPDATEHOLD_FUNCTION_ID, getIssueId(form)) {
            throw org.osid.PermissionDeniedException();
        }

        return (super.updateHold(form));
    }
    public void deleteHold(org.osid.id.Id holdId) {
        if (this.authzSession.isAuthorized(getAuthenticatedAgentId(), DELETEHOLD_FUNCTION_ID, holdId) {
            throw org.osid.PermissionDeniedException();
        }
    
        return (super.deleteHold(holdId));
    }
 
    private static org.osid.id.Id getIssueId(org.osid.hold.HoldForm form) {
        if (!(form instance of HoldFormAdapter)) {
            throw new org.osid.InvalidArgumentException("not my form!");
        }
 
        return (((HoldFormAdapter) form).getIssueId());
    }
}

The Gap

The next step is to note the gap between the above Authorizations and the way the project wants to manage Authorizations. Somehow we need to get from Issues and Holds to Organizations. 

Bouncing Off The Organization Wall

Working from the Organization side, the service architect needs to map Organizations to Issues. While there is an owning Organization in the Issue, it doesn't capture the nuance that separate Organizations have access to place and remove Holds. She is already aware that this translates into create and update operations. Perhaps this can be done using an OsidRecord.

...

organizationIssueRecord

...

) form).getIssueId());
    }
}

The Gap

The next step is to note the gap between the above Authorizations and the way the project wants to manage Authorizations. Somehow we need to get from Issues and Holds to Organizations. 

Bouncing Off The Organization Wall

Working from the Organization side, the service architect needs to map Organizations to Issues. While there is an owning Organization in the Issue, it doesn't capture the nuance that separate Organizations have access to place and remove Holds. She is already aware that this translates into create and update operations. Perhaps this can be done using the OsidRecord defined by the team.

With this data the service architect believes she can create the proper Authorizations. But how? 

...