Security and Authentication

From alfrescowiki

Jump to: navigation, search

Contents

Authentication

Authentication is carried out/required at several entry points to the repository, for example:

  • Web Scripts
  • CMIS
  • CIFS
  • FTP
  • WebDAV
  • Web Client
  • Web Services (deprecated)
  • 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

ServerAuthArch.gif


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 public 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 precedence 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.


PermissionsSimpleExample.gif


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 precedence 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 precedence 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:

  1. the Authentication Service;
  2. the Authority Service;
  3. the Ownable Service;
  4. the Permission Service; and
  5. 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 the 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.


The authentiation service brings together three components:

  • the authentication component;
  • the authentication DAO; and
  • the ticket component.

The authentication component supports authentication only. The authentication DAO provides an API to create, delete and update authentication information. The ticket component is resposible for managing and storing tickets that may be obtained after authentication and used in place of authentication.

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.

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.

NTLM, LDAP, JAAS, Kerberos and Chaining

Alternative implementations of the authentication service and its supporting components for various flavours of external authentication systems, including NTLM, LDAP, JAAS, and Kerberos, have been included in the preconfigured Authentication Subsystems. Multiple instance of these can be 'chained' together to support more advanced enterprise-level authentication requirements. See Alfresco Authentication Subsystems for more information.

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.

Default Node-based Implementation

The default authority service implementation uses Alfresco Repository nodes to store authority metadata. These nodes are under the path /sys:system/sys:authorities in the spaces store.

Note: whilst these nodes under /sys:system/sys:authorities are accessible through other Alfresco APIs, e.g. Javascript and REST APIs, they should be treated as read only and should not be modified through any other APIs other than the authority service. Otherwise authority information may be lost on an upgrade.

Configuration

A common configuration requirement is to change the set of user names that the authority service automatically recognizes as administrator authorities. For example, if using pass-through authentication with a Windows domain server, you might want to add "administrator" to this set of names.

This is now all configurable through the authentication subsystem without any need to edit spring configuration. See Alfresco Authentication Subsystems for more details.

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 contentModel.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.

  1. Find the best match, use it; leave all entries as they are
  2. Find the best match and fix the other uids to be unique by appending a guid
  3. 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

Since 1.4, a personal home space (or folder) may be created for each person. Prior to 1.4 their home spaces would be a shared Company Home Space. It is still possible to share home spaces but this is no longer the default.

When a person's home space is created, the person property cm:homeFolderProvider (the name of the provider) is used by the HomeFolderManager to obtain the provider. The provider locates or creates the home folder, but normally does this by specifying the location and calling a method on the HomeFolderManager. The location of the home folder is saved in the person property cm:homeFolder.

Custom providers may be created however there are a number of predefined providers:

userHomesHomeFolderProvider
the default provider which specifies that each user be given their own home folder (the name is based on their user name) directly under a root path (which by default is app:company_home/app:user_homes);
largeHomeFolderProvider
the default provider for users created via an LDAP import. It is intended for the case there there are thousands of users. In this situation having all the home folders under a single root path, will cause performance problems. Out of the box this provider is identical to userHomesHomeFolderProvider, however by setting a few Alfresco global properties, it will organise home folders into a nested structure of parent folders;
companyHomeFolderProvider
users share the Company Home Space, as they did prior to 1.4;
bootstrapHomeFolderProvider
for use by admin and guest users only;
guestHomeFolderProvider
no longer in use.

The creation of the home folder may be delayed until first used. The PersonService has a homeFolderCreationEager property to indicate if the home folder should be created when the person object is created. The HomeFolderManager has a defaultProvider property that specifies the default provider to be used if there is no homeFolderProvider property on the person, making this pluggable (see the bean in authentication-services-context.xml). The default provider for LDAP import is defined in common-ldap-context.xml, but the cm:homeFolderProvider property may also be set from an attribute stored in LDAP.

Some interfaces, such as Alfresco Explorer create the home folder themselves and set the cm:homeFolder property when creating the person. As a result the userHomesHomeFolderProvider discovers the folder already exists so does not try to create it, but will still set permissions.

Home Folder Provider Properties

The HomeFolderProvider2 interface providers methods to obtain a number of properties that define where the home folder should be located or created.

name
The bean name of the home folder provider. Set cm:homeSpaceProvider on cm:person to this value to select the use of a particular provider.
storeUrl
The store URL (used for search context etc. to look up paths). The home folder will exist in the store.
rootPath (was path)
The root path under which all home folders will be placed either directly or indirectly. In the case of companyHomeFolderProvider this is the path to the company home space. For userHomesHomeFolderProvider this is the parent directory under which all user home spaces are created. For largeHomeFolderProvider this is the root path under which all user home spaces will be placed either directly or indirectly.
Prior to 4.0, this property was called "path", but was changed to rootPath to better reflect its meaning and to avoid confusion with the homeFolderPath property.
homeFolderPath
A relative path from the rootPath to the home Folder.
templateNodeRef
A node to copy (a template) for the home folder. Ignored if null and only used if the home folder does not already exist.
owner
The name of the owner of the home folder on creation. If not specified then the person will own their home space.
onCreatePermissionsManager
The permissions manager to set permissions on the home folder when it is created.
onReferencePermissionsManager
The permissions manager to set permissions on the home folder when it already exists.
homeFolderManager
Although not part of the interface, implementations will need the homeFolderManager injected in order to register themselves.

