Access control
From Qubit Toolkit
Design
This page proposes a new feature and reviews design options
Development
This page describes a feature that's in development
Documentation
This page documents an implemented feature
[edit] Requirements
Access control needed to provide a fine-grained, hierarchical, highly flexible system that allowed managing user and group privileges via the web-based user interface.
Several other design elements were highly desirable:
- Based on existing design patterns for a fine-grained ACL
- Allow system-wide access control definitions
- Use the Symfony plugin architecture to allow sharing the work with the Symfony community
- Performant - because access control touches all parts of the application, it must not adversely effect system responsiveness
- Access control "roles" will do double duty for assigning workflow triggers, tasks, and workitem lists. Of course ACLs will also be necessary to control who can define workflow processes, and who can access documents (e.g. information objects)
Some requirements for access control were defined in the Functional Requirements, although this also includes authentication requirements, which are handled already.
Specific use-case requirements were defined per milestone (below).
[edit] Minimum requirements for release 1.0.8
- restrict permission to create or edit an information object to specific repository(ies)
- link a user to 0..* repositories
- If 0 then the user can edit all repositories (i.e. default is permission to "edit all" to avoid having to update permissions each time a repository is added)
- If user account is linked to 1..* repositories then the user can only edit information objects that are linked to those repositories
- If user account is linked to 1..* repositories, when user is creating a new information object, the repository options are limited to just those 1..* repositories for which they have permission
- link a user to 0..* repositories
- publish permission
- user with this permission can see all information objects where publication status == "draft for approval" and has the ability to change the publication status to "published" (see related requirement: publication workflow (lite))
- users that don't have this permission cannot see any information objects where publication status == "draft for approval" on search results, browse lists, or show templates... unless they are the user that created that information object and are awaiting its publication approval
- user with this permission can see all information objects where publication status == "draft for approval" and has the ability to change the publication status to "published" (see related requirement: publication workflow (lite))
[edit] Minimum requirements for release 1.0.9-beta
- restrict access to individual digital objects
- restrict "editor" privileges to a specific taxonomy or authority record
- restrict "translator" role to a specific language for a specific user
- restrict ability to edit archival descriptions to allowed languages if source language != current language
- restrict translation of UI by language
[edit] Minimum requirements for release 1.1
- Secure digital assets directory to prevent http access by unauthorized users, issue 764
[edit] Solutions considered
The default Symfony security model and popular sfGuard plugin did not not provide resource-level permissions (all permissions are per page/action) or context specific permissions (such as current language for translation) which prompted research on an outside solution.
We examined two existing Access control APIs:
CakePHP ACL did not appear to allow multiple inheritance for groups (e.g. the user belongs to the contributor and translator groups) or allow for conditional authorization (i.e. only allow user to translate if current language is French).
[edit] Selected solution
Zend ACL was selected because it met all of the requirements and provided an easy integration with Symfony/Qubit.
Zend ACL provides a simple Interface for Resources and Roles that can be integrated easily with Qubit's existing ORM objects. [1] [2]
Zend ACL use a "last in, first out" conflict resolution system where the last permission defined is checked before earlier permissions - thus the first "positive" result (either grant or deny) will forgo checking additional permissions. [3] Hierarchical rule definition takes precedence over this "last in" system (e.g. Check all parent roles for valid permission before checking sibling roles).
[edit] Model
[edit] Group
- A user may belong to many groups
- Groups are hierarchical and may inherit permissions from parent groups
- Rules for child groups take precedence over parent permissions
Entity relations
- QubitUser many-to-many to QubitAclGroup
- QubitAclGroup one-to-many to QubitAclGroup (parent relationship)
- QubitAclGroup zero or one-to-many to QubitAclPermission
[edit] Resource
- Resources are hierarchical and a child resource inherits permissions from it's parent resource
- Rules for a child resource take precedence over rules at the parent level
- If no resource is specified, the permission applies to all resources in the system
Entity relations:
- QubitObject zero or one-to-many to QubitAclPermission
- QubitObject one-to-many to Resource (parent relationship)
Notes:
- Zend Acl provides a simple interface which allows easily using Qubit ORM objects (QubitInformationObject, QubitActor, QubitTerm, etc.) as an ACL resource
- Information objects provide a convenient way to provide permissions to all objects of a type (e.g. QubitInformationObject, QubitTaxonomy) via their ROOT object.
- Taxonomies currently do not extend QubitObject, which we will need to change for the foreign key realtionship from the permissions table to work.
- Other primary ORM objects (QubitActor, QubitRepository, QubitFunction) do not currently have a ROOT object, making it impractical to assign permissions for all QubitActors (for example)
- With Zend Acl a "Global tree" model is not required for system-wide permissions. However, because using a global tree will enforce unique lft and rgt values across classes, it does make it easier to select ascendants/descendants of the target resource.
[edit] Permission
Columns:
- user_id (int)
- foreign key to QubitUser
- null for group permission
- group_id (int)
- foreign key to QubitAclGroup
- null for user permission
- object_id (int)
- foreign key to QubitObject
- null for all objects/resources (system wide)
- grantDeny (tinyint)
- values: (1 = Grant, 0 = Deny)
- action (string)
- values: create, read, update, delete, viewDraft, publish, translate
- null to grant/deny all actions
- conditional (text)
- specify condition for permission (e.g. limit by repository id, limit to a particular language)
- constants (text)
- serialized PHP array listing constant values for conditional statements
Entity Relations:
- zero or one-to-many QubitUser to QubitAclPermission
- zero or one-to-many QubitAclGroup to QubitAclPermission
- zero or one-to-many QubitObject to QubitAclPermission
[edit] Web interfaces
Group
- List existing groups
- Create new groups
- Edit/delete existing groups
- Assign group-specific permisssions (per resource or system-wide)
User (Only ACL specific functions are listed)
- Assign user to groups
- Assign user-specific permissions (per resource or system-wide)
[edit] Discussion
[edit] September 22, 2008
Peter and David discussed the Access Control design Peter had in mind (and had partially implemented) vs. David's proposed design (this page). From this discussion we agreed that the design documented on this page was preferable.
[edit] Additional requirements
Some addition requirements arose from the discussion,
- Allow setting permissions dependent on the QubitInformationObject::REPOSITORY_ID column.
- Allow setting permissions dependent on the QubitInformationObjectI18n::CULTURE column (for translations)
- Add "translate" as a ACL permission
David: I propose meeting requirements #1 and #2 by implementing a flexible system for assigning permissions based on matching an arbitrary list of values for an arbitrary column. I'll need to think a bit on how to implement this
[edit] Other decisions
- Don't implement a dual system of an ACL module + symfony action security (YAML) system
- Would defeat the purpose of having a frontend access control system as any changes to security permissions would need to be mirrored by changes to the YAML files
- Doesn't offer significant security advantages
- Do try to implement the new ACL module as a symfony filter (possibly copying or extending the existing symfony security filter), thereby automatically making it part of any request to the application

