You are here: Home V2 Software Software More ... Developer Notes Memops Code Generation Access Control Proposal

Access Control Proposal

Proposal for access control that is simple, supports fine-grained control, and supports role-based access algorithms. Rasmus Fogh 2006. Base for 2012 opTypes and AccessControl package.

 

Introduction
Proposal
Function
Implementation
Questions

Introduction

Current model

The current access control proposal is based on the PIMS interface specified by Chris Morris in the autumn of 2004. As given in the current data model, it been modified to allow for a more fine-grained control. The model is given below:

2006Apr10 - Access Control package

 

Analysis

The model has many advantages (once you understand it ;->). It is simple, yet powerful enough to handle any possible assignment of permissions to objects. All access control is in a separate package, where different access control implementation can produce their own code without breaking anything else.

To see how it works, imagine that every user has his own, one-person UserGroup, and that every MemopsBaseClass object has its own separate AccessObject. You can now use the Permission objects to set individual access permissions for every single object. Once that is done, you can take the AccessObjects that have identical links to Permission and merge them into a single object without changing the contents. Similarly you can take users that have identical permissions for all objects and merge their groups into one UserGroup.

The problem is that there is no clear connection between the model and the kind of things users would like to do, such as:

  • Transfer target XXX and all related objects from Oxford to Dundee
  • Change the Administrator of the G-proteins project from Bob Smith to John Brown
  • Allow all technicians to create Samples, but let only academics create RefSamples
  • Only the owner of an object and the system administrator may delete objects
  • Only the creator of a Sample may add new SampleComponents to it
  • Give Mary and Kate in Daresbury read access to all the Kinase targets.
  • etc.

While the model can accomodate any combination of permissions, there is no clear way of setting policy, of finding out where the current set came from, or of deciding what permissions need to be changed to achieve a general result. The very general nature of the AccessObject means that the necessary information is lacking. One could give the AccessObject a meaning in terms of lab projects, roles, etc., but by the same token you would lose the ability to specify any desired combination of accesses.

The model as shown here has been changed relative to the PIMS specification. The original PIMS specification was fairly coarse-grained. Access was defined as 'canRead', 'canWrite', 'canCreate' and 'canDelete'. There were separate query functions for the four access types, which made it impossible to accommodate more fine-grained access control without a model change.

The PIMS specification also had separate classes to control object creation. This was organised in a way that did not allow varying the permisisons by context - you either could or could not create SampleComponents, period.

Proposal

The new model

The proposal is shown below. Note that this is a proposal. The basic mechanisms are up for discussion (though I obviously think these are good). Details like names etc. are clearly open.

2006Apr10 Access Control Proposal

UserGroup:
The UserGroup has groupName as the key. The attributes groupingName, groupType, and groupRole are meant to hold information for the use of access control implications only. Say, for instance, that your access policy gave permissions to:

  1. Individual users
  2. Lab Projects (e.g. Kinases, Arabidopsis, ...). where each project had project members and
  3. Project leaders, with different rights.

You would then have groups like

  1. groupName:myUserId, groupingName:myUserId, groupType:user, groupRole:user
  2. groupName: Kinase_members, groupingName:Kinases, groupType:LabProject, groupRole:members
  3. groupName: Kinase_leaders, groupingName:Kinases, groupType:LabProject, groupRole:leaders

Access control applications now have enough information to enforce that every LabProject group had exactly one leader, that only LabProject Leader gorups were accepted for the 'projectLeader' role (see below)), etc. (they would still have to write the code to do it, though).

AccessRole:
The AccessRole now holds only a groupName and a role attribute. The key to the class is made up from role, groupName and accessObject.  Basically the groupname says who you are, the role says what you are allowed to do, and the accessObject say what you are allowed to do it to.  The groupName is a foreign key to the UserGroups table. The role describes the kind of permission being given, such as 'creator', 'owner', 'friend', 'groupMember', 'groupLeader', 'QAmanager', etc. This makes it easier to keep track of what a given set of permissions mean. AccessRole has derived links to UserGroup (based on groupName) and Permission (based on role), as shown by the two one-way-navigable links on the diagram

Permission:
The permissions appropriate for any given role are set in the Permissions table. Permissions are set according to role, className, opType, and targetName, either of which may be set to a  wildcard value ('any').

  • The role is the role as given i the AccessRole class. Indeed AccessRole.role defines a derived -to-many link into the Permissions class.
  • className refers to the class of the object you are operating on - note that the test is 'isinstance', the function you are trying to execute may have been defiend in a super- or subclass.
  • opType refers to the type of operation, which could be specific ('set', 'add', 'get', 'findAll, ...) or general ('query', 'modify', 'delete', 'create'). See the discussion of opTypes for details
  • targetName describes what the function is operating on. In most cases the combination of className, opType, and targetName uniquely identify a specific function. E.g. UserGroup.setGroupRole would be className:'memops.Access.UserGroup','set','groupRole'; UserGroup.delete will be 'memops.Access.UserGroup','delete','memops.Access.UserGroup'; and MemopsRoot.newUserGroup would be 'memops.Implementation.MemopsRoot','new','memops.Access.UserGroup'. See opTypes for details


AccessInfo:
The AccessInfo class is for storing permission settings These are under user control. The access control implementation can read the AccessInfo records and automatically use them when creating new objects.

MemopsRoot
The MemopsRoot.currentUser object shows who is currently logged in, and is used to calculate permissions. The implementation must protect this attribute from being changed by hand.

Function

Overview

Every API function will call AccessObject.isPermitted with parameters the object being called, the opType of the function, and the appropriate targetName. Only permitted operations would go ahead.Static functions would have to pass in the class instead of the object.

When creating new objects, the relevant call is isPermitted(parentObject, 'new', newClassName). The access permissions for new objects are set automatically by the AccessObject.newAccesses function, which is called as part of the object creation.  This allows you to specify creation permissions based on the access to the Parent class, in addition to the class being created. The AccessInfo class can be used to pass information to the newAccesses function. the newAccesses function must be protected, so that only the implementation can call it.

Access control to Access objects is handled in exactly the same as for other objects. Note that you can specify access permissions by class. If you want special restrictions on access objects, you can specify e.g.

  • role:'any', className:'Permission', opType:'any', targetName:'any', isPermitted:False
  • role:'accessAdmin', className:'Permission', opType:'any', targetName:'any',isPermitted:True

to limit access to a special accessAdmin role. Your application can put its own limits on which groups can be made 'accessAdmin'.

As you notice the use of 'any' may mean that several Permission objects are relevant for a given  case. A conflict resolution rule is needed - I propose that  the Permission where 'any' appears first (in the given attribute order) is given lower priority.

Explicit changing of attributes is done with the Access functions shown in MemopBaseClass. The function are put here because you can then use the normal access control system to set permission on a per-class basis. The names should be self-explanatory, given that 'recursive' means 'for the objects and all its child objects, recursively'. The functions must obviously delegate their work to one or more functions inside the Access package, as the actions, and especially the validity checking), will vary with the access implementation. The effect of the functions will be to add (remove, change) an AccessRole with the appropriate role,groupName combination to the AccessObject. I would propose that accessObjects should be split and merged transparently, so that they always reflect the correct access state. It follows that functions like AccessObject.addAccessRole should be reserved to the Implementation.

Implementation


The isPermitted function and the access functione in MemopsBaseClass are the same for any implementation. Also, much of the access customisation can be done simply by setting the Permissions objects. But some of the business rules of access control must be wired into the code of the Access package.

The individual implementations must make their own AccessObject.newAccess function. In addition they must make rules about what combinations of groups, roles, etc. are valid. E.g. Can an AccessObject have more than one owner? What roles must be present on every AccessObject? Are there roles that cannot be changed or deleted (e.g. 'creator')? Must certain types of UserGroups come in groups - e.g. must every LabProject Group come with a LabProjectLeader group? How many members can there be in the various types of group? What groupRoles are acceptable for what roles?

Below some examples of simple implementations

 

Simple Default

A simple default implementation could follow the following rules:

  • Right from the creation of the database there must be a UserGroup groupName:'root', groupingName:'root', groupType:'sysadmin', groupRole:'root'. The group must be created containing a single user with userId:'root'. The root user automatically has all privileges on all accessObjects and this can not be changed. Root access can neither be added to nor removed from any AccessObject.
  • All users are members of their own private single-member group, e.g. groupName:'myUserId', groupingName:'myUserId', groupType:user', groupRole:'user'
  • All objects must at all times have at least one owner.
  • The newAccesses function is called immediately after the creation of an empty object. It works as follows:
    1. newObj.giveAccess('owner',MemopsRoot.currentUserId)
    2. for info in MemopsRoot.accessInfos:
      newObj.giveAccess(info.role, obj.groupName)

 

No Access control

Implementations that do not want access control have various options:

  • Have all users log in as 'root' and have all objects owned by 'root'.
  • Have all users log in as either 'root' (for reference data) or as 'user' for all non-reference data.
  • Make a custom implementation that does not check - this might be faster.

 

PIMS access control

The original PIMS interface distinguished only betwen 'read', 'write', 'update', and 'delete'  permissions. The same effect can be obtained by using only the opTypes 'query', 'new', 'modify', and 'delete' (and maybe 'access') in the Permission objects, and by making all Permissions of the form role:'aRole', clasName:'any', opType:'anOptype', targetName:'any'.
The original PIMS interface did not use roles, but gave the permissions explicitly. The same effect can be obtained by making a role for every sensible combination of permissions and prohibiting other roles. This would not fit with the simple default implementation proposed above, but there could be others.

Complex access control

If you want more complicated access schemes there are many possibilities - they just need to be coded:

  • You can let newAccesses set permissions depending on the permissions on the parent object.
  • You can use AccessInfo to store information like role:'LabProject', groupName:'GTPases', or role:'department', groupName:'GSK CNS drugs', and set a whole series of group access permissions (group members and leaders, department members and heads, auditors, QA teams and their leaders, etc.) based on that information.
  • You could use the accesscontrol machinery to store e.g. who was the creator of an object, even if the creator had no access rights. That would mean more AccessObjects.
  • You could make a model change and add tiem stamps to AccessROle objects. You could then (mis)use them to store e.g. creation dates. That would mean one AccessObject per object.

Open questions:

The entire proposal is in a sense an open question. But some areas are more fluid than others.

  • You could remove the AccessObject and have a many-to-many link between MemopsBaseClass and AccessRole. In some ways this would fit better with the proposed usage. It would correspond to having a database table with the columns 'role', 'groupName', and 'MemopsBaseClass_ID' to represent teh many-to-many link. It might be better to model things this way, with the option to implement them differently 'under the hood', for speed..