Obsolete Pages{{Obsolete}}
The official documentation is at: http://docs.alfresco.com
Any discussion of security addresses two issues – Authentication and Authorization. Authentication asks the question is whether a user is valid and should be allowed to use the system. Authorization then allows to specify what an authenticated user can do. An authenticated user may have no permissions granted to him.
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.
Authentication is managed by the core repository. Since the repository has many interfaces, this means that uthentication is carried out/required at all entry points to repository:
Authentication can be by an Alfresco ticket, a user name / password pair, or some other mechanism.
Given a ticket or username/password, a number of authentication authorities may be tried for authentication. The user needs to be authenticated by at least one authority,
Single sign on from the browser and via CIFS involves a multi-stage authentication process and may include negotiation of the authentication mechanism. While authentication chaining is supported in other modes, there is no authentication chaining for single sign on.
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 Please see below.
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 configuration 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 includes the following functionality:
Please see Security Services for more information.
The person service knows nothing of home folder creation and providers.
(This behaviour is bound to a create policy for the cmerson 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:
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>
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:
<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).
The BootstrapHomeFolderProvider class is present to support specifying home folders.
You will probably not have to use this.
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'>
<value>companyHomeFolderProvider</value>
</entry>
</map>
</property>
...
Insert non-formatted text here
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.
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'/>
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.
<permissionSet type='cm:ownable' expose='selected'>
<permissionGroup name='TakeOwnership' requiresType='false' expose='false'/>
<permission name='SetOwner' expose='false' requiresType='false'>
<grantedToGroup permissionGroup='TakeOwnership' />
<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 'TakeOnwership' 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.
This can be found in public-services-context.xml.
<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>
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' />
Now would be a good time to look at the rest of the permission model config to see what it does.
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 ower of a lock
to check in, unlock, and cancel a check out.
...
<permissionSet type='cm:lockable' expose='selected'>
<permissionGroup name='CheckOut' requiresType='false' expose='false'/>
<permissionGroup name='CheckIn' requiresType='true' expose='false'/>
<permissionGroup name='CancelCheckOut' requiresType='true' expose='false'/>
<permission name='Lock' requiresType='false' expose='false'>
<grantedToGroup permissionGroup='CheckOut' />
<requiredPermission on='node' type='sys:base' name='Write'/>
</permission>
<permission name='Unlock' requiresType='true' expose='false'>
<grantedToGroup permissionGroup='CheckIn' />
<grantedToGroup permissionGroup='CancelCheckOut' />
</permission>
</permissionSet>
...
...
<globalPermission permission='Unlock' authority='ROLE_LOCK_OWNER'/>
<globalPermission permission='CheckIn' authority='ROLE_LOCK_OWNER'/>
<globalPermission permission='CancelCheckOut' authority='ROLE_LOCK_OWNER'/>
...
....
<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>
...
...
<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>
...
First, consider the following questions.
Unless you are reimplementing a full security framework (including permissions) you should only have to:
You should not have to implement:
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.
The following class will allow the generation of the correct MD4 hash.
You will need the following jars:
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);
}
}
}
If you do not know the admin password it can be reset several ways.
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'
);
209c6174da490caeb422f3fa5a7ae634
0cb6948805f797bf2a82807973b89537
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.
ACEGI has been chosen as the framework for implementing authentication and authorisation.
Familiarity with the ACEGI reference documentation is assumed.
Configuration instruction for the enterprise networks can be found at Enterprise Security and Authentication Configuration. This inlcudes JAAS, LDAP and Kerberos configuration.
Add the idea of domain - but not yet used
Move Groups from the user store into the spaces store
Introduce the concept of domains.
Authentication Services
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
Approx. 4 days work
Also requires 0.5 days to add a username constraint to exclude '\' in user names.
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.