Access Control Proposal
Proposal for access control that is simple, supports fine-grained control, and supports role-based access algorithms.
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:

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.

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:
- Individual users
- Lab Projects (e.g. Kinases, Arabidopsis, ...). where each project had project members and
- Project leaders, with different rights.
- groupName:myUserId, groupingName:myUserId, groupType:user, groupRole:user
- groupName: Kinase_members, groupingName:Kinases, groupType:LabProject, groupRole:members
- groupName: Kinase_leaders, groupingName:Kinases, groupType:LabProject, groupRole:leaders
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
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:
- newObj.giveAccess('owner',MemopsRoot.currentUserId)
- 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..