HomeFolderProvider2 and HomeFolderProvider

Prior to 4.0, home folder providers implemented HomeFolderProvider which simply has OnCreateNodePolicy and getName methods. This has now been deprecated in favour of HomeFolderProvider2 which has methods to obtain properties common to most providers. This allows much of the code in the providers to be moved into the HomeFolderManger. Custom providers that implement the deprecated HomeFolderProvider interface still work, but are limited when it comes to moving home folders and ongoing support as the interface will be removed at some point in the future. Custom providers that extend the now deprecated AbstractHomeFolderProvider will fully support moving home folders as the abstract class has been modified to use an adaptor between the two interfaces.

The largeHomeFolderProvider

The largeHomeFolderProvider is needed to split home folders into groups so they don't all exist under the same rootPath. Having thousands of folders under one folder will cause performance problems. This provider may be configured to introduce zero or more parent folders above the actual home folder. For example we might wish to group users if they share the same two characters of their user name:

  al/alan
  al/alfred   
  ja/jane

The largeHomeFolderProvider uses the RegexHomeFolderProvider class. It is an implementation that returns a tree structure for a home folder based on a property (typically userName) from the supplied person. The parent folder names are derived from regular expression groups matched against the property value. The final folder name is the full property value. The above example could be achieved using the regular expression "^(..)". With the regular expression "^(.)(.?)" the home folder path would be

  a/l/alan
  a/l/alfred
  j/a/jane

If any group matches a zero length string, it is just ignored. The largeHomeFolderProvider has the following additional properties, which are set from Alfresco global properties (in brackets), so are easily customised.

propertyName (spaces.user_homes.regex.key)
Which person property to use (default value is "userName").
pattern (spaces.user_homes.regex.pattern)
The regular expression to be used (default is "" - no groups so no parents, so it is identical to userHomesHomeFolderProvider. A good value to set would be "^(..)".
groupOrder (spaces.user_homes.regex.group_order)
The order in which the regular expression groups should be used (for example, "2,1"). The default is the order in which they appear.

Moving home folders

As of 4.0, it is possible to move home folders to their preferred provider location or even override the provider to be used. This is achieved by setting a number of Alfresco global properties and restarting Alfresco. The home folders are moved during the start-up process which may take some time. The global properties should be cleared once Alfresco is up, to avoid repeating the process the next time Alfresco starts. This action may be required if a large number of users have been created via an LDAP import directly under the rootPath or if a new tree structure for home folders has changed and there are user that use a previous structure.

The home folders of internal users (such as admin and guest) are not moved, nor are users that share home folders (the rootPath of the provider).

It is also possible change the HomeFolderProvider used by all other users by setting the global property home_folder_provider_synchronizer.override_provider=<providerBeanName>.

By default no action is taken unless the the global property home_folder_provider_synchronizer.enabled=true

Warning: The LDAP synchronise process overwrites the home folder provider property. This is not an issue as long as the rootPath of the overwriting provider is the same as the overwritten provider or is not an ancestor of any of the existing home folders. This is important because the rootPath value (or the original provider) is used to tidy up empty 'parent' folders under the rootPath when a home folders are moved elsewhere. If you have any concerns that this may not be true, set the global property home_folder_provider_synchronizer.keep_empty_parents=true and tidy up any empty folders manually. Historically users created by the LDAP sync process are all placed under the same root folder so there will be no parent folders anyway.

The following global properties:

  • configure the largeHomeFolderProvider it to place home folders within a parent folder using the first two characters of the username;
  • run the home folder provider synchronizer on start up (must be turned off again) to move existing users home folders;
  • informs the home folder provider synchronizer to switch existing users over to using the largeHomeFolderProvider.
  spaces.user_homes.regex.pattern=^(..)
  home_folder_provider_synchronizer.enabled=true
  home_folder_provider_synchronizer.override_provider=largeHomeFolderProvider 

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 returned 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-context.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 HTTPRequestAuthenticationFilter. The latter can be used with SiteMinder and can be used to integrate with CAS.
  • 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 if this file has a "adminUsers" section. If not, in file alfresco-global.properties add a line like: alfresco_user_store.adminusername=nicolas
    • Restart the repository
    • Login in as this user
    • Reset the "admin" user's password
    • Reset the config
  • If you do not know the admin password
    • Configure the authentication component to accept all logins using org.alfresco.repo.security.authentication.SimpleAcceptOrRejectAllAuthenticationComponentImpl
    • Login as anyone who has admin rights
    • Reset the "admin" users's password
    • Revert the configuration

Note: in the text below when reading MD4 hash of a password you need to understand MD4 hash of the UTF16LE encoded password (i.e. the NTLM hash). Here is a python sample code:

import hashlib
passwd = 'admin'
# NTLM (as in alfresco)
print hashlib.new('md4', passwd.encode('utf-16le')).hexdigest()
# 209c6174da490caeb422f3fa5a7ae634

# simple hash
print hashlib.new('md4', passwd).hexdigest()
# f9d4049dd6a4dc35d40e5265954b2a46

Alfresco Version 3.1sp1 and later

For an Alfresco 3.1sp1 or later system, use the following instructions:

1) Run the following commands and check you have only one row in the output:

 SELECT anp1.node_id,
  anp1.qname_id,       
  anp1.string_value       
  FROM alf_node_properties anp1  
  INNER JOIN alf_qname aq1 ON aq1.id = anp1.qname_id                          
  INNER JOIN alf_node_properties anp2 ON anp2.node_id = anp1.node_id       
  INNER JOIN alf_qname aq2 ON aq2.id = anp2.qname_id                          
  WHERE aq1.local_name = 'password'    
  AND aq2.local_name = 'username'       
  AND anp2.string_value = 'admin'   
 

