Security and Authentication
From AlfrescoWiki
Authentication
Authentication is carried out/required at several entry points to repository:
- CIFS
- Web Client
- FTP
- WebDAV
- Web Services
- Spring Beans exposed as public services in Java
Authentication can be by an Alfresco ticket, a user name and password pair, or some other mechanism.
Using a ticket or username/password, a number of authentication instances may be tried for authentication, any one is sufficient.
Single sign on from the browser and via CIFS involves a multi-stage authentication process and may include negotiation of the authentication mechanism. There is no chaining of authentication for single sign on.
Authentication Overview
Notes
The authentication DAO and authentication component implementation are paired in a single instance of an Authentication Service Impl. The third element is the ticket component. See later.
The architecture could be unified by supporting complex callbacks from the AuthenticationService API. This would make the implementation parallel JAAS. So why not use JAAS completely? The configuration of JAAS via config files does not play well with the configuration of beans within the Spring framework. The call back would have to include service injection. This is going to give repeated configuration parameters and fun with things like hibernate persistence.
The ticket component should be added to the diagram.
MD4 password hashes live at the bottom of the NTLM and NTLM2 authentication protocols. This is a fundamental restriction as to when NTLM can be used.
Security Overview
Security includes the following functionality:
- users and user management;
- provision of personal information about users;
- user authentication;
- groups and group management;
- ownership of nodes within the repository;
- repository wide permissions;
- permissions at the node level;
- an extendable permission model; and
- access control, to restrict calls to public services to suitable authenticated users.
An authority is a string representation of a user name, group name, or role name.
Specific strings are used to identify well known authorities such as the administrator, everyone, owner and guest.
Prefixed strings are used to identify groups and roles.
Groups are identified by strings starting with the GROUP_ prefix, which should be hidden from end users in the client.
Similarly, roles start with the ROLE_ prefix.
Strings that do not match these patterns may be used for usernames.
The username is the key to obtain other information about a user such as their real identity, email etc.
This is held by the PersonService.
Some authorities are dynamic, such as the owner of a node and the owner of a lock held on a node.
These authorities are evaluated in the context of a node.
For some nodes you may be the "owner" of that node. You will then have the permissions granted to the owner authority available to you.
Dynamic authorities are similar to an access policy. You can evaluate any condition knowing the node to be accessed and the current user and decide if the user should
have an authority in the given context. An access policy would be a dynamic permission assignment.
Groups can include both other groups and users so it is possible to construct hierarchies.
When any call is made to the repository through the publc service API, the caller must first be authenticated.
This can be done by logging in using a username and password or using a ticket.
A ticket can be requested after logging in and can be used, under certain conditions, to revalidate a user.
Some applications and authentication mechanisms may support single sign on.
By default, the owner of a node is the person who created it. Ownership may be transferred to another user.
Only one user can own a node. However, other users may be able to take ownership of a node if they have suitable permission.
Permissions are identified by a string. A particular permission, for example "ReadChildren", may be granted or denied to an authority; a user, group, administrator, owner, etc. The children of a node will inherit permissions from their parents. So by default, the files in a folder will inherit their permissions from the folder. Permissions set on a node take precidence over permissions set on the parent nodes. The inheritance of permissions may be turned off for any node. A permission group is a convenient grouping of permissions such as "Read" made up of "ReadProperties" and "ReadChildren".
As the username is used as the key to find personal information, group membership and against which to assign permission, it is key to know if this is case sensitive or not.
The security framework as a whole may be case sensitive, or not.
Configuration of this key behaviour is set in repository.properties. It is quite unusual to have case sensitive user ids.
An ACL is a permission or permission group assigned to an authority for a particular node. An ACL can deny or allow. An "allow" permission will be inherited by any children unless the permission is denied by an ACL on a child or inheritance of ACLs is disabled on a child. Allow takes precedence. If "bob" is a member of the group "rats" and "bob" is allowed "read" and "rats" is denied "read" then "bob" will be allowed read. Any allow allows access, as opposed to a single deny denies all.
Global Permission - a permission or permission group assigned to an authority regardless of the node. A global permission takes precedence over node specific ACLS. If "bob" were granted the global permission "read" then "bob" would have the "read" permission for everything, regardless of any ACLs set on any nodes.
What is a role?
We think of role is an idea in the UI that refers to a set of permissions that can be applied to a folder. If you have role based permissions, this is not usually the same thing; it is really just another authority, like groups, with global permissions.
How are you using roles?
- If you are assigning a permission like read/write/delete to a role, you are using role as an authority. Groups could probably be used to do the same thing.
- If you are assigning roles to people and groups you are using roles as a convenient name for a bundle of permissions and other roles. This is what we do in the UI.
A Simple Permissions Example
The diagram below shows some simple permissions that have applied to a basic directory strutcure. There are two users, andy and dave, to which permissions have been assigned. Permissions are also assigned to two special authorities: one that applies to all users and one that applies to the owner of a node. In this example, we are only interested in read, create and all permissions.
A summary of the permissions shown in the above diagram:
- /
- All users can read the properties and children of this folder.
- No one can alter properties or children.
- /app:company_home
- Permissions are inherited from the parent nodes.
- All users can read the properties and children of this folder (inherited from /).
- /app:company_home/app:andy
- Permissions are not inherited from the parent nodes.
- All permissions are granted to andy.
- All users can read the properties and children of this folder.
- /app:company_home/app:dave
- Permissions are not inherited from the parent nodes.
- All permissions are granted to dave.
- There are no permissions granted to other users.
- /app:company_home/app:public
- Permissions are inherited from the parent nodes.
- All users can read the properties and children of this folder (inherited from /).
- /app:company_home/app:andy/app:private
- Permissions are not inherited from the parent nodes.
- All permissions are granted to andy.
- There are no permissions granted to other users.
- /app:company_home/app:andy/app:public
- Permissions are inherited from the parent nodes.
- All users can read the properties and children of this folder. This permission takes precidence over the permission set on the parent node. If the permission on the parent node is altered it will have no effect on this permission.
- /app:company_home/app:andy/app:collab
- Permissions are inherited from the parent nodes.
- andy inherits all permissions.
- dave can read the properties and children of this folder, regardless of the permissions set on any parent nodes.
- dave can create children in this folder, regardless of the permissions set on any parent nodes.
- The owner of a node has all permissions on their content. If dave creates a file he will have full control over it. andy could take ownership of this file, as he has all permissions; dave would then only have read access to this file, as he would no longer be the owner.
- All users are denied read permission; this takes precidence over the allow read permissions to all users set on the parent node.
Enforcement of security
Security is enforced when methods are called on public services. Access control is defined in meta-data and not in code. The meta-data is used to "inject" security in an aspect oriented way. Security settings can therefore be changed without affecting the implementation code.
Security could be based on:
- the method accessed;
- the objects provided as arguments to the called method; and
- the objects returned by the invoked method.
Services
Security is supported by five key services:
- the Authentication Service;
- the Authority Service;
- the Ownable Service;
- the Permission Service; and
- the Person Service.
The Authentication Service
The authentication service provides an API to:
- authenticate using a user name and password;
- authenticate using a ticket;
- create, update and delete authentication information;
- clear the current authentication;
- invalidate a ticket;
- get the username for who is currently authenticated;
- get a ticket for subsequent re-authentication; and
- determine if the current user is "the system user".
Not all implementations will support creating, updating and deleting authentication information.
The authenticated username is used as they key to obtain other security information such as group membership,
the details about the person, to record a user as the owner of an object.
It is one of the identifiers against which permissions may be assigned.
The authentication service does not provide any details about a user other than authentication.
The authentication service stores authentication information on the calling thread.
Application developers should ensure that this information is cleared.
Implementations
Default
The implementation and configuration for this service can be found in authentication-services-context.xml.
The product ships with a default authentication implementation. This default implementation coordinates two service providers for the AuthenticationComponent and MutableAuthenticationDAO. It also uses the permission service provider interface to clear up permissions as users are deleted. Tickets are supported using the ticket component.
The only configuration option that should be changed is if user names are case sensitive. This is set in repository.properties.
Note that in the authentication service implementation, the MD4 hash algorithm is used to encode passwords.
This is to allow the open source authentication service to support authentication using NTLM while using the CIFS protocol.
NTLM
See Configuring NTLM to configure NTLM authentication and single sign on using NTLM. As well as requiring changes to the authentication service configuration, authentication filters in the client have to be changed to match.
Any of the acegi authentication mechanisms may be used. Alfresco expects a vanilla Acegi Authentication object in the Acegi context. This allows third parties to use classes extending UserDetails without any issue, and implement the services described in this guide on top of an existing Acegi security implementation.
ACEGI
Alfresco ships with a DAO provider implementation to plug into the Acegi DAO provider, this supports authentication using information held in the repository. There is a provider to authenticate credentials that have programmatically been set as authenticated. The authentication service uses this provider to set the current user (without acegi attempting to authenticate against the dao). An NTLM authentication implementation is also available.
Alfresco does not add roles and groups etc to the objects held in the acegi context. This information is encapsulated through the AuthorityService. If you use acegi already, you could implement an authority service that retrieves group and role information from the acegi authentication object.
LDAP, JAAS, Kerberos
See Enterprise Security and Authentication Configuration.
Chaining
The ChainingAuthenticationServiceImpl can be used to link together any combination of other authentication services.
It takes a list of authenticationServices and an optional mutableAuthenticationService.
If the mutableAuthenticationService is set then users can be managed in this authentication service. They can be created, deleted, password updated etc. No attempt is made to modify the other services.
All other operations are tried against each authentication service in the order they are defined. If a mutableAuthenticationService is present it will be treated as first in the list.
Authentication will be tried against each service in turn. If all fail authentication will fail. If one authentication is allowed authentication will be granted. It does not matter if previous services contain the same user with different passwords. This will change when domains are fully supported. It is not advisable for users with the same id to exist in more than one authentication service that are linked together. If this is done - authentication can be done against any - the first definition will define the properties such as "enabled".
This implementation could be used for:
- External LDAP users and internal users held in alfresco
- Several LDAP repositories
- One LDAP repository with users to be found in different locations on the server.
See projects\repository\config\alfresco\extension\chaining-authentication-context.xml.sample for an example of JAAS and Alfresco authentication services combined.
In the configuration, take care to give unique bean names where required in the definitions of each authentication service stack.
The Authority Service
The authority service is responsible for:
- creating authority identifiers;
- querying for authority identifiers;
- deleting authority identifiers;
- structuring authority identifiers into hierarchies;
- supporting queries against authority identifiers hierarchies;
- finding all the authorties that apply to the current authenticated user; and
- determining if the current authenticated user has admin rights.
It does not support users, authentication and user management. This is for the AuthenticationService.
Implementations
The authority service is defined and configured in authority-services-context.xml. This file includes documentation on how to configure specific implementations of this service.
You can provide your own implementation of this service by implementing the
org.alfresco.service.cmr.security.AuthorityService interface. The implementation should be a spring bean identified by the id authorityService. This will then get wrapped with the appropriate transactional and security behaviour and exposed as a service bean called AuthorityService.
Configuration
In both cases, the admin users can be changed by altering the admin users property, on the authorityService bean defined in authority-services-context.xml. In the standard Alfresco Tomcat installation, this file is located in the directory /usr/local/alfresco/tomcat/webapps/alfresco/WEB-INF/classes/alfresco.
<bean id="authorityService" .....>
....
....
<!-- -->
<!-- A list of users with admin rights. -->
<!-- -->
<!-- If the security framework is case sensitive these values should -->
<!-- be case sensitive user names. If the security framework is not -->
<!-- case sensitive these values should be the lower-case user names. -->
<!-- -->
<!-- By default this includes: -->
<!-- admin (the user name of default alfresco admin user) -->
<!-- administrator (the windows default admin user) -->
<!-- -->
<!-- This assumes that user names are not case sensitive. -->
<!-- -->
<property name="adminUsers">
<set>
<value>admin</value>
<value>administrator</value>
</set>
</property>
</bean>
To include andy as an admin user and remove the alfresco admin user you would change the config to
....
<property name="adminUsers">
<set>
<value>administrator</value>
<value>andy</value>
</set>
</property>
....
The Ownable Service
The idea of file ownership is present in both unix and windows. The repository has the concept of node ownership. This is optional and is implemented as an aspect. The owner of a node may have specific ACLs granted to them. Owner is a dynamic authority. If the Ownable aspect is not present, the creator is used as the default owner. The owner is simply defined using the user name. If the username of the current user matches the user name of the node owner then the current user will be granted all permissions assigned to the owner authority.
The ownable service is responsible for:
- determining the owner of a node;
- setting the owner of a node;
- determining if a node has an owner; and
- allowing the current user to take ownership of a node.
The Ownable Service is supported by an ownable aspect defined in contentMode.xml.
There are a set of well known permissions associated with the Ownable Service.
Implementations
The beans and configuration for the ownership service are defined in ownable-services-context.xml. There are no configuration options for this service.
The Permission Service
The permission service is responsible for:
- providing well known permissions and authorities;
- providing an API to read, set and delete permissions for a node;
- providing an API to query, enable and disable permission inheritance for a node; and
- determine if the current, authenticated user has a permission for a node.
The PermissionService interface defines constants for well known permissions and authorities.
Implementations
The default implementation coordinates implementations of two service provider interfaces; a ModelDAO and a PermissionsDAO. A permission is just a name scoped by the fully qualified name of the type or aspect to which it applies. The beans are defined and configured in public-services-security-context.xml. This file also contains the configuration for security enforcement.
The ModelDAO interface defines an API to access a permissions model. This model defines low level permissions, groups of permissions, and groups of permissions that extend existing groups of permissions. Each permission or grouping of permissions applies only in the context of a type or aspect, or to all types. Permission requirements may then be set in service beans to protect method invocations. For example, a method may only be called by authenticated users that have been granted particular permissions on one of the nodes identified by a parameter for the method. The model also supports static or global permissions. Global permissions apply to all objects in the repository and take precedence over permissions assigned to individual nodes. For example, the assignment of all permissions to the admin user for all nodes in the repository. The permission model is separate from the data dictionary; fully qualified type names provide the key to link the two together.
The PermissionsDAO interface defines an API to persist permissions set against specific nodes and to determine if the current user has a specified permission for a given node. This takes into account both global and node level permissions and all the authorities that apply to the current user.
In the default PermissionsDAO implementation, a permission or permission group is granted or denied to an authority for a particular node. There can be any number of these entries for a given node. Permissions will be inherited by any children unless the permission is denied by an ACL on a child or inheritance of ACLs is disabled on a child. An ACL can deny or allow. Allow takes precedence. If "bob" is a member of the group "rats" and "bob" is allowed "read" and "rats" is denied "read" then "bob" will be allowed read. Any allow allows access, as opposed to Microsoft file systems, wherein a single deny denies all.
Permissions are assigned at the node level, not at the attribute level. Ths makes sense with the current implementation of search.
Search results need to reflect what the user performing the search can see. It makes sense that all properties have the same read access as the node, as nodes are indexed for searching. Applying read ACLs at the property level would require a change to the indexing implementation or a complex post analysis to work out how nodes were found by the search.
If not, The values of properties could be deduced by how a readable node was found from a search on restricted properties.
Fine grain attribute permissions could be implemented by using children nodes to partition meta-data if required.
If we need to support attribute level permissions in the future, the best compromise would be to index as we are now but only allow different read access for properties that are not indexed - so their content can not be determined via query. Alternatively, only index properties etc. that have the same access permissions as the node. In both cases the content can not be determined from the index. We could have additional indexing support to search against restricted properties.
What is a role? You could define groups and roles, against which you could assign permissions and permission groups, by convention, starting with "ROLE_" for roles and "GROUP_" for groups. The AuthorityService is responsible for providing the additional authorities, over and above user name, that apply to a particular user. This service can be extended or replaced if you want to add additional roles/groups to users. We do not use roles in this way, as there is not much distinction between a role and a group.
A role is more like a set of permissions that are granted to a particular authority for all nodes. Or if it is context sensitive, a set of permissions granted to a particular authority for a given node and maybe its children. Both of these are supported by the implementation
The available permissions are defined in the permissions model. This is defined in the permissionDefinitions.xml file in the repository/config/aflresco/model directory. This configuration is loaded in a bean definition in the public-services-security-context.xml file. This file also defines global permissions. The definition file is read once at application start up. If you make changes to this file you will have to restart the repository to apply the changes.
Here is the v1.0 permission model definition as an example PermissionModelDefintionExample
The Person Service
The person service is the API by which nodes of the person type, as defined in contentModel.xml, should be accessed.
The person service is responsible for:
- obtaining a reference to the Person node for a given user name;
- determining if a person entry exists for a user;
- potentially creating missing people entries, with default settings, on demand;
- supplying a list of mutable properties for each person;
- creating, deleting and altering personal information.
Implementations
The beans to support the person service and its configuration can be found in authentication-services-context.xml. The principle configuration options are around how people are created on demand if users are managed via NTLM or some other external user repository.
If you are using an external user repository it is likely that you will have to implement your own person service using the org.alfresco.service.cmr.security.PersonService interface. If not, you will want people to be created in the repository on demand so they do not have to exist before a user logs in.
Handling duplicate person entries for the same uid - from 2.0 onwards
How do I end up with duplicate person objects?
- A configuration error in the LDAP import - each import creates a new person - the query to find the existing person does not match the person you expect. Thus usually means the uid attriute is not correct or consistent over configurations. There is a task to simplify the config and make this less likely.
- Before the first LDAP import completes a person is created automatically as the user logs in. The LDAP import will not see the auto created person - a duplicate will be present after the import completes. One way to avoid this is to turn off the auto creation of people.
- There are simultaneous logins for the same person which auto create the person details.
Duplicate person entries can now be handled in a number of ways.
- Find the best match, use it; leave all entries as they are
- Find the best match and fix the other uids to be unique by appending a guid
- Find the best match and delete all others
This can be configured on the person service in authentication-services-context.xml.
Person service properties to split users
- processDuplicates
- boolean
- - true(default) - handles duplicates
- - false - throws an error
- lastIsBest
- boolean
- - true(default) - prefer the most most recently created or updated person entry
- - false - the reverse
- used to detemine the best match
- includeAutoCreated
- boolean
- - false(default) - do not include auto created entries when searching for the best match
- - true - they are included
- used to detemine the best match
- duplicateMode
- one of DELETE, SPLIT, LEAVE
- How to deal with duplicates
- - leave them as they are but just use the best one found - the duplication will remain
- - leave the best one and use it, split the rest by appending a GUID - the duplication will be removed but the person entries remain
- - leave the best one and use it, all others are deleted - the duplication will be removed and so will the duplicate person objects.
TODO: include the existance of preferences and personal configuration in selecting the best person against which to match for duplicate users.
Creating home spaces - from 1.4 onwards
Home space creation can be done when people are created from v1.4 onwards. See
When a person is created a call is made to a home space provider as specified by the property cm:homeFolderProvider. This names a home space provider to use on a per person basis. If the property is not specified then a default home space provider is used.
A home space provider may also use the cm:defaultHomeFolderPath property.
Prior to 1.4 any person created on demand or via LDAP import would be assigned the Company Home space as their home space. This meant it was awkward to set up home spaces. This behaviour was tied to the default PersonService implementation. Home space creation or assignment is now pluggable. The change removes the companyHomePath property from the default PersonServiceImpl, and it adds the defaultHomeFolderProvider property which is set for each person created on demand, if this is allowed.
To change the default home folder provider, you can override the homeFolderManager bean to change the defaultProvider property. See below for examples and also authentication-services-context.xml.
<bean name="homeFolderManager" class="org.alfresco.repo.security.person.HomeFolderManager">
<property name="nodeService">
<ref bean="nodeService" />
</property>
<property name="policyComponent">
<ref bean="policyComponent" />
</property>
<property name="defaultProvider">
<!--
<ref bean="userHomesHomeFolderProvider" />
-->
<ref bean="myHomeFolderProvider" />
</property>
</bean>
Providers
General
The person service knows nothing of home folder creation and providers. (This behaviour is bound to a create policy for the cm:person type using a bean exposing the HomeFolderManager class.) HomeFolderManager defines the default home folder provider.
The base class used to implement the providers below allows permissions to be set. There are two sets of permissions: those set when home spaces are created and those set when a home space is assigned (it already existed).
Common properties for all providers:
- name
- The bean name is the name of the home space provider. Set cm:homeSpaceProvider on cm:person to this value to select the use of a particular provider.
- homeFolderManager
- Inject the home folder manager.
- storeRef
- A store ref (used for search context etc - to look up path)
- serviceRegistry
- Inject the service registry.
- path
- A path to a folder. This is required in some form by all providers. They may use this path in different ways.
- ownerOnCreate
- The name of the owner to set on creation. If not specified then the person will own their home space. So you could set this to "admin" if you do not want folk to own their home spaces.
- inheritsPermissionsOnCreate
- If a home space is created, set if permissions are inherited. The default is false.
- ownerPemissionsToSetOnCreate
- A set of permissions to set for the owner when a home space is created.
- permissionsToSetOnCreate
- A Map of Sets of permissions to for specified users when a home space is created
- userPemissions
- The permissions to set for the user/person when a homespace is created or an existing one reused.
- clearExistingPermissionsOnCreate
- Specifically to support clearing permissions when a home folder is created from a template. If true then permissions are cleared, if false they will be taken from the template.
Defining a home space that is an existing space (ExistingPathBasedHomeFolderProvider)
- path
- The path to folder to assign as the home space.
The default in 1.3 and before was to use company home.
This can be implemented using the provider below.
<bean name="companyHomeFolderProvider" class="org.alfresco.repo.security.person.ExistingPathBasedHomeFolderProvider">
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
<property name="path">
<value>/${spaces.company_home.childname}</value>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
<property name="homeFolderManager">
<ref bean="homeFolderManager" />
</property>
</bean>
<bean name="guestHomeFolderProvider" class="org.alfresco.repo.security.person.ExistingPathBasedHomeFolderProvider">
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
<property name="path">
<value>/${spaces.company_home.childname}/${spaces.guest_home.childname}</value>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
<property name="homeFolderManager">
<ref bean="homeFolderManager" />
</property>
<property name="userPemissions">
<set>
<value>Consumer</value>
</set>
</property>
</bean>
Creating a home space based on uid
Below is an example configuration for creating a home folder based on the uid of a user. As the uid is unique it can be used to name a home folder.
Properties:
- templatePath
- An optional path to a template node used to create home spaces.
- path
- The folder in which to create home spaces.
<bean name="exampleHomeFolderProvider" class="org.alfresco.repo.security.person.UIDBasedHomeFolderProvider">
<property name="serviceRegistry">
<ref bean="ServiceRegistry" />
</property>
<property name="path">
<value>/${spaces.company_home.childname}</value>
</property>
<property name="storeUrl">
<value>${spaces.store}</value>
</property>
<property name="homeFolderManager">
<ref bean="homeFolderManager" />
</property>
<property name="ownerOnCreate">
<value>admin</value>
</property>
<property name="inheritsPermissionsOnCreate">
<value>false</value>
</property>
<property name="ownerPemissionsToSetOnCreate">
<set>
<value>Coordinator</value>
<value>All</value>
</set>
</property>
<property name="permissionsToSetOnCreate">
<map>
<entry key="GROUP_A">
<set>
<value>Consumer</value>
</set>
</entry>
<entry key="GROUP_B">
<set>
<value>Editor</value>
<value>Collaborator</value>
</set>
</entry>
</map>
</property>
<property name="userPemissions">
<set>
<value>All</value>
</set>
</property>
<property name="templatePath">
<value>/${spaces.company_home.childname}/${spaces.guest_home.childname}</value>
</property>
<property name="clearExistingPermissionsOnCreate">
<value>true</value>
</property>
</bean>
Also refer to authentication-services-context.xml for examples of "userHomesHomeFolderProvider" (the current default provider, which creates home folders under User Homes) and "personalHomeFolderProvider" (which creates home folders under Company Home).
Defining a home space during bootstrap
The BootstrapHomeFolderProvider class is present to support specifying home folders. You will probably not have to use this.
LDAP Import
The LDAP import can specify a default home folder provider name, or it could be set per person imported by setting the cm:homeFolderProvider property from an attribute stored in LDAP.
To set the default provider for imported LDAP users that do not specify a specific home folder provider
...
<property name="attributeDefaults">
<map>
<entry key="cm:homeFolderProvider">
<!-- The name of the provider bean from the xml config -->
<!-- <value>companyHomeFolderProvider</value> -->
<!-- <value>guestHomeFolderProvider</value> -->
<!-- <value>personalHomeFolderProvider</value> -->
<!-- For the old behaviour this would be -->
<value>companyHomeFolderProvider</value>
</entry>
</map>
</property>
...
Security Enforcement
Security is enforced around public services. Web services, the UI, CIFS, WebDav, FTP etc all use public services and so include security enforcement. Public services are defined in public-services-context.xml.
Access control allows or prevents people from executing service methods on a particular object by checking if the current user, or any of the authorities granted to the current user, has a particular permission or permission group.
For example, on the NodeService bean, the readProperties method checks that the current user has read access to the properties of the node before invoking the method. On the SearchService, the results from queries are restricted to return only the nodes for which a user has read permission.
Configuring Security Enforcement
Security is enforced by defining proxies for each internal service implementation and adding. A method interceptor to enforce security is included in each proxy. When a method is called on a public service, the security interceptor is called before it returns execution to the internal implementation. At this stage, the interceptor can examine the function arguments to the method and check that the user has the appropriate rights for each in order to invoke the method.
For example, a method delete(NodeRef nodeRef) exists on the node service. The security interceptor can see the nodeRef argument before the underlying delete(...) method is called.
If configured correctly, the interceptor could check that the current user has "Delete" permission for the node. If not, a security exception is raised. If permission allows the method goes ahead.
In a similar manner, after a method has executed the interceptor can examine the retured object and decided if it should return it the caller. For example, a search method could return a list of nodes. The security interceptor could filter this list for only those nodes that the current user can "Read".
It is possible you may want to configure a method so that it can only be called by an admin user, specific users, groups of users etc. Ths can also be enforced by the security method interceptor.
Access control for public services is definied in public-services-context.xml. The public services are the only Spring beans to have access restrictions.
Each service defines a method interceptor bean that enforces any security requirements for method calls. By convention, these are defined in public-services-security-context.xml, along with any other supporting beans. This file also defines the location from which the permission model is loaded.
Here is the v1.0 public services security context as an example PublicServicesSecurityContext
Defining method level security
In public-services-security-context.xml the beans required to support acegi based security around method invocation are defined. This configures two alfresco specific beans: a voter that can veto method execution based on the permissions granted to the current user for specifc arguments to the method, and an after invocation provider to apply security to objects returned by methods. Method access defined in the normal acegi way with a few additions.
In the followng: ? represents an authority (user name or group), # a method argument index and * a string representation of a permission.
Pre-conditions take one of the following forms:
- ACL_METHOD.?
- Access to the method is restricted to those with the given authority in alfresco.
- This could be a user name or group. Dynamic authorities are not supported
- ACL_NODE.#.*
- Access control is restricted to users who have the the specified permission for the node on the identified argument.
- If the argument is a NodeRef it will be used; if it is a StoreRef then the root node for the store will be used; if it is a ChildAssociationRef then the child node will be used.
- ACL_PARENT.#.*
- Access control is restricted to users who have the the specified permission for the parent of the node on the identified argument.
- If the argument is a NodeRef the parent of the node will be used; if it is a ChildAssociationRef then the parent node will be used.
- ROLE_...
- Check for an acegi authority starting with ROLE_
- GROUP_...
- Check for an acegi authority starting with GROUP_
If more than one ACL_NODE.#.* or ACL_PARENT.#.* entry is present then all the permissions must be available for the method to execute.
Post-conditions take the forms:
- AFTER_ACL_NODE.*
- Similar to ACL_NODE.#.* but the restriction applies to the return argument.
- The supported return types are ChildAssociationRef, FileInfo, NodeRef, StoreRef, ResultSet; Collections and arrays of StoreRef, NodeRef, ChildAssociationRef, and FileInfo.
- AFTER_ACL_PARENT.*
- Similar to ACL_PARENT.#.* but the restriction applies to the return argument.
- The supported return types are ChildAssociationRef, FileInfo, NodeRef, StoreRef, ResultSet; Collections and arrays of StoreRef, NodeRef, ChildAssociationRef, and FileInfo.
The post-conditions will create access denied exceptions for return types like NodeRef's, StoreRef's, ChildAssociationRef's. For collections and arrays members will be filtered based on the access conditions.
Some examples and explanations taken from the method security interceptor for the NodeService public service bean.
Refer to the NodeService interface for the method documentation and methos parameters etc.
org.alfresco.service.cmr.repository.NodeService.getStores=AFTER_ACL_NODE.sys:base.Read
If the returned object is a collection or array then filter its members to contain only those that can be read by the current user. If it is a single reference to a node of some form, throw an AccessDeniedException.
org.alfresco.service.cmr.repository.NodeService.createStore=ACL_METHOD.ROLE_ADMINISTRATOR
Allow access to users who are members of the administrator group. (This does not use an acegi ROLE_... as we do not bind groups and roles to the acegi authentication object.
org.alfresco.service.cmr.repository.NodeService.exists=ACL_NODE.0.sys:base.Read
Only allow for users who have read permission for the node specified by the first parameter of the method.
org.alfresco.service.cmr.repository.NodeService.createNode=ACL_NODE.0.sys:base.CreateChildren
Only allow for users who have create children permission for the node specified by the first parameter of the method.
org.alfresco.service.cmr.repository.NodeService.moveNode=ACL_NODE.0.sys:base.WriteProperties,ACL_PARENT.0.sys:base.DeleteChildren,ACL_NODE.1.sys:base.CreateChildren
Only users who can write the properties of the moving node, remove the node from its parent, and ann children to the destination node can perform a move node.
org.alfresco.service.cmr.repository.NodeService.setChildAssociationIndex=ACL_PARENT.0.sys:base.WriteProperties
Setting the index of an association requires the write properties permissions
org.alfresco.service.cmr.repository.NodeService.getType=ACL_NODE.0.sys:base.ReadProperties
Getting the type of a node required read permission for properties.
org.alfresco.service.cmr.repository.NodeService.addAspect=ACL_NODE.0.sys:base.Write
Adding an aspect to a node requires the write permission set.
org.alfresco.service.cmr.repository.NodeService.removeAspect=ACL_NODE.0.sys:base.Write
Remoing an aspect from a node requires the write permission set.
org.alfresco.service.cmr.repository.NodeService.hasAspect=ACL_NODE.0.sys:base.ReadProperties
Querying if a node has an aspect requires the read properties permission on the node.
org.alfresco.service.cmr.repository.NodeService.getAspects=ACL_NODE.0.sys:base.ReadProperties
Getting a list of all aspects for a node requires the read properties permission on the node.
org.alfresco.service.cmr.repository.NodeService.deleteNode=ACL_NODE.0.sys:base.Delete
Deleting a node requires the delete permission. Testing for delete on the children is not possible for performance reasons. The node service should retest this for each node that it tries to delete.
org.alfresco.service.cmr.repository.NodeService.addChild=ACL_NODE.0.sys:base.CreateChildren,ACL_NODE.1.sys:base.ReadProperties
Adding children to a node requires the create children property on the parent and read properties on the node (this ensures that you can see the child node)
org.alfresco.service.cmr.repository.NodeService.removeChild=ACL_NODE.1.sys:base.Delete
To remove a child node you need delete permission for the child (this includes the test to see if it is deletable from the parent.)
org.alfresco.service.cmr.repository.NodeService.getProperties=ACL_NODE.0.sys:base.ReadProperties
Accessing the properties of a node requires the permission to read properties.
org.alfresco.service.cmr.repository.NodeService.getProperty=ACL_NODE.0.sys:base.ReadProperties
Accessing a particular property on a node requires the permission to read properties.
org.alfresco.service.cmr.repository.NodeService.setProperties=ACL_NODE.0.sys:base.WriteProperties
Setting all properties on a node required the permission to write properties.
org.alfresco.service.cmr.repository.NodeService.setProperty=ACL_NODE.0.sys:base.WriteProperties
Setting a specific property on a node required the permission to write properties.
org.alfresco.service.cmr.repository.NodeService.getParentAssocs=ACL_NODE.0.sys:base.ReadProperties,AFTER_ACL_PARENT.sys:base.Read
Getting the parent associations on a node requires read access to the node. The method only reports the parents that are visible. There may be mutiple parents some of which are not visible. You must have access via the primary parent to read a node.
org.alfresco.service.cmr.repository.NodeService.getChildAssocs=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read
Reading child associations requires read access to the node and filters for only the children that are visible.
org.alfresco.service.cmr.repository.NodeService.getPrimaryParent=ACL_NODE.0.sys:base.ReadProperties,AFTER_ACL_PARENT.sys:base.Read
Accessing the primary parent requires that the node and its primary parent are accessible. In future, we may allow access along a link and not just the primary parent, in which case it is possible that the primary parent is not visible.
org.alfresco.service.cmr.repository.NodeService.createAssociation=ROLE_AUTHENTICATED org.alfresco.service.cmr.repository.NodeService.removeAssociation=ROLE_AUTHENTICATED org.alfresco.service.cmr.repository.NodeService.getTargetAssocs=ROLE_AUTHENTICATED org.alfresco.service.cmr.repository.NodeService.getSourceAssocs=ROLE_AUTHENTICATED
Currently, there are no retrictions on the creation of associations between nodes.
org.alfresco.service.cmr.repository.NodeService.getPath=ACL_NODE.0.sys:base.ReadProperties
Getting the primary path to a node requires read access to the node.
org.alfresco.service.cmr.repository.NodeService.getPaths=ACL_NODE.0.sys:base.ReadProperties
Getting all the paths to a node requires read access to the node.
The default permission model and simple extensions
The default permission model is defined in config/alfresco/model/permissionDefinitions.xml according to config/alfresco/model/permissionSchema.dtd, which describes the elements of the model and how they should be used.
The file that defines the permission model is defined in public-services-security-modex.xml in the permissionsModelDAO bean.
To extend or change the permission model:
1) In the extensions directory, over ride this bean to point to a file containing the complete permission definitions
<bean id='permissionsModelDAO' class="org.alfresco.repo.security.permissions.impl.model.PermissionModel">
<property name="model">
<value>alfresco/extension/myPermissionDefinitions.xml</value>
</property>
<property name="nodeService">
<ref bean="nodeService" />
</property>
<property name="dictionaryService">
<ref bean="dictionaryService" />
</property>
</bean>
2) Update the permissions definitions as required.
Simple changes to the existing permissions model
Removing all permissions from OWNER of a file
This may have side effects, removing write permission where it is required (e.g. to add shortcuts to the person object). This means all users will require explicit permissions to be set for them.
<globalPermission permission="Read" authority="ROLE_OWNER"/>
Adding another role to the default list
How to add your own type or aspect, assign permissions for it, configure it and secure a service that uses it ....
The Ownable Permissions Model
We will take implementing the ownable aspect as an example.
Assuming the ownable aspect has been defined in a model, we can link it into the permission model XML definition.
<!-- ============================================== -->
<!-- Permissions associated with the Ownable aspect -->
<!-- ============================================== -->
<permissionSet type="cm:ownable" expose="selected">
<!-- Permission control to allow ownership of the node to be taken from others -->
<permissionGroup name="TakeOwnership" requiresType="false" expose="false"/>
<!-- The low level permission to control setting the owner of a node -->
<permission name="SetOwner" expose="false" requiresType="false">
<grantedToGroup permissionGroup="TakeOwnership" />
<!-- require to be able to reach the node and set properties in the node -->
<requiredPermission on="parent" name="ReadChildren" />
<requiredPermission on="node" name="WriteProperties" />
</permission>
</permissionSet>
This configuration entry defines a set of permissions related to the ownable aspect. The set definition says that only some of permissions defined here should be reported in the list of permissions that can be set on a node. As opposed to saying all are exposed.
It defines one low level permission "SetOwner". The expose option determines if this low level permission should be exposed to an end user; in this case not. The requires type implies that this permission can be set for any object type, not just those that are of a derived type or have an aspect of the derived type. This permission is added to the TakeOwnership permission group. The permission requires some other permissions to be granted to the user. That they can access the node, and all of its parents "ReadChildren" and because owership is held as a node property, to change this you need the "WriteProperties" permission.
The "TakeOwnership" permission group again can be used for any type, and is not exposed in the UI.
Effectively this support is all hidden at the moment.
The definition of Ownable service
This can be found in public-services-context.xml.
<!-- Ownable Service -->
<bean id="OwnableService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.security.OwnableService</value>
</property>
<property name="target"><ref bean="ownableService"/></property>
<property name="interceptorNames">
<list>
<idref local="OwnableService_transaction" />
<idref local="exceptionTranslator" />
<idref bean="OwnableService_security" />
<idref local="OwnableService_descriptor" />
</list>
</property>
</bean>
<bean id="OwnableService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<bean id="OwnableService_descriptor" parent="AlfrescoServiceDescriptor">
<property name="interface">
<value>org.alfresco.service.cmr.security.OwnableService</value>
</property>
<property name="description">
<value>OwnableService Service</value>
</property>
</bean>
Security Configuration for this service
This definition refers to the OwnableService_security bean. This is defined in public-services-security-context.xml. This definition would place no restrictions on this service.
<bean id="OwnableService_security" class="org.alfresco.repo.security.permissions.impl.AlwaysProceedMethodInterceptor" />
The Lockable permissions model and Check Out/Check In
Now would be a good time to look at the rest of the permission model config to see what it does.
The lockable permission model
Lock and Check Out/Check In permissions are defined against the lockable aspect.
The permission model entries for the lockable service and the global permissions that allow the owner of a lock to check in, unlock, and cancel a check out.
...
<!-- =================================================== -->
<!-- Permission related to lock, check out and check in. -->
<!-- =================================================== -->
<permissionSet type="cm:lockable" expose="selected">
<!-- At the moment these permissions are hidden so they do not appear in the list -->
<!-- of permissions. -->
<!-- Check Out permission - exposed for all object types -->
<permissionGroup name="CheckOut" requiresType="false" expose="false"/>
<!-- Check In permission - only exposed when the lockable aspect is present -->
<permissionGroup name="CheckIn" requiresType="true" expose="false"/>
<!-- Cancel Check Out permission - only exposed for the lockable aspect is present -->
<permissionGroup name="CancelCheckOut" requiresType="true" expose="false"/>
<!-- Low level lock permission -->
<permission name="Lock" requiresType="false" expose="false">
<grantedToGroup permissionGroup="CheckOut" />
<requiredPermission on="node" type="sys:base" name="Write"/>
</permission>
<!-- Low level unlock permission -->
<permission name="Unlock" requiresType="true" expose="false">
<grantedToGroup permissionGroup="CheckIn" />
<grantedToGroup permissionGroup="CancelCheckOut" />
</permission>
</permissionSet>
...
...
<!-- Unlock is granted to the lock owner -->
<globalPermission permission="Unlock" authority="ROLE_LOCK_OWNER"/>
<!-- Check in is granted to the lock owner -->
<globalPermission permission="CheckIn" authority="ROLE_LOCK_OWNER"/>
<!-- Cancel check out is granted to the locak owner -->
<globalPermission permission="CancelCheckOut" authority="ROLE_LOCK_OWNER"/>
...
The service defintion
....
<!-- COCI Service -->
<bean id="CheckoutCheckinService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.coci.CheckOutCheckInService</value>
</property>
<property name="target"><ref bean="checkOutCheckInService"/></property>
<property name="interceptorNames">
<list>
<idref local="CheckoutCheckinService_transaction" />
<idref local="exceptionTranslator" />
<idref bean="CheckoutCheckinService_security" />
<idref local="CheckoutCheckinService_descriptor" />
</list>
</property>
</bean>
<bean id="CheckoutCheckinService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributes">
<props>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
</bean>
<bean id="CheckoutCheckinService_descriptor" parent="AlfrescoServiceDescriptor">
<property name="interface">
<value>org.alfresco.service.cmr.coci.CheckOutCheckInService</value>
</property>
<property name="description">
<value>Version Service</value>
</property>
</bean>
...
The security configuration
...
<!-- ============================== -->
<!-- The Check-out/Check-in service -->
<!-- ============================== -->
<!-- To check out a node requires that you have permission to check out the node and -->
<!-- create the working copy in the specified location. Check in requires the -->
<!-- the associated permission, as does cancel check out. See the permission model -->
<!-- for how these permissions are granted. -->
<bean id="CheckoutCheckinService_security" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
<property name="authenticationManager"><ref bean="authenticationManager"/></property>
<property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
<property name="afterInvocationManager"><ref local="afterInvocationManager"/></property>
<property name="objectDefinitionSource">
<value>
org.alfresco.service.cmr.coci.CheckOutCheckInService.checkout=ACL_NODE.0.cm:lockable.CheckOut,ACL_NODE.1.sys:base.CreateChildren
org.alfresco.service.cmr.coci.CheckOutCheckInService.checkin=ACL_NODE.0.cm:lockable.CheckIn
org.alfresco.service.cmr.coci.CheckOutCheckInService.cancelCheckout=ACL_NODE.0.cm:lockable.CancelCheckOut
</value>
</property>
</bean>
...
Implementing your own authentication mechanism
First, consider the following questions.
- Does your authentication system support single sign on?
If so, you will need to work out how this interacts with the web browser, and may be a portal container. On the client side, you will need to write and configure an Authentication Filter. The NTLM authentication filter provides a good example, as does the NovellIChainsHTTPRequestAuthenticationFilter. The latter can be used with SiteMainder and can be used to integrate with CAS. TODO: improve configuration support for this filter.
- Does your authentication API support recovering an MD4 password hash?
This is a requirement to integrate your authentication implementation with the NTLM authentication mechanism used by CIFS. If an MD4 password is not available you will not be able to use your authentication system to provide access to CIFS using NTLM authentication. CIFS can go direct to a Kerberos server. Configuration options for CIFS can be found:
- Does your authentication mechanism expose adding and deleting users?
If your authentication system is read only, you only need to implement the AuthenticationComponent interface and wire this implementation into the AuthenticationService. You will need the DefaultMutableAuthenticationDAO implementation, which does not allow any changes to authentication information.
- Do you manage groups (or roles)?
If yes, you will need to implement the AuthorityService interface to support groups, an provide support to get lists of all users and groups etc.
- Do you manage personal information?
If yes, you will need to implement a PersonService that creates personal information in the alfresco repository. You will probably want to configure how it creates default spaces etc. Approaches may be: to create People on demand, load all people on app start up, synchronise with a back end store as queries are made.
- Does your authentication mechanism support case sensitive user names?
You need to set the configuration in repository.properties and use this property to configure any services you implement to be sensitive to the case of user names.
- Are you integrating with an existing authentication API or storing authentication information yourself?
If the authentication mechamism exists, implement the AuthenticationComponent interface be extending AbstractAuthenticationComponent. If you allow user manipulation, also implement the MutableAuthenticationDAO. If you are storing the information yourself, consider using the default implementation, or use this as a model for constructing your own.
Unless you are reimplementing a full security framework (including permissions) you should only have to:
- implement an AuthenticationComponent by extending AbstractAuthenticationComponent and wire this into the AuthenticationService in the Spring configuration;
- implement a MutableAuthenticationDAO, or use the default one, and wire this into the AuthenticationService in the Spring configuration;
- implement a PersonService and configure it in Spring;
- implement an AuthorityService and configure it in Spring.
You should not have to implement:
- the OwnableService; or
- the PermissionService (unless you want another permissions model and permission dao)
The Admin password in the default authentication
Ths admin password for the default authentication is set as part of the initial bootstrap. This is located in config\alfresco\bootstrap\alfrescoUserStore.xml. The password is MD4 encoded as required by NTLM. The encoding is important.
How to generate the correct MD4 hash
The following class will allow the generation of the correct MD4 hash.
You will need the following jars:
- cryptix-jce-provider.jar
- commons-codec-1.2.jar
public class MD4HashGenerator
{
static
{
try
{
MessageDigest.getInstance("MD4");
}
catch (NoSuchAlgorithmException e)
{
Security.addProvider(new CryptixCrypto());
}
}
public MD4HashGenerator()
{
super();
}
/**
* @param args
*/
public static void main(String[] args)
{
System.out.println("Hash: " + new String(Hex.encodeHex(md4(args[0]))));
}
private static byte[] md4(String input)
{
try
{
MessageDigest digester = MessageDigest.getInstance("MD4");
return digester.digest(input.getBytes("UnicodeLittleUnmarked"));
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException(e.getMessage(), e);
}
catch (UnsupportedEncodingException e)
{
throw new RuntimeException(e.getMessage(), e);
}
}
}
How to reset the admin password
If you do not know the admin password it can be reset several ways.
- If you know the password of at least one user
- Give a known user admin rights in config\alfresco\authority-services-context.xml
- Login in as this user
- Reset the admin password
- Reset the config
- If you do not know the admin password
- Configure the authenticatoin component to accept all logins using org.alfresco.repo.security.authentication.SimpleAcceptOrRejectAllAuthenticationComponentImpl
- Login as anyone who has admin rights
- Reset the password
- Revert the configuratoin
For alfresco 1.3 and lower, set the password in the database using the MD4 hash, like this.
UPDATE node_properties
SET string_value = '<MD4 hash here>'
WHERE qname = '{http://www.alfresco.org/model/user/1.0}password'
AND guid = (
SELECT guid
FROM node_properties
WHERE qname = '{http://www.alfresco.org/model/user/1.0}username'
AND string_value = 'admin'
);
In alfresco 1.4 you will have to do this.
UPDATE alf_node_properties
SET string_value = '<MD4 hash here>'
WHERE qname = '{http://www.alfresco.org/model/user/1.0}password'
AND node_id in (
SELECT node_id
FROM alf_node_properties
WHERE qname = '{http://www.alfresco.org/model/user/1.0}username'
AND string_value = 'admin'
);
- Note the MD4 hash for password 'admin' is
209c6174da490caeb422f3fa5a7ae634
- Note the MD4 hash for password 'test' is
0cb6948805f797bf2a82807973b89537
Guest
Guest uses the lowercase userName "guest". If you delete the guest user you should be able to recreate them again with this user id and using the guest home as their home space. It does not matter what first name or surname etc they are given.
It will also not matter what password they are given. The bootstrap does not usually create a guest user, only the guest person is required.
Implementation of Security
ACEGI has been chosen as the framework for implementing authentication and authorisation. Familiarity with the ACEGI reference documentation is assumed.
Enterprise Network authentication configuration
Configuration instruction for the enterprise networks can be found at Enterprise Security and Authentication Configuration. This inlcudes JAAS, LDAP and Kerberos configuration.
To Do
Support authentication against multiple authentication services
Short Term (1.3)
Add the idea of domain - but not yet used
Medium Term (1.4)
Move Groups from the user store into the spaces store
- Groups of people not users
- Add attributes
- Add domain to users and people in the model
- gid
- name
- Add attributes
- Fix group service
- Patch to move groups and add default name
- Update LDAP sync of groups including group name
- Approx 3 days
Introduce the concept of domains.
Authentication Services
- Each authentication service applies to a domain
- There may be one or more authentication service in a domain.
- When there is more than one authentication service in a domain they are handled as the simple chaining case, as described above.
Domain
- Users, groups and people all apply to a domain.
- Assign permissions to groups and people
- Domain + id
- Node Ref (could assign permissions to the person and group noderefs)
- Patch
- Requires groups to be moved
- Tickets will have to identify a user and domain
Domain as a string
Both microsoft and unix do not allow "\" in user names. In Windows it is used as the domain name\user name separator. It seems reasonable that we can represent domain and user name in the same way. If there is no "\" in the name then there is no domain present implying that the default domain should be used. This significantly reduces the collateral damage of adding domain.
Impact of adding domains
- UI to manage groups, people and users
- LDAP import to specific domain
- UI - Login
- UI Permissions
- Domain+User as a string authority vs object - still should be a unique person or group
Approx. 4 days work
Also requires 0.5 days to add a username constraint to exclude "\" in user names.
Longer Term
- permissions and visibility of groups and users to other groups and users
- visibilty of people based on group membership. This would hace to be done in the service layer (not via the node service)
- Admin could be domain specific - repo and UI support
Mapping of external roles, groups and users
All usernames and authentication tokens will be mapped to a unique repository identifier. If external usernames or authentications are changed these can then be updated in the mapping and have no effect in information (e.g. permissions) stored in the repository.
Authorisation will be defined against these internal representations.
In most cases this will be an identity mapping unless the authentication already exists, its name has been changed or there is some reason to map many external roles into one internal role etc.
These translations will set up during authentication to support existing ACEGI code.
ACLs
Default Permissions
The default permissions model defines the following permissions.
sys:base
Base permissions
From 1.4, previously permission groups were not prefixed by _ and there were no simple permission groups so that more complex permission groups were easy to build.
For those with RBAC experience, you may think of stuff starting with _ as a permission and the rest as roles. However subjects (people, groups and dynamic subjects) can be assigned to either. The checks around service can also use any. So it is not traditional RBAC.
The underlying permissions are defined here. These are:
- _ReadProperties
- Restrict read access to the properties of a node. Access to content is controlled separately. All properties have the same access retrictions.
- _ReadChildren
- Restrict read access to children. The permissions set on individual children will be respected. If this permission is not granted you can not see any children when browsing. You could find children to which you have access during a search. To view a node there is no requirement that you can see its parent. This constraint can be added in the configuration.
- _WriteProperties
- Restrict write access to all properties of a node. Access to content is controlled separately. All properties have the same access retrictions.
- _ReadContent
- Restrict access to read all content for a node.
- _WriteContent
- Restrict access to create and update all content for a node.
- _ExecuteContent
- Restrict access to execute content.
- _DeleteNode
- Restrict acccess to delete a node. Currently there is no check that you have the right to delete all the children of the node or that you can delete children from parent. This support is available and is commented out in the configuration. Checking that all children can be deleted upfront is expensive.
- _DeleteChildren
- Restrict access to deleting children of a node (as opposed to removing secondary links to other nodes)
- _CreateChildren
- Restrict access to creating new child nodes (primary associations).
- _LinkChildren
- Restrict access to create secondary associations to already existing nodes.
- _DeleteAssociations
- Restrict access to delete non child associations for a node.
- _ReadAssociations
- Restrict access to read non child associations for a node.
- _CreateAssociations
- Retrict access to create non child associations for a node.
- _ReadPermissions
- Restrict access to read permissions on a node.
- _ChangePermissions
- Restrict access to write permissions on a node.
Simple groups
These are grouped into simple permission groups, which themselves can be grouped into more complex groups (see cm:object)
Simple groups are normally used to control the access to methods on public services. This is clear as they translate by convention to a single permission.
- FullControl
- A permission group that allows all other permissions.
- ReadProperties
- Granted from _ReadProperties.
- ReadChildren
- Granted from _ReadChildren.
- WriteProperties
- Granted from _WriteProperties.
- ReadContent
- Granted from _ReadContent.
- WriteContent
- Granted from _WriteContent.
- ExecuteContent
- Granted from _ExecuteContent.
- DeleteNode
- Granted from _DeleteNode.
- DeleteChildren
- Granted from _DeleteChildren.
- CreateChildren
- Granted from _CreateChildren.
- LinkChildren
- Granted from _LinkChildren.
- DeleteAssociations
- Granted from _DeleteAssociations.
- ReadAssociations
- Granted from _ReadAssociations.
- CreateAssociations
- Granted from _CreateAssociations.
- ReadPermissions
- Granted from _ReadPermissions.
- ChangePermissions
- Granted from _ChangePermissions.
Complex Groups
There are some common groupings of permissions for CRUD operations on nodes.
- Read
- Combines ReadProperties, ReadChildren, and ReadContent.
- Write (Update in CRUD)
- Combines WriteProperties and WriteContent.
- Delete
- Combines DeleteNode and DeleteChildren.
- AddChildren (Create in CRUD)
- Combines CreateChildren and LinkChildren. (Link is not strictly part of create in the CRUD sense)
- Execute
- Just ExecuteContent.
cm:object
Complex Groups
- Administrator
- Has all permissions. For backward compatibilty.
- Coordinator
- The coordinator gets all permissions and permission groups defined.
- Collaborator
- Combines Editor and Contributor permission groups.
- Contributor
- Includes the Consumer permission group and adds AddChildren and CheckOut.
- They will, by default own anything they create and have the ROLE_OWNER authority.
- Editor
- Include the Consumer permission group and adds Write and CheckOut.
- Consumer
- Includes Read
- RecordAdministrator
- Includes ReadProperties, ReadChildren, WriteProperties, ReadContent, DeleteChildren, CreateChildren, LinkChildren, DeleteAssociations and CreateAssociations.
cm:folder
Complex Groups
As cm:object but with no Administrator permission group.
cm:ownable
Base Permissions
- _SetOwner
- Restrict setting permisisons on a node. This permissions also requires that _WriteProperties is also granted. It is not implied by having _WriteProperties.
Simple Permission Groups
- SetOwner
- Granted from ;_SetOwner
Complex Permission Groups
- TakeOwnership
- Includes SetOwner.
cm:lockable
Base Permissions
- _Lock
- Restrict access to lock a node.
- _Unlock
- Restrict access to unlock a node.
Simple Permission Groups
- Lock
- Granted from _Lock
- Unlock
- Granted from _Unlock
Complex Permission Groups
- CheckOut
- Includes Lock.
- CheckIn
- Includes Unlock.
- CancelCheckOut
- Includes Unlock.
Global permissions
These apply to all nodes regardless of where they are in the repository.
- "FullControl" granted to "ROLE_ADMINISTRATOR"
- Admin users can do anything
- "FullControl" granted to "ROLE_OWNER"
- The owner (as defined by the owable apspect, or, if the aspect is not present the node creator) is allowed all rights. This interacts with contributor for cm:content. They only need the right to create content in the default set up; all other rights come from the fact that they own the nodes they create.
- "Unlock" granted to "ROLE_LOCK_OWNER"
- The owner of a lock can always release the lock.
- "CheckIn" granted to "ROLE_LOCK_OWNER"
- The owner of a lock can checkin a document - underneath the cover it holds a lock.
- "CancelCheckOut" granted to "ROLE_LOCK_OWNER"
- The owner of a lock can cancel a check out of a document - underneath the cover it holds a lock.

