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
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
    OrganizationHoldRecord record = (OrganizationHoldRecord) issue.getIssueRecord(organizationHoldRecordType);
 
    // 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.getOrgIdsWhoCanPlaceHold()) {
        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);
}            

...

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

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());
    }
}

...

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

Code Block
titleSyncing With The Authorization OSID
public org.osid.hold.Issue createIssue(org.osid.hold.IssueForm form) {
    foreachthrows (org.osid.NotFoundException,
in getHoldCreatorOrganizationIds) {         foreach (person in org) { // insert complexity from first iteration here
 org.osid.OperationFailedException,
           org.osid.PermissionDeniedException {    
 
     createAuthorization(person, function, newIssue);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.
}

This doesn't seem right. This is worse than the code from the first iteration. Yes, it's now in the service provider but it fixes the membership such that if the organization changes, the authorization service is out of date. 

It appears that the Organization OSID is important but it has no idea about hold authorizations. Resolving the organization above the Hold OSID looks terrible and resolving it within the Hold OSID Provider isn't an improvement. There's only one service left.

It's A Song About Alice

The goal is to manage Authorizations in such a way that the Authorization checks in the adapter work as expected. This stake in the ground for the most common pathway, the checking of authorizations, is the simplest. 

The service architect needs to look deeper into the Authorization OSID. 

...

Again, the mappings for the Authorization check are:

  • Agent: the authenticated entity
  • Function: "can create Hold" "can update Hold" "can delete Hold"
  • Qualifier: Issue for create, Hold for update and delete

The Qualifier as an Organization

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
boolean isAuthorized(org.osid.id.Id agentId, org.osid.id.Id functionId, org.osid.id.Id qualifierId) {
    org.osid.id.Id issueId;
    if (functionId.equals(CREATEHOLD_FUNCTION_IDgetPositionLookupSession();
    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()) {
        issueId = qualifierid;     } else if (functionId.equals(UPDATEHOLD_FUNCTION_ID)) {
        org org.osid.holdpersonnel.HoldPosition holdposition = holdLookupSessionpositions.getHoldgetNextPosition(qualifierId);
        issueId      = hold.getIssueId();     } elseif (useThisPosition(position)) {
        return (underlyingAuthorizationProvider.isAuthorized(agentId, functionId, qualfiierId));           }      try (org.osid.idpersonnel.IdAppintmentList resourceIdappointments = resourceAgentSessionappointmentLookupSession.getResourceIdByAgent(agentId);getAppointmentsForPosition(position.getNextId()) {
            org.osid.hold.Issue issue = issueLookupSession.getIssue(issueId);     OrganizationIssueRecord record = (OrganizationIssueRecord) issue.getIssueRecord(ORGANIZATION_ISSUE_RECORD_TYPE);    try (org.osid.id.IdList agentIds organizationIds;
 
= resourceAgentSession.getAgentIdsByResource(position.getResourceId()) {
   if (functionId.equals(CREATEHOLD_FUNCTION_ID)) {         organizationIds = record.getHoldCreatorOrganizationIds();     } else if (functionId.equals(UPDATEHOLD_FUNCTION_ID)) {         organizationIds = record.getHoldUpdaterOrganizationIds();while (agentIds.hasNext()) {
     }       try {         while (organizationIds.hasNext()) {             org.osid.idauthorization.IdAuthorizationForm orgIdauthorizationForm = orgIdsauthorizationSession.getAuthorizationFormForCreateForAgent(agentIds.getNextId(), CREATEHOLD_FUNCTION_ID, issue.getId(), new org.osid.type.Type[0]);
            try (org.osid.personnel.PositionList positions = positionLookupSession.getPositionsForOrganization(orgId)) {                       while (positionsauthorizationSession.hasNextcreateAuthorization(authorizationForm));
 {                     org.osid.personnel.Position position = positions.getNextPosition();        }
                            }
    try (org.osid.personnel.AppintmentList appointments = appointmentLookupSession.getAppointmentsForPersonAndPosition(resourceId, position.getNextId()) {              }
           if (appointments.hasNext()) {       }
                }
    return (true);       }
        }
    }
}

This doesn't seem right. This is worse than the code from the first iteration and something like this needs to occur again for the creation each Hold. Yes, it's now in the Hold OSID Provider but it fixes the membership such that if the organization changes, the authorization service is out of date. 

It appears that the Organization OSID is important but it has no idea about hold authorizations. Resolving the organization above the Hold OSID looks terrible and resolving it within the Hold OSID Provider isn't an improvement. There's only one service left.

It's A Song About Alice

The goal is to manage Authorizations in such a way that the Authorization checks in the adapter work as expected. This stake in the ground for the most common pathway, the checking of authorizations, is the simplest. 

The service architect needs to look deeper into the Authorization OSID. 

Gliffy
size500
nameAuthorization Model

Again, the mappings for the Authorization check are:

  • Agent: the authenticated entity
  • Function: "can create Hold" "can update Hold" "can delete Hold"
  • Qualifier: Issue for create, Hold for update and delete

The Qualifier as an Organization

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
titleAuthorizations 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 = 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);
    } finally {
        organizationIds.close();
    }
}

What's been accomplished to is to bury both the organizational affiliations and the mappings from Issues & Holds to the Organizations. It's certainly encapsulated but that's all this solution has going for it. It's hairy, has lots of service dependencies, and is probably slow. Also, this solution deals with only one aspect of authorization. In order to be able to audit who has access to what this logic would have to be replicated in such a way to support the return of implicit authorizations. 

The Authorization Two-Step

Gliffy
size500
nameAuthorization Model with Resource

It is the Agent that performs and action therefore Authorizations are checked using Agents. However, they can be created using Resources as well. What's a Resource? Anything you need it to be.

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
titleGranting Authorizations To Resources
public org.osid.hold.Issue  }createIssue(org.osid.hold.IssueForm form) 
                   }
         throws org.osid.NotFoundException,
      }     org.osid.OperationFailedException,
       }    org.osid.PermissionDeniedException {    }
 
        return (falseorg.osid.hold.Issue peristIssue(form);
    OrganizationHoldFormRecord }record finally= {
        organizationIds.close((OrganizationHoldFormRecord) form.getIssueFormRecord(organizationHoldRecordType);
 
  }
}

What's been accomplished to is to bury both the organizational affiliations and the mappings from Issues & Holds to the Organizations. It's certainly encapsulated but that's all this solution has going for it. It's hairy, has lots of service dependencies, and is probably slow. Also, this solution deals with only one aspect of authorization. In order to be able to audit who has access to what this logic would have to be replicated in such a way to support the return of implicit authorizations. 

The Authorization Two-Step

...

It is the Agent that performs and action therefore Authorizations are checked using Agents. However, they can be created using Resources as well. What's a Resource? Anything you need it to be.

...

  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);
        }
    }
}

Better