Versions Compared

Key

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

...

Authorization designs are some of the most difficult puzzles to solve in an enterprise system. Sprinkling authorization data into various services to surface what appears to be simple data can be inflexible and obtuse. A service architect starts with leading requirements and runs into difficulty sorting out plowing through a complex problem.

Table of Contents

...

The Student System Project is using the Hold OSID as a means of restricting registration access to students. The registration process uses the Rules.Check OSID as a means of managing what hold Blocks will be checked. 

The product owner understands that one organization may place a Hold on a student while another organization is responsible for expiring it. The However, the Issue has defines a single responsible Resource? . The product owner asks that a list of Organizations, not Resources, who can place the Hold and a list of Organizations who can remove the Hold be added to the Issue.

...

The service architect is brought in to make it official. She asks why these services have been joined together and she is told that the service design does not meet their needs. She is informed that if this new method was put into the interface, then it would be compliant. However, there isn't much she can do to affect the OSIDs within the time frame of this project's milestone. Not yet knowing the details of the problem she talks in generalities -- "this is usually sign of a factoring issue." This has no impact because as fas as the project is concerned, she was already given the solution to the problem and simply has to executeshould carry it out.

On other projects, the service architect can generally align with one of the project roles to help get the others on board. She can speak to the product owner's vision or simplify the work of an OSID implementation developer. When it comes to performance issues, she is alone to defend a methodology that appears to fly in the face of efficiency.

...

The service architect returns to her think tank to ponder the problem. She traces the problem starting from the requirement of discerning among the organizations who can do what with Holds of various Issues. Issues do constrain Holds and that seems like the place to do it. She eventually arrives at the same place the developers on the project did.

...

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

...

This doesn't seem right. This is worse than the code from the first iteration Example 1 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. 

...

The service architect decides to go back to the beginning and look looks at the situation from an interoperability and stability perspective.

...

The service architect starts over from the basics and changes one thing. The responsibility for syncing authorizations now rests with the OSID Consumer. For every Issue and Hold created, the application creates Authorizations. basic principles. A service is not necessarily an entire OSID. OSIDs are comprised of many services. The OSID packaging is simply a cluster of services around some problem domain. The service of checking Authorizations (AuthorizationSession) and the service of managing Authorizations (AuthorizationAdminSession) can be used in different ways. The checking of Authorizations within the Hold OSID Provider (Example 2) still looks sweet. Perhaps the managing Authorization piece can be moved around.

The responsibility for managing authorizations is moved to 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
  • use the correct Qualifier (Hold or Issue) for each Function

In this scenario, checking Authorizations is fully encapsulated under the Hold OSID. However, managing Authorizations is orchestrated alongside the Hold OSID. 

Solution 2: Grouping Agents

...

  1. Know the difference between a functional requirement and a solution. It's easy to be led by the nose into a clumsy solution. Many people think in terms of capturing and moving data. They also believe that any application responsibility means the end-user has to do more work. Service design is a different paradigm. It is about assigning responsibilities among blocks of code.Some functional requirements
  2. Gnarly code (Example 1) and poor performance are signs of a factoring problem. Performance can be increased incrementally by breaking things up and leveraging bulk operations. However, more study is warranted when performance is very off base and developers look to merge services together.
  3. Some functional requirements are short sighted. Tightly coupling organizational affiliations with authorizations is not necessary in small environments and never holds up well in an enterprise (changing hold authorization requirements will never cause an institution to do a re-org).
  4. Squirreling through a problem creates new problems to be solved, and solving those creates more problems, and so on. Sometimes it helps to go back to the very beginning and find that one initial assumption on which everything else was based. It's so much easier to do this on a whiteboard rather than wait until the code was written. 
  5. People have trouble with the asymmetry of services. How a consumer uses stuff coming out of a service and how that stuff gets in there are two different problems. In simpler designs, the data object goes in, the data object comes out. In a service paradigm, approach these two aspects independently. 
  6. Generally speaking, encapsulation is good. But here, it made more sense to surface the management of authorizations but in such a way as to carefully manage assumptions between the application and the Authorization OSID.

 

 

...

  1. This case study avoided the use of the word "Role." Roles are often used in other frameworks to manage sets of people on the periphery of an authorization service. A role should describes the action one can perform on something. In the OSID world, it is the Agent, Function, and Qualifier. This is the Authorization itself. A Role is not an attribute of a Person nor is it a group. An Agent doesn't have a role until it is joined with a Function and Qualifier. The slipperiness of Resources allows for creating Authorizations using groups or other rules to cut down on the explicit Authorizations that need to be managed. These composite Resources can be aligned with Functions and Qualifiers ("the group of people who can create Holds in the Bursar's Office") but the role isn't realized until the explicit Authorization is created. This semantic detail helps clarify how the enforcement point works and pays off when applying this principle to Workflow. OSIDs avoid the word altogether. 
  2. A litmus test for a good authorization design is to have the ability to ask the question "who has access to perform this Function on Qualifier?" In order to answer this question, there needs to be the ability to expand explicitly managed authorizations into a list of implicit authorizations based on expansion of the Resources or Qualifier hierarchy. This helps keep authorization logic clear and out in the open.