It should output the current md4 hashed password for the 'admin' user. An example output is:

+---------+----------+----------------------------------+
| node_id | qname_id | string_value |                     
+---------+----------+----------------------------------+
| 4 | 10 | 209c6174da490caeb422f3fa5a7ae634 |                
+---------+----------+----------------------------------+
1 row in set (0.00 sec)

2) If you get only one row, then update the table:

 UPDATE alf_node_properties  
 SET string_value='209c6174da490caeb422f3fa5a7ae634'
 WHERE  
 node_id=THENODEIDABOVE
 and
 qname_id=THEQNAMEVALUEABOVE
 

Where you need to replace THENODEIDABOVE and THEQNAMEVALUEABOVE with the results from step 1), in this example 4 and 10 respectively.

Note: Please use caution when running this SQL, and ensure that you have the appropriate AND conditions in the UPDATE query.

3) Restart Alfresco.

Note: The hashed password you use in your UPDATE statement MUST be in all lower case. If you use a hash tool that returns a string with uppercase letters (as many internet NTLM hashing tools do), change them all to lowercase.

Alfresco Version 1.4

For Alfresco 1.4, use the following instructions:

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'  
);
  1. Note the MD4 hash for password 'admin' is
    209c6174da490caeb422f3fa5a7ae634
  2. Note the MD4 hash for password 'test' is
    0cb6948805f797bf2a82807973b89537


Alfresco Version 1.3

For Alfresco version 1.3, use the following instructions:

You can use this to get the hash for current admin password:

select string_value  
  from alf_node_properties p, alf_namespace ns, alf_qname q
 where p.qname_id = q.id
  and q.ns_id = ns.id  
  and ns.uri = 'http://www.alfresco.org/model/user/1.0'    
  and q.local_name = 'password'    
  AND node_id in (  
  SELECT node_id  
  FROM alf_node_properties p, alf_namespace ns, alf_qname q  
  WHERE p.qname_id = q.id  
  and q.ns_id = ns.id    
  and ns.uri = 'http://www.alfresco.org/model/user/1.0'    
  and q.local_name = 'username'    
  AND string_value = 'admin'    
);

And, you can use this to set the password:

UPDATE alf_node_properties p
SET string_value = '<MD4 hash here>'
WHERE p.qname_id = (select q.id from alf_qname q, alf_namespace ns  
  where q.ns_id = ns.id                    
  and ns.uri = 'http://www.alfresco.org/model/user/1.0'                      
  and q.local_name = 'password')                      
AND node_id in (
  SELECT node_id  
  FROM alf_node_properties p, alf_namespace ns, alf_qname q  
  WHERE p.qname_id = q.id  
  and q.ns_id = ns.id    
  and ns.uri = 'http://www.alfresco.org/model/user/1.0'    
  and q.local_name = 'username'    
  AND string_value = 'admin'    
);


Alfresco versions lower than 1.3

For Alfresco versions lower than 1.3, set the password in the database using the MD4 hash. For example:

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'   
);

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 Alfresco Authentication Subsystems. This inlcudes JAAS, LDAP and Kerberos configuration.



To Do

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

Access Control Lists

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 ownable aspect, 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.

Notes on permission evaluation

Admin does not bypass permission evaluation. Permission evaluation is however faster for admin as the admin rights are assigned via a global permission. The node context is not required nor is it required to get the node ACL. The global permission assignments are kept in memory.
"admin" permission evaluation after a query will use the acl.maxPermissionCheckTimeMillis property. However, the evaluation will be faster.
The search parameters object allows you to set the time outs and max permission evaluations
If you use non-public service then there will be no permission evaluations for any user
The public services are the boundary across which security is enforced - going below this boundary direct will by-pass security
SearchService - enforces security
searchService - does not enforce security
In code you can use AuthenticationUtil.runAs() to run code with the rights of another user
there are no controls as to who can get elevated rights - there is in fact nothing to stop you doing what you like in server side code
The "system" user is the only user that bypasses all security evaluation
you can use AuthenticationUtil.runAs() with "admin" and "system" or any other user.
Personal tools
© 2014 Alfresco Software, Inc. All Rights Reserved. Legal | Privacy | Accessibility