Versions Compared

Key

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

...

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
title1. Application 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
    OrganizationIssueRecord record = (OrganizationIssueRecord) issue.getIssueRecord(organizationIssueRecordType);
 
    // 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.getHoldCreatorOrganizationIds()) {
        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.AppointmentList appointments = appointmentLookupSession.getAppointmentsForPersonAndPosition(resourceId, position.getNextId()) {
                        if (appointments.hasNext()) {
                            return (true);
                        }
                    }
                }
            }
        }
    }
 
    return (false);
}            

...

The service architect creates a Hold OSID Adapter wrapping the following methods.

Code Block
title2. Authorization 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());
    }
}

...

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

Code Block
title3. Syncing With The Authorization OSID
public org.osid.hold.Issue createIssue(org.osid.hold.IssueForm form) 
    throws org.osid.NotFoundException,
           org.osid.OperationFailedException,
           org.osid.PermissionDeniedException {    
 
    org.osid.hold.Issue peristIssue(form);
    OrganizationHoldFormRecord record = (OrganizationHoldFormRecord) form.getIssueFormRecord(organizationHoldRecordType);
  
    org.osid.personnel.AppointmentLookupSession appointmentLookupSession = personnelMgr.getAppointmentLookupSession();
    appointmentLookupSession.useEffectiveAppointmentView();
    org.osid.personnel.PositionLookupSession positionLookupSession = personnelMgr.getPositionLookupSession();
    positionLookupSession.useEffectivePositionView();
 
    try (org.osid.id.IdList orgIds = record.getHoldCreatorOrganizationIds()) {
        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();
                    if (useThisPosition(position)) {
                        try (org.osid.personnel.AppintmentList appointments = appointmentLookupSession.getAppointmentsForPosition(position.getNextId()) {
                            try (org.osid.id.IdList agentIds = resourceAgentSession.getAgentIdsByResource(position.getResourceId()) {
                                while (agentIds.hasNext()) {
                                    org.osid.authorization.AuthorizationForm authorizationForm = authorizationSession.getAuthorizationFormForCreateForAgent(agentIds.getNextId(), CREATEHOLD_FUNCTION_ID, issue.getId(), new org.osid.type.Type[0]);
                                    authorizationSession.createAuthorization(authorizationForm);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

...

The service architect has seen Qualifier hierarchies based on organizations. She attempts to map these two kinds of Qualifiers and jots down some pseudo-code in an Authorization OSID Adapter:

Code Block
title4. Authorizations In The Authorization OSID
boolean isAuthorized(org.osid.id.Id agentId, org.osid.id.Id functionId, org.osid.id.Id qualifierId)
    throws org.osid.NotFoundException,
           org.osid.OperationFailedException,
           org.osid.PermissionDeniedException { 
    org.osid.id.Id issueId;
    if (functionId.equals(CREATEHOLD_FUNCTION_ID)) {
        issueId = qualifierid;
    } else if (functionId.equals(UPDATEHOLD_FUNCTION_ID)) {
        org.osid.hold.Hold hold = holdLookupSession.getHold(qualifierId);
        issueId = hold.getIssueId();
    } else {
        return (underlyingAuthorizationProvider.isAuthorized(agentId, functionId, qualfiierId));
    }
 
    org.osid.id.Id resourceId = resourceAgentSession.getResourceIdByAgent(agentId);
    org.osid.hold.Issue issue = issueLookupSession.getIssue(issueId);
    OrganizationIssueRecord record = (OrganizationIssueRecord) issue.getIssueRecord(ORGANIZATION_ISSUE_RECORD_TYPE);
    org.osid.id.IdList organizationIds;
 
    if (functionId.equals(CREATEHOLD_FUNCTION_ID)) {
        organizationIds = record.getHoldCreatorOrganizationIds();
    } else if (functionId.equals(UPDATEHOLD_FUNCTION_ID)) {
        organizationIds = record.getHoldUpdaterOrganizationIds();
    }
 
    try {
        while (organizationIds.hasNext()) {
            org.osid.id.Id orgId = organizationIds.getNextId();
            try (org.osid.personnel.PositionList positions = positionLookupSession.getPositionsForOrganization(orgId)) {
                while (positions.hasNext()) {
                    org.osid.personnel.Position position = positions.getNextPosition();
                    try (org.osid.personnel.AppointmentList appointments = appointmentLookupSession.getAppointmentsForPersonAndPosition(resourceId, position.getNextId()) {
                         if (appointments.hasNext()) {
                            return (true);
                        }
                    }
                }
            }
        }
 
        return (false);
    } finally {
        organizationIds.close();
    }
}

...

After a few beers, our service architect equates a Resource with an Organization. While it is the Agent that is creating or updating a Hold, the authorization has been granted to a Resource. 

Code Block
title5. Granting Authorizations To Resources
public org.osid.hold.Issue createIssue(org.osid.hold.IssueForm form) 
    throws org.osid.NotFoundException,
           org.osid.OperationFailedException,
           org.osid.PermissionDeniedException {    
 
    org.osid.hold.Issue peristIssue(form);
    OrganizationHoldFormRecord record = (OrganizationHoldFormRecord) form.getIssueFormRecord(organizationHoldRecordType);
 
    try (org.osid.id.IdList orgIds = record.getHoldCreatorOrganizationIds()) {
        while (orgIds.hasNext()) {
            org.osid.authorization.AuthorizationForm authorizationForm = authorizationSession.getAuthorizationFormForCreateForResource(orgIds.getNextId(), CREATEHOLD_FUNCTION_ID, issue.getId());
            authorizationSession.createAuthorization(authorizationForm);
        }
    }
}

...

Designing around with services is always a choosing with side of the line has ownership of a problem. If the problem of mapping Agents to Resources doesn't belong to the Authorization OSID, then maybe it's a problem for the Resource OSID (as what is implied by its definition). The next iteration on the authorization implementation is:

Code Block
title6. Authorization Iteration 2
boolean isAuthorized(org.osid.id.Id agentId, org.osid.id.Id functionId, org.osid.id.Id qualifierId)
    throws org.osid.NotFoundException,
           org.osid.OperationFailedException,
           org.osid.PermissionDeniedException { 

    org.osid.id.Id issueId;
    if (functionId.equals(CREATEHOLD_FUNCTION_ID)) {
        issueId = qualifierid;
    } else if (functionId.equals(UPDATEHOLD_FUNCTION_ID)) {
        org.osid.hold.Hold hold = holdLookupSession.getHold(qualifierId);
        issueId = hold.getIssueId();
    } else {
        return (underlyingAuthorizationProvider.isAuthorized(agentId, functionId, qualfiierId));
    }
 
    org.osid.hold.Issue issue = issueLookupSession.getIssue(issueId);
    OrganizationIssueRecord record = (OrganizationIssueRecord) issue.getIssueRecord(ORGANIZATION_ISSUE_RECORD_TYPE);
 
    if (functionId.equals(CREATEHOLD_FUNCTION_ID)) {
        if (resourceAgentSession.isAgentForResources(agentId, record.getHoldCreatorOrganizationIds()) {
            return (true);
        }
    } else if (functionId.equals(UPDATEHOLD_FUNCTION_ID)) {
        if (resourceAgentSession.isAgentForResources(agentId, record.getHoldUpdaterOrganizationIds()) {
            return (true);
        }  
    }

    return (false);
}

...

Gliffy
size500
nameStack Service Model

Code Block
title7. Implicit Resource Groups
// ResourceGroupSession - gets the list of "people" in a "organization of people who can create holds"
public getResourcesByGroup(org.osid.id.Id groupResourceId) {
    // assumption: groupResourceId is an org Id
 
    java.util.Collection<org.osid.resource.Resource> ret = new java.util.ArrayList<>();
    try (org.osid.personnel.PositionList positions = positionLookupSession.getPositionsForOrganization(groupResourceId)) {
        while (positions.hasNext()) {
            org.osid.personnel.Position position = positions.getNextPosition();
            // can add a filter on a type of position
            try (org.osid.personnel.AppointmentList appointments = appointmentLookupSession.getAppointmentsForPosition(position.getNextId()) {
                 if (appointments.hasNext()) {
                    ret.add(convertPerson2Resource(position.getPerson()));
                }
            }
        }
    }
 
    return (new net.okapia.osid.jamocha.resource.resource.ArrayResourceList(ret));
}

...

Solution 1: Ignoring the Organizations

Solution 2: Mixing in the Organizations

...

Gliffy
size500
nameApplication Manages Authorization

The service architect starts over from the basics. In the above scenario, the responsibility for syncing authorizations rests with the OSID Consumer. For every Issue and Hold created, the application creates Authorizations. In order to accomplish this, the application must:

  • provide a list of people who are authorized for each function
  • understand the Function to be used for each authorization
  • understand which Qualifier (Hold or Issue) is to be used for each Function

Solution 2: Grouping Agents

Gliffy
size500
nameManaging Authorizations and Resources

Layering in the Resource OSID provides a means of grouping Resources (and thus Agents). In the above scenario, the Application is also taught how to create and manage Resource Groups and the Authorization OSID Provider is taught how to evaluate Resources as was done in an earlier example.

Solution 3: Injecting Organizations

Knowing that the product owner will balk on this manual process, the service architect layers in the Organization code to create implicit groups into the Resource OSID (Example 7). 

Gliffy
size500
nameManaging Authorizations With Resources and Organizations

Now, the application can select Resource Groups that look like Organizations and use those for creating Authorizations. When the product owner gets around to iterating amend its exception cases, the Application need not do anything else other than allow administrative users to supplement the Organizations with a list of people.

Sooner or later, they will want to not include certain people coming through the Organization evaluation. It is around this point where the letting go process of Organizational-based authorization begins. It's a process.

The service architect feels good. This iterative path is going somewhere. 

Retrospective