r4 - 11 Feb 2004 - 12:17:23 - DaveMorrisYou are here: TWiki >  Astrogrid Web  >  DaveMorris > Iter05SecurityContext
In order to talk about the proposed new SecurityDelegate? API, it might make things useful to think about an example scenario based around Portal, JobEntrySystem?, DataCenter? and MySpace.

Some or all of the following may be wrong, it is just an initial guess.

Please feel free to add comments, suggestions or objections.

Login to Portal

The user logs-in to the portal by choosing a community and supplying a name and password. The portal calls the relevant Community, supplying name and password, and gets back the Account identifier, plus a token.

   <message>
      <community>ivo://ast.cam.ac.uk</community>
      <name>dave</name>
      <pass>####</pass>
   </message>
   <response>
      <account>ivo://ast.cam.ac.uk/dave</account>
      <token>2347834589tafc\sd9q359\sv</token>
   </response>

Portal session

The data returned from the Community login should be stored in the current CocoonSession?. However, the CommunityPortal? toolkit should include a SecurityContext? helper class to retrieve the data.

This means that the other components do not have to fiddle around looking for individual params in the request, session or config. The other components do not need to know the details of where the data is held, just how to get at it easily.

public class ComponentAction extends AbstractAction
   {
   ....
   public Map act(....)
      {
      //
      // Create a SecurityContextHelper.
      SecurityContextHelper securityContext = new CocoonSecurityContextHelper(request, session, objectModel) ;
      //
      // Get the current Account from the SecurityContextHelper.
      AccountDetails account = securityContext.getAccount() ;
      ....
      }
   }

DataCenter? query

The user creates a job, involving a query to a DataCenter?. The query is sent from the Portal to the JobEntrySystem?, passing the security details in the header.

At the moment, the user will need to select the Group membership that allows them to perform the action. Later implementations should not require this.

   <header>
      <wsse:UsernameToken>
         <wsse:Username>ivo://ast.cam.ac.uk/dave|ivo://star.le.ac.uk/xray-studies</wsse:Username>
         <astrogrid:IdentityToken>2347834589tafc\sd9q359\sv</astrogrid:IdentityToken>
      </wsee:UsernameToken>
   </header>
   <message>
      <job>
         <query>details of the ADQL query</query>
         ....
      </job>
   </message>

JobEntry? processing

When the message arrives at the JobEntrySystem?, the SecurityHandler? processes the message headers before it is passed on to the JobEntrySystem? service.

  • If sender supplies valid credentials, then the message is passed on to the service as normal.
  • If sender supplies invalid credentials, then the message is bounced by the Axis handler and does not reach the service.
  • If sender does not supply any credentials in the header, then the message is passed to the service, but the authentication checks return false or null.
  • This allows our services to handle anonymous calls from external components.

When the message arrives at the JobEntrySystem? service, the service method can request the SecurityContext? from the SecurityDelegate?, which allows them to check that the sender of the message has been authenticated.

   public Object jobEntryMethod(Object params ...)
      {
      //
      // Get a SecurityDelegate.
      SecurityDelegate securityDelegate = SecurityDelegateFactory.getSecurityDelegate() ;
      //
      // Get the message SecurityContext from the delegate
      SecurityContext securityContext = securityDelegate.getSecurityContext() ;
      //
      // Check that the message sender has been authenticated.
      if (securityContext.isAuthenticated())
         {
         //
         // Perform the service for an authenticated Account.
         //
         }
      //
      // If the message sender has not been authenticated.
      else {
         //
         // Perform the service for an anonymous user ?
         //
         }
      }

To check that the message sender is allowed to schedule jobs, the JobEntrySystem? can query the current SecurityContext?.

   public Object jobEntryMethod(Object params ...)
      {
      ....
         {
         //
         // Check that the message sender is allowed to schedule jobs.
         Permission permisson = securityContext.checkPermissions("job-scheduler", "insert") ;
         if (permission.isValid())
            {
            //
            // Schedule the job ....
            //
            }
         }
      ....
      }

Saving SecurityContext?

As part of the process of scheduling the job, the JobEntrySystem? may want to save the current SecurityContext?, without having to know what it contains. This would suggest that the SecurityContext? should be serializable in some form (Castor XML, String or just BLOB ?).

   public Object jobEntryMethod(Object params ...)
      {
      ....
         {
         ....
            {
            //
            // Save the current SecurityContext.
            database.store(securityContext) ;
            }
         }
      ....
      }

When the job is ready to execute, the JobScheduler? will want to restore the original SecurityContext?.

   public Object jobSchedulerMethod(Object params ...)
      {
      //
      // Restore the original SecurityContext.
      SecurityContext originalContext = database.load(identifier) ;
      //
      // Set the current SecurityContext for this Thread.
      securityDelegate.setSecurityContext(originalContext) ;
      ....
      }

The JobScheduler? will then want to send messages to other services, e.g. DataCenter?, using this restored context.

   public Object jobSchedulerMethod(Object params ...)
      {
      //
      // Restore the original SecurityContext.
      SecurityContext originalContext = database.load(identifier) ;
      //
      // Get a SecurityDelegate.
      SecurityDelegate securityDelegate = SecurityDelegateFactory.getSecurityDelegate() ;
      //
      // Set the current SecurityContext for this Thread.
      securityDelegate.setSecurityContext(originalContext) ;
      ....
      //
      // Call DataCenter to perform the query.
      DataCenterDelegate dataCenterDelegate = DataCenterDelegateFactory.getDataCenterDelegate() ;
      //
      // Setup the query details ....
      //
      dataCenterDelegate.dataCenterMethod(query) ;
      }

Setting the current SecurityContext? on the SecurityDelegate? means that any subsequent outbound calls to other services automatically have the SecurityContext? details added to their message headers.

   <header>
      <wsse:UsernameToken>
         <wsse:Username>ivo://ast.cam.ac.uk/dave|ivo://star.le.ac.uk/xray-studies</wsse:Username>
         <astrogrid:IdentityToken>2347834589tafc\sd9q359\sv</astrogrid:IdentityToken>
      </wsee:UsernameToken>
   </header>
   <message>
      <query>details of the ADQL query</query>
   </message>

DataCenter? processing

When the message arrives at the DataCenter?, the SecurityHandler? processes the message headers before it is passed on to the DataCenter? service. The DataCenter? service can then get the current SecurityContext? from the SecurityDelegate?.

   public Object dataCenterMethod(Object params ...)
      {
      //
      // Get a SecurityDelegate.
      SecurityDelegate securityDelegate = SecurityDelegateFactory.getSecurityDelegate() ;
      //
      // Get the message SecurityContext from the delegate
      SecurityContext securityContext = securityDelegate.getSecurityContext() ;
      //
      // Check that the message sender has been authenticated.
      if (securityContext.isAuthenticated())
         {
         //
         // Perform the service for an authenticated Account.
         //
         }
      //
      // If the message sender has not been authenticated.
      else {
         //
         // Perform the service for an anonymous Account ?
         //
         }
      }

To check that the message sender is allowed to access a database table, the DataCenter? can query the current SecurityContext?.

   public Object dataCenterMethod(Object params ...)
      {
      ....
         {
         //
         // Check that the message sender is allowed to access the database table.
         Permission permisson = securityContext.checkPermissions("table-name", "select") ;
         if (permission.isValid())
            {
            //
            // Perform the database query ....
            //
            }
         }
      ....
      }

Access to MySpace home

The DataCenter? may need access to the MySpaceManager for the Account in order to find out where to put the results of the query. The MySpaceManager address and 'home' identifier should be stored as part of the Account details in the Community. The DataCenter? should be able to request the Account details for the message sender from the current SecurityContext?.

   public Object dataCenterMethod(Object params ...)
      {
      ....
         {
            {
            //
            // Perform the database query ....
            ....
            //
            // Get the Account details.
            AccountDetails account = securityContext.getAccountDetails() ;
            //
            // Get the MySpace 'home' reference.
            MySpaceReference myspaceHome = account.getMySpaceHome() ;
            //
            // Create a MySpace delegate.
            MySpaceManagerDelegate mySpaceDelegate =
               MySpaceManagerDelegateFactory.getMySpaceManagerDelegate(myspaceHome) ;
            //
            // Negotiate space for the results.
            MySpaceReference destination = mySpaceDelegate.method(params) ;
            //
            // Send the results to the Account MySpace.
            OutputStream stream = destination.getOutputStream() ;
            .....
            }
         }
      ....
      }

Again, the current SecurityContext? headers are added to all of the outbound service calls from the DataCenter? to the MySpaceManager.

   <header>
      <wsse:UsernameToken>
         <wsse:Username>ivo://ast.cam.ac.uk/dave|ivo://star.le.ac.uk/xray-studies</wsse:Username>
         <astrogrid:IdentityToken>2347834589tafc\sd9q359\sv</astrogrid:IdentityToken>
      </wsee:UsernameToken>
   </header>
   <message>
      <request>details of the MySpace message</request>
   </message>

The MySpaceManager can then request the current SecurityContext? and perform its own policy checking before allocating space for the data.

   public Object mySpaceManagerMethod(Object params ...)
      {
      //
      // Get a SecurityDelegate.
      SecurityDelegate securityDelegate = SecurityDelegateFactory.getSecurityDelegate() ;
      //
      // Get the message SecurityContext from the delegate
      SecurityContext securityContext = securityDelegate.getSecurityContext() ;
      //
      // Check that the message sender has been authenticated.
      if (securityContext.isAuthenticated())
         {
         //
         // Perform the service for an authenticated Account.
         //
         }
      }


SecurityToken?

In the above examples, the messages are shown with the same SecurityToken? in all of the messages.

In the real implementation, each time the SecurityDetails? are passed through the SecurityHandler?, SecurityDelegate? or SecurityContext?, the existing SecurityToken? would be validated and then replaced with a new SecurityToken?.


Additional cases

Change SecurityContext?

In some cases, some of the services may want to send a message to another service using a different SecurityContext?, e.g. Call a service using an anonymous or administrator Account. In order to support this, a service should be able to create a new SecurityContext?, and apply that to some messages, and then swap back to the original SecurityContext? when done.

   public Object someServiceMethod(Object params ...)
      {
      //
      // Get a SecurityDelegate.
      SecurityDelegate securityDelegate = SecurityDelegateFactory.getSecurityDelegate() ;
      //
      // Get the message SecurityContext from the delegate
      SecurityContext originalContext = securityDelegate.getSecurityContext() ;
      ....
      //
      // Outbound messages use the original SecurityContext, from the inbound message.
      //
      ....
      //
      // Create a new SecurityContext.
      SecurityContext specialContext = securityDelegate.newSecurityContext(
         account,
         group,
         token
         ) ;
      //
      // Make this the current SecurityContext for outbound messages.
      securityDelegate.setSecurityContext(specialContext) ;
      ....
      //
      // Outbound messages use the new SecurityContext, created internally.
      //
      ....
      //
      // Change back to the original SecurityContext, from the inbound message
      securityDelegate.setSecurityContext(originalContext) ;
      ....
      //
      // Outbound messages use the original SecurityContext, from the inbound message.
      //
      ....
      }

Null SecurityContext?

In some cases, some of the services may want to send a message to another, non-astrogrid service without the SecurityContext? hedares. In order to support this, a service should be able to set the current SecurityContext? to null, send the messages, and then swap back to the original SecurityContext? when done.

   public Object someServiceMethod(Object params ...)
      {
      //
      // Get a SecurityDelegate.
      SecurityDelegate securityDelegate = SecurityDelegateFactory.getSecurityDelegate() ;
      //
      // Get the message SecurityContext from the delegate

      SecurityContext originalContext = securityDelegate.getSecurityContext() ;
      ....
      //
      // Outbound messages use the original SecurityContext, from the inbound message.
      //
      ....
      //
      // Set the SecurityContext to null
      securityDelegate.setSecurityContext(null) ;
      ....
      //
      // Outbound messages do not have the SecurityContext hedares.
      //
      ....
      //
      // Change back to the original SecurityContext, from the inbound message
      securityDelegate.setSecurityContext(originalContext) ;
      ....
      //
      // Outbound messages use the original SecurityContext, from the inbound message.
      //
      ....
      }

SecurityContext? on replies

I'm not sure about this one, but if DataCenter? calls MySpaceManager, should the same SecurityContext? headers also be added to the responses returned from the MySpaceManager back to the DataCenter?.

   <header>
      <wsse:UsernameToken>
         <wsse:Username>ivo://ast.cam.ac.uk/dave|ivo://star.le.ac.uk/xray-studies</wsse:Username>
         <astrogrid:IdentityToken>2347834589tafc\sd9q359\sv</astrogrid:IdentityToken>
      </wsee:UsernameToken>
   </header>
   <message>
      <response>details of the MySpace response</response>
   </message>

Probably not applicable for the current implementation.


The Security Guard pattern

In the prototype code I am developing for this area of AstroGrid, (most of) the principles above apply but the analysis to classes differs. The roles that Dave assigns above to SecurityDelegate? and SecurityContext? are assigned in my code to SecurityGuard?, ClientSecurityGuard? and ServiceSecurityGuard?.

SecurityGuard? is a simple Java bean. It has properties representing various kinds of credentials such as username, password, one-shot password, etc. A SecurityGuard? is a way of passing collections of credentials between classes inside one JVM.

ClientSecurityGuard? is a subclass of SecurityGuard?. It is intended for calling in the client's JVM. It adds to the basic bean the ability to mountGuard() on a web-service endpoint: i.e. to set up JAX-RPC handlers for the javax.xml.rpc.Service object representing the endpoint such that each outgoing message from the client is then decorated with security headers according to the current credentials in the ClientSecurityGuard?. A client can use the same ClientSecurityGuard? for any number of service endpoints (i.e. all the services get sent the same credentials) or it can have different guards for some endpoints (e.g. to use different identities with different services).

ServiceSecurityGuard? is also a subclass of SecurityGuard?. It is called in the service implementation. I need to work out the details, but the general idea is that the service method implementing a SOAP operation calls a factory method and gets given a ServiceSecurityGuard? the properties of which are filled in from the credentials in the SOAP header and/or from any calls the message-handler chain has made to external community/policy services.

Note that the semantics are different in the two cases. The client creates its guard with a constructor, sets its properties, and then has the guard configure the message handlers at run time. The service starts with its message handlers set up, and has a guard created for it when a request message comes in. The service retrieves the ServiceSecurityGuard? and reads its properties to find out the caller's credentials.

However, in both cases the application code is calling a security guard sub-class and the state of the object is common to both subclasses. Therefore, a service calls a copyCredentials() method (declared on the parent SecurityGuard?) to copy all the credentials from a ServiceSecurityGuard? to a ClientSecurityGuard?. If client A calls service B and service B then calls service C:

  1. A sets up credentials using a ClientSecurityGuard?.
  2. B extracts A's credentials using a ServiceSecurityGuard?.
  3. B creates a ClientSecurityGuard? for the call to C.
  4. B initializes the credentials in its ClientSecurityGuard? by copying from its ServiceSecurityGuard?.
  5. C extracts B's credentiuals using another ServiceSecurityGuard?.

There are some other classes in org.astrogrid.security but the application codes doesn't need to call them. The SecurityGuard? classes are the facade for the security operation.

Note that ClientSecurityGuard? and ServiceSecurityGuard? are not themselves delegate classes. They are are not bound one-to-one to any remote service-end-point. Where a SecurityGuard? subclass needs to talk to a remote service (community server, typically) it calls a separate delegate class.

An application can call the community delegate directly, but it doesn't need to; the security facade hides that call and abstracts the exact pattern or remote calls required. Therefore, the application is protected against changes in the topology of the system.

Finally, I've avoided calling anything a SecurityContext? as that class-name is used elsewhere (JAAS, Globus) with extremely-specific meanings that differ from our likely usage.

-- GuyRixon - 10 Feb 2004

Edit | Attach | Printable | Raw View | Backlinks: Web, All Webs | History: r4 < r3 < r2 < r1 | More topic actions
 
AstroGrid Service Click here for the
AstroGrid Service Web
This is the AstroGrid
Development Wiki
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback