Close

Setting up authentication and security

Use this information to manage authentication in Alfresco Content Services, and to configure keystores for repository encryption.

The first time you access a clean Alfresco Content Services installation, you can identify yourself by entering a new user name and password in the Sign in screen. If you sign in with the credentials of a user with administrator privileges, you can create additional users and assign them passwords. In this out-of-the-box set up, you can manage the user base and their passwords manually.

Note: Choose a strong, unique password for your admin account, and consider changing it regularly.

From here, there are a number of common customizations you might want to make to scale up to the needs of a larger enterprise. For example, you might want to:

  • Enable automatic sign-on using operating system credentials or a Single Sign-On (SSO) server to remove the need for a Sign in screen
  • Delegate authentication responsibility to a central directory server to remove the need to set up users manually
  • Protect your repository and Solr communications using encryption

  • Alfresco Content Services security
    Alfresco Content Services security comprises a combination of authentication and authorization.
  • Mitigating brute force attack on user passwords
    Alfresco Content Services 5.2.7 provides basic out-of-the-box protection against brute force attacks on password logins.
  • Managing Alfresco keystores
    The out-of-the-box Alfresco Content Services installation has a pre-configured main keystore, which contains a secret key generated by Alfresco Content Services. If you want to use encrypted properties, you should create your own keystore with your own password, and update the metadata file appropriately.
  • Cryptographic password hashing
    Alfresco Content Services uses cryptographic password hashing technique to securely store passwords.
  • Encrypting properties
    The alfresco-global.properties file (and other subsystem properties file) holds configuration properties that contain sensitive information or passwords, such as db.password. All the properties that can be specified in the alfresco-global.properties file can be encrypted.
  • Authentication subsystems
    Authentication is one of the categories of the Alfresco Content Services subsystem. An authentication subsystem is a coordinated stack of compatible components responsible for providing authentication and identity-related functionality to Alfresco Content Services.
  • Authentication chain
    The authentication subsystem types allow you to integrate Alfresco Content Services with the authentication servers in your environment. However, if integrating with only one of these systems is not sufficient, you might want to combine multiple authentication protocols against a collection of servers.
  • Configuring authentication subsystems
    A number of examples demonstrate how to express various authentication configuration requirements in subsystem instances in the authentication chain. They also explain how the authentication chain integrates the functions of multiple subsystem instances into a more powerful conglomerate, letting you cater for even the most complex authentication scenarios.
  • Configuring synchronization
    The synchronization subsystem manages the synchronization of Alfresco Content Services with all the user registries (LDAP servers) in the authentication chain.
  • Managing authentication directories
    Use Directory Management in the Admin Console to set up authentication chains, and configure external SSO, CIFS and FTP authentication. The Directory Management feature gives you the ability to configure and test connections to various directory services. 
  • Authorities
    Authorities are people (or persons) or groups.
  • Defining permissions
    Permissions and their groupings are defined in an XML configuration file.
  • Access Control Lists
    An Access Control List (ACL) is an ordered list of one or more Access Control Entries (ACE). An ACE associates a single authority to a single permission group or permission, and states whether the permission is to be allowed or denied. All nodes have an associated ACL.
  • Modifying access control
    Modifying access control can involve changing definitions, adding services, defining types and aspects, or adding definitions to new or existing security interceptors.
  • Public services
    Security is enforced around public services. Web services, web scripts, Alfresco Share, CIFS, WebDAV, FTP, CMIS, and more, all use public services, and therefore include security enforcement.
  • Implementation and services
    Alfresco Content Services enforces security services for managing authentication information.
  • Admin password in default authentication
    The Admin user password is used by the default authentication system.
  • Security policies and filters
    You can configure a number of policies and filters in Alfresco Share to mitigate security attacks. You can also configure filters in Alfresco Repository to mitigate security attacks when the Alfresco Content Services ReST API is accessed externally.

Alfresco Content Services security

Alfresco Content Services security comprises a combination of authentication and authorization.

Authentication is about validating that a user or principal is who or what they claim to be. A user’s credentials can take many forms and can be validated in a number ways. For example, a password validated against an LDAP directory, or a Kerberos ticket validated against a Microsoft Active Directory Server.

Alfresco Content Services includes:

  • An internal, password-based, authentication implementation
  • Support to integrate with many external authentication environments
  • The option to write your own authentication integration and to use several of these options simultaneously

Alfresco Content Services can integrate with LDAP, Microsoft Active Directory Server, the Java Authentication and Authorization Service (JAAS), Kerberos, and NTLM. A user ID can also be presented as an HTML attribute over HTTPS to integrate with web-based single-sign-on solutions.

Authorization determines what operations an authenticated user is allowed to perform. There are many authorization models. Popular ones include: Role Based Access Control (RBAC), UNIX-style Access Control Lists (ACLs) and extended ACLs, Windows-style ACLs, and many more. Authorization requirements for the management of records are more detailed and include additional requirements, for example, enforcing access based on security clearance or record state.

Authorization is based on UNIX-extended ACLs. Each node in the repository has an ACL that is used to assign permissions to users and groups. Operations, such as creating a new node, describe what permissions are required to carry out the operation. ACLs are then used to determine if a given user can execute the operation based on the permissions that have been assigned directly to the user or indirectly through a group. An operation is invoking a method on a public service bean. For example, creating a user’s home folder requires invoking methods on several public services; to create the folder, set permissions, disable permission inheritance, and so on. Each public service method invocation will check that the user is allowed to execute the method.

By convention, public service beans are the beans whose names start with capital letters, such as the NodeService. You configure the security requirements for public service beans in XML. A given method on a particular service might be available to all users, all users in a specified group, all users with a specified role, or users who have particular permissions on specified arguments to the method or its return value. In addition, for methods that return collections or arrays, their content can be filtered based on user permissions. If the authorization requirements for a method call are not met, the method call will fail and it will throw an AccessDeniedException. Non-public beans, such as nodeService, do not enforce security; use these only when the enforcement of authorization is not required.

Permission assignments are made in Access Control Lists (ACLs), which are lists of Access Control Entries (ACEs). An ACE associates an authority (group or user) with a permission or set of permissions, and defines whether the permission is denied or allowed for the authority. Every node has a related ACL. When you create a node, it automatically inherits an ACL from its parent. You can alter this behavior after node creation by breaking inheritance or modifying the ACL.

The XML configuration for permissions also defines a context-free ACL for ACEs that apply to all nodes. For example, you could use this to assign everyone Read access to all nodes regardless of what individual ACLs any node has set. (See the Permissions section in this chapter for more details on how to modify the permission model.)

<!-- Extension to alfresco\model\permissionDefinitions.xml -->
<globalPermission permission="Read" authority="GROUP_EVERYONE" />

A check that a user has Read permission for a node is done in two stages. First, the context-free ACL is checked to see if it allows access. If not, the ACL assigned or inherited by the node is checked. A user might be allowed to perform an operation because of permissions assigned to the context-free ACL, assigned to the node’s ACL, inherited by the node from its parent, or a combination of all three.

Mitigating brute force attack on user passwords

Alfresco Content Services 5.2.7 provides basic out-of-the-box protection against brute force attacks on password logins.

To mitigate brute force attacks on user passwords, after a few failed login attempts for any given user id, the user id is locked out and marked as protected. The user id stays in the protected mode for a six seconds protection period. During this time, even if the correct login details are specified, the user can’t login. After the six seconds protection period is over, the user can login with the correct login details.

To summarize, once a user id is protected:

  • the schedule causes a six seconds delay between the allowed login attempts.
  • authentication requests occurring more frequently than the permitted schedule are denied.
  • next login attempt that is denied due to the rate limiting algorithm generates a WARN message in the Alfresco log file (only once).
  • for every consecutive failed login attempt, if the limit exceeds 10 attempts, a WARN message is shown in the Alfresco log file (only once).
  • details about authentication attempts, including the number of login attempts and time stamp of last login attempt, are cached.

The administrator receives one log message per protection period. To avoid username disclosure in logs, the message displays only the first two letters of the username.

The user id stays as protected until a correct authentication request is processed after the six seconds protection period. The entry is then removed from the cache.

This login protection feature is enabled by default, and can be configured by adding the following properties to the alfresco-global.properties file.

Property Description Default Value
authentication.protection.enabled Specifies if the login protection feature is enabled or disabled. true
authentication.protection.limit Specifies the number of attempts after which the user id becomes protected. 10
authentication.protection.periodSeconds Specifies the protection period after which a valid login attempt can be done. 6

Note: This feature provides some basic protections against brute force attacks by slowing down repeated logins, but it does not replace more advanced brute force attack detection and mitigation that would be done on the network level or through log analysis.

Managing Alfresco keystores

The out-of-the-box Alfresco Content Services installation has a pre-configured main keystore, which contains a secret key generated by Alfresco Content Services. If you want to use encrypted properties, you should create your own keystore with your own password, and update the metadata file appropriately.

The default keystore configuration protects the keys by using two levels of passwords - a keystore password and a password for each key. Currently, the keystore contains only a metadata secret key that is used for encrypting and decrypting node properties that are of type d:encrypted.

You can also configure a backup keystore. This is useful in case the keys need to be changed. The user can back up the main keystore to the backup keystore location and create a new keystore in its place.

If both the main and backup keystores are configured, the repository encryption works in the fallback mode. In this mode, the node properties are decrypted with the main keystore’s metadata key first. If that fails, the backup keystore’s metadata key is tried. This allows the keystores to be changed on the disk and reloaded without affecting the running of the repository.

Keystores are used also to protect repository/Solr communications using encryption and mutual authentication. In this case, the keystores store RSA keys and certificates. For more information, see Solr security.

Keystore configuration

You can configure the main and backup keystores using the alfresco-global.properties file.

To configure the main keystore, set the following properties in the alfresco-global.properties file:

Property Description with example
encryption.keystore.location Specifies the location of the main keystore.encryption.keystore.location=${dir.keystore}/keystore
   
encryption.keystore.keyMetaData.location Specifies the location of the main keystore’s metadata file.encryption.keystore.keyMetaData.location=${dir.keystore}
   
encryption.keystore.provider Specifies the main keystore provider.
encryption.keystore.type Specifies the main keystore type.encryption.keystore.type=JCEKS
   
encryption.keystore.backup.location Specifies the location of the backup keystore.encryption.keystore.backup.location=${dir.keystore}/backup-keystore
   
encryption.keystore.backup.keyMetaData.location Specifies the location of the backup keystore’s metadata file.encryption.keystore.backup.keyMetaData.location=${dir.keystore}
   
encryption.keystore.backup.provider Specifies the backup keystore provider.
encryption.keystore.backup.type Specifies the backup keystore type. encryption.keystore.backup.type=JCEKS

Some other general encryption properties are:

encryption.keySpec.class=org.alfresco.encryption.DESEDEKeyGenerator
encryption.keyAlgorithm=DESede
encryption.cipherAlgorithm=DESede/CBC/PKCS5Padding

Because of these encryption properties, the keystores and metadata files can be easily located. Also, the metadata file uses a clear text password to access the keystore. For this reason, appropriate operating system permissions should be applied so that the files cannot be accidentally changed nor read by anyone other than an administrator and the username running the repository.

Each keystore must have a corresponding keystore metadata file. This file contains the passwords, its keys, and other metadata relevant to the keystore. The metadata file must contain three entries:

  • aliases=
  • keystore.password=
  • metadata.password=

At bootstrap, the repository checks if the metadata key in the main keystore has been changed (unless running in the fallback mode, in which case the backup keystore is checked instead). This prevents accidental changes to the keystore. If it detects that the metadata key has been changed, an exception will occur and the bootstrap will stop.

Keystore generation

Keystore generation can be automatic or manual.

Automatic keystore generation

During bootstrap, if the repository detects a missing secret key keystore, it will dynamically create a keystore containing a single metadata secret key. In order to do this, the repository assumes the existence of a keystore metadata file containing information about the metadata key. Specifically, it expects the following properties to be set:

Property Description
metadata.keyData Specifies the key data used to generate the secret key.
metadata.algorithm Specifies the key algorithm used to generate the secret key.Each Java environment may support a different set of algorithms. For the list of algorithm names that can be specified, see SecretKeyFactory Algorithms.

For keytool defaults specific to the secret key generation, see the Oracle documentation - keytool.

The keyData can be generated by executing the class org.alfresco.encryption.GenerateSecretKey as shown below:

java -classpath "projects/3rd-party/lib/commons/commons-codec-1.4.jar:projects/core/build/dist/alfresco-core-4.0.a.jar"
 org.alfresco.encryption.GenerateSecretKey

Manual keystore generation

A new keystore can be generated using the Java keytool command as shown below:

keytool -genseckey -alias metadata -keypass <metadata key password> -storepass <key store password> -keystore keystore
 -storetype JCEKS -keyalg DESede

Note: Make sure the keystore is placed in the location specified by the property encryption.keystore.location and that the passwords you have used in the keytool commands are placed in the file specified by the property encryption.keystore.keyMetaData.location.

Keystore key registration

The keystore keys are registered with the repository to ensure that they are not accidentally changed.

During bootstrap and JMX keystore reload and re-encryption operations, the repository checks if the main keystore’s keys and the metadata key have changed. If they have changed, the repository throws an exception.

Encrypted node properties

Data encryption in Alfresco Content Services uses secret keys which are stored in the Java keystore.

Note: Encrypted node properties will be deprecated and no longer available from Alfresco Content Services 6.0 and above.

Alfresco provides a type d:encrypted that can be used to store sensitive property values in the database in a sealed encrypted format.

Using encrypted node properties

Node properties can be encrypted in the repository by setting their type to d:encrypted in the model.

By default, the node service will not automatically encrypt and decrypt these properties as they pass in and out of the node service. Encryption and decryption should be handled by the trusted custom code (that uses the metadataEncryptor) running in the repository. Clients, such as Alfresco Share will not automatically be able to decrypt and display encrypted property values.

Note: The encrypted node properties will not available in Share without the code.

The org.alfresco.repo.node.encryption.MetadataEncryptor class (defined as the Spring bean with name metadataEncryptor) provides an interface to encrypt and decrypt encryptable properties. The repository’s node integrity checking will ensure that encryptable properties are actually encrypted (by the MetaDataEncryptor) when the transaction commits. If they are not encrypted, an integrity violation exception is raised.

For example, given the model:

<model name="test:encryptedPropModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
   <description>Alfresco Content Model</description>
   <author>Alfresco</author>
   <published>2005-05-30</published>
   <version>1.0</version>
  
   <imports>
       <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
       <import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
   </imports>
  
   <namespaces>
       <namespace uri="http://www.alfresco.org/test/encryptedPropModel/1.0" prefix="test"/>
   </namespaces>
  
   <constraints>
   </constraints>
  
........................................................................................................
 
   <types>
     
      <type name="test:encrypted">
         <title>Encrypted</title>
         <description>The Base Type</description>
         <parent>cm:content</parent>
        
         <properties>
            <property name="test:prop1">
               <type>d:encrypted</type>
               <protected>true</protected>
               <default></default>
               <constraints>
               </constraints>
            </property>
         </properties>
        
         <associations>
         </associations>
        
         <mandatory-aspects>
         </mandatory-aspects>
      </type>
     
   </types>
</model>

the following code creates a node of type test:encrypted using MetadataEncryptor to encrypt the property.

MetadataEncryptor metadataEncryptor = (MetadataEncryptor)ctx.getBean("metadataEncryptor");

.....................................................................................................

  Map<QName, Serializable> allProperties = new PropertyMap();
  allProperties.put(ENCRYPTED_PROP_QNAME, "ABC");
  allProperties = metadataEncryptor.encrypt(allProperties);
               
  try
   {
     // Create a node using the thread's locale
      NodeRef nodeRef2 = nodeService.createNode(
      nodeRef1,
      ContentModel.ASSOC_CONTAINS,
      QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()),
      ENCRYPTED_TYPE_QNAME, allProperties).getChildRef();

The property can be decrypted as follows:

Serializable encryptedPropertyValue = nodeService.getProperty(nodeRef2, ENCRYPTED_PROP_QNAME);
Serializable decryptedPropertyValue = metadataEncryptor.decrypt(ENCRYPTED_PROP_QNAME, encryptedPropertyValue);
assertEquals("ABC", decryptedPropertyValue);

Changing encrypted properties keystore keys and re-encryption

During bootstrap, the repository checks whether the keys in the main encrypted properties keystore have been changed in order to detect any accidental keystore changes.

However if you purposely want to change your keys, you can do so and the repository will re-encrypt any existing encrypted node properties for you. The newly encrypted node properties will be encrypted using the new keys.

Changing your keys involves backing up your keystore to a specific location and creating a new keystore in its place. This can be done in two ways:

  • During bootstrap
  • During runtime (not in Alfresco Community Edition)

Bootstrap Re-encryption

Re-encryption occurs during the repository bootstrap. For bootstrap re-encryption, follow the steps below:

  1. Stop the Alfresco Content Services server.
  2. Set the following property in the alfresco-global.properties file.

    encryption.bootstrap.reencrypt=true 
    
  3. Backup the current keystore to backup-keystore as shown below:

    mv keystore backup-keystore
    mv keystore-passwords.properties backup-keystore-passwords.properties
    
  4. Copy your new keystore over the old keystore.
  5. Update keystore-passwords.properties with the passwords you used to create the keystore. In other words, update the keystore.password property with the keystore password and the metadata.password property with the metadata key password.
  6. Restart the server.

Runtime Re-encryption

Re-encryption occurs while the repository is running. For runtime re-encryption, follow the steps below:

  1. Backup the current keystore to backup-keystore.

    mv keystore backup-keystore
    mv keystore-passwords.properties backup-keystore-passwords.properties
    
  2. Copy your new keystore over the old keystore.
  3. In your JMX console, execute the operation Encryption > Operations > Encrypt.

    This will re-read the main and backup keystores and re-encrypt the encrypted properties. The repository can continue to run during this operation; any newly-created encrypted properties will be encrypted with the new key.

    Note: Only a single re-encryption can be done at a particular time. If a re-encrypt is already running then subsequent requests have no effect.

Cryptographic password hashing

Alfresco Content Services uses cryptographic password hashing technique to securely store passwords.

All versions Alfresco Content Services 5.2.7 used the MD4 (Message Digest 4) and SHA256 hash algorithms (mainly to support NLTM and CIFS) to store critical data. But this is no longer considered a secure approach as the hashed password is very easy to decrypt. You now have the option to configure Alfresco Content Services 5.2.7 to use Bcrypt to store passwords. By default, the system uses MD4 to allow users to use MD4 hashed passwords for NTLM and CIFS authentication.

Bcrypt is an adaptive hash function based on the Blowfish symmetric block cipher cryptographic algorithm. It is incredibly slow to hash input compared to other functions, but this results in a much better output hash. Alfresco Content Services 5.2.7 is configured to use a strength of 10 to provide a good compromise of speed and strength.

With Bcrypt, the hashing algorithm (also called an encoder) can be configured by setting the system.preferred.password.encoding property in the alfresco-global.properties file. The supported values for this property are:

  • md4
  • sha256
  • bcrypt10

If you provide a different value, the repository won’t start.

To maintain backwards compatibility with previous versions, the default setting for this property is:

system.preferred.password.encoding=md4

After upgrading to Alfresco Content Services 5.2.7, when the user logs in or changes the password, the system rehashes the password using the preferred encoding mechanism and stores the mechanism being used. If the preferred encoding is set to md4, the system moves the current hashed passwords for that user.

Note: If SAML SSO is enabled, cryptographic password rehashing will not work at login.

You can run a background job to completely remove all the old hashed passwords for those users that have not logged in yet. If the system is still set to md4, all user objects will be upgraded. However, the background job will maintain the current hash.

If the background job is executed after the system.preferred.password.encoding property has been changed, it will double-hash all the user objects in the system (unless they have already been upgraded by the user logging in). As a result, the system will temporarily hash (until the user logs in) the current hashed password, store the list of encoders used, and clean out the old hashes.

The background job uses the repository’s BatchProcessor to execute the job. The execution of the job can be controlled if necessary via the following properties:

  • system.upgradePasswordHash.jobBatchSize: Specifies the number of user objects to process in each batch.
  • system.upgradePasswordHash.jobQueryRange: Specifies the nodeId range to search for in each iteration.
  • system.upgradePasswordHash.jobThreadCount: Specifies the number of threads the batch processor uses.

Out of the box, this background job is enabled but set to a future date. To configure it, set the system.upgradePasswordHash.jobCronExpression property in the alfresco-global.properties file. For example, the following setting runs the job every 10 minutes:

system.upgradePasswordHash.jobCronExpression=0 0/10 * * * ?

Alternatively, the job can be executed immediately via a JMX console. The job makes use of JobLockService so it is safe to run in a clustered environment.

Note: Once you change the preferred encoding from md4, the NTLM SSO authentication will no longer function. Also, the CIFS authentication will only work if the Kerberos authentication is enabled.

If the password upgrade job is enabled, make sure you enable the log4j.logger.org.alfresco.repo.security.authentication.UpgradePasswordHashWorker logging in log4j.properties.

You can either set it to trace or debug as shown below:

log4j.logger.org.alfresco.repo.security.authentication.UpgradePasswordHashWorker=trace

OR

log4j.logger.org.alfresco.repo.security.authentication.UpgradePasswordHashWorker=debug

Trace displays a list of all the processed users. Debug is a slightly less verbose output; it displays a list of only those users whose password was changed.

To monitor users that have their passwords upgraded when they log in, add the following in log4j.properties:

log4j.logger.org.alfresco.repo.security.authentication.HashPasswordTransactionListener=debug

Encrypting properties

The alfresco-global.properties file (and other subsystem properties file) holds configuration properties that contain sensitive information or passwords, such as db.password. All the properties that can be specified in the alfresco-global.properties file can be encrypted.

Use this information to encrypt any property using the Alfresco Encrypted Properties Management Tool. This tool uses the RSA/ECB/PKCS1PADDING encryption algorithm.

Note: This functionality is not related to Encrypted node properties (Encrypted node properties will be deprecated and no longer available from Alfresco Content Services 6.0 and above) or cryptographic password hashing.

Important: Boolean properties, number properties, and properties that contain expressions cannot be encrypted.

The values for some of the properties that may contain sensitive data (see the list below) is hidden from JMX whereas other values, including non-sensitive values are shown in JMX. The administrator can set new values for the security-sensitive properties in JMX but they can’t see the old value.

Here is the list of protected attributes (the value for these will be masked in the JMX console and Admin Console UI):

  • alfresco.hazelcast.password
  • db.password
  • mail.password
  • solr.solrPassword
  • cryptodoc.jce.key.passwords
  • cryptodoc.jce.keystore.password
  • ldap.synchronization.java.naming.security.credentials

  • Encrypting configuration properties
    You can encrypt sensitive properties in the alfresco-global.properties configuration file.

Encrypting configuration properties

You can encrypt sensitive properties in the alfresco-global.properties configuration file.

  1. Run the Alfresco Encrypted Properties Management Tool.

    1. Navigate to <ALFRESCO_HOME>/bin directory.

    2. Locate the Alfresco Encrypted Properties Management Tool, alfresco-spring-encryptor.jar.

    3. Run the executable jar file.

      java -jar alfresco-spring-encryptor.jar
      

  2. Generate the public and private keys using the initkey function. The public and private key pair is stored in the enterprise directory.

    java -jar alfresco-spring-encryptor.jar initkey c:/alfresco/tomcat/shared/classes
    

    You now have a public key (alfrescoSpringKey.pub) and a private key (alfrescoSpringKey.pri) in your <ALFRESCO_HOME>/tomcat/shared/classes/alfresco/extension/enterprise directory.

    Note: The private key file should be secured with the operating system permissions so that only the Alfresco Content Services process can read it.

    Note: Anyone can encrypt new values with the public key but only the Alfresco process can read the plain text value with the private key.

  3. Generate the encrypted string for your password/value using the encrypt function.

    java -jar alfresco-spring-encryptor.jar encrypt c:/alfresco/tomcat/shared/classes <password>
    

    Note: In the above command, remember to replace <password> with the actual password that you want to encrypt.

  4. Validate that the encrypted value obtained in Step 3 will decrypt the password.

    1. Run the validate function.

      java -jar alfresco-spring-encryptor.jar validate c:/alfresco/tomcat/shared/classes <encrypted value>
      

      Note: In the above command, remember to replace <encrypted value> with encrypted string value obtained in Step 3.

    2. You will be prompted to specify the value. Enter the password/value you want to encrypt.

    3. You will be prompted to specify the value again. Enter the password/value you want to encrypt.

  5. Add the encrypted password to <ALFRESCO_HOME>/tomcat/shared/classes/alfresco-encrypted.properties file.

    db.password.enc=ENC(<enter encrypted password here>)
    
    db.password.enc=ENC(QcAf1Lr81meuP2p6Lu9ZQqFY1AsCfoWd)
    

    Note: Uncomment the db.password.enc property by removing the “#” character.

  6. Set the value of the db.password property in the alfresco-global.properties file to point to the db.password.enc property in the alfresco-encrypted.properties file.

    db.password=${db.password.enc}
    

    Note: Uncomment the db.password property by removing the “#” character.

Authorities

Authorities are people (or persons) or groups.

A group can contain people or other groups as members. The authorities assigned to a user at any time are the userName from their associated Person node, all of the groups in which the user is a direct or indirect member, and any appropriate dynamic authorities. Dynamic authorities are used for internal roles.

  • Dynamic authorities and roles
    Alfresco Content Services uses some custom roles. To implement a custom role, you create a dynamic authority for that role and assign global permissions to it. The internal roles have not been assigned any object-specific rights.
  • People and users
    When a user logs in, Alfresco Content Services validates the user’s identifier and password. It uses the identifier to look up the appropriate person details for the user, using the userName property on the Person type. You can configure this look-up to be case sensitive or case insensitive. The userName property on the matching Person node is used as the actual user authority; it might differ in case from the user identifier presented to the authentication system. After the Person node look-up, Alfresco Content Services is case sensitive when matching authorities to permissions, group membership, roles, and for all other authorization tests.
  • Groups
    Groups are collections of authorities with a name and display name.
  • Zones
    All person and group nodes are in one or more zones. You can use zones for any partitioning of authorities. For example, synchronization uses zones to record from which LDAP server users and groups have been synchronized. Zones are used to hide some groups that provide Role Based Access Control (RBAC) role-like functionality from the administration pages of Alfresco Share. Examples of hidden groups are the roles used in Share and Alfresco Records Management. Only users and groups in the default zone are shown for normal group and user selection on the group administration pages. Zones cannot be managed from the administration pages of Share.

Dynamic authorities and roles

Alfresco Content Services uses some custom roles. To implement a custom role, you create a dynamic authority for that role and assign global permissions to it. The internal roles have not been assigned any object-specific rights.

The internal roles are:

  • ROLE_ADMINISTRATOR is assigned to the default administrators for the configured authentication mechanisms or members of the administration groups defined on the AuthorityServiceImpl bean. This role has all rights.
  • ROLE_OWNER is assigned to the owner of a node. If there is no explicit owner, this role is assigned to the creator. This role has all rights on the owned node.
  • ROLE_LOCK_OWNER is assigned to the owner of the lock on a locked node. This supports a lock owner’s right to check in, cancel a check out, or unlock the node.

Alfresco Share supports the assignment of permissions only to the owner role. You can use such things as the Java API and scripting to make other assignments.

Note: Hierarchical and zoned roles can be added in the future to avoid the hidden group implementation for true roles.

People and users

When a user logs in, Alfresco Content Services validates the user’s identifier and password. It uses the identifier to look up the appropriate person details for the user, using the userName property on the Person type. You can configure this look-up to be case sensitive or case insensitive. The userName property on the matching Person node is used as the actual user authority; it might differ in case from the user identifier presented to the authentication system. After the Person node look-up, Alfresco Content Services is case sensitive when matching authorities to permissions, group membership, roles, and for all other authorization tests.

Any user, who authenticates by any mechanism, must have an associated person node. Person nodes can be:

  • Explicitly created
  • Created on demand with some default entries
  • Created from LDAP synchronization

Person nodes are explicitly created when using Alfresco Share to manage users.

By default, person nodes are auto-created if not present. If an external authentication system is configured, such as NTLM, when any user authenticates, an appropriate person node might not exist. If a person node does not exist and auto-creation is enabled, a person node will then be created using the identifier exactly as presented by the user and validated by the authentication system. The auto-created Person node’s userName will have the same case as typed by the user. LDAP synchronization will create person nodes with the userName, as provided from the LDAP server.

It is possible that LDAP synchronization can change the userName associated with a Person node. For example, this can happen with a system that uses NTLM authentication, LDAP synchronization, or a system that creates person nodes on demand, or uses case-insensitive authentication. For example, Andy could log in as “Andy” and the associated Person node is created with the userName “Andy.” Later, the LDAP synchronization runs and changes the userName to “andy”.

Changes to Person node userNames will cause updates to other related data, such as ACL assignment.

Groups

Groups are collections of authorities with a name and display name.

Groups can include other groups or people. You can include a group in one or more other groups, as long as this inclusion does not create any cyclic relationships.

Zones

All person and group nodes are in one or more zones. You can use zones for any partitioning of authorities. For example, synchronization uses zones to record from which LDAP server users and groups have been synchronized. Zones are used to hide some groups that provide Role Based Access Control (RBAC) role-like functionality from the administration pages of Alfresco Share. Examples of hidden groups are the roles used in Share and Alfresco Records Management. Only users and groups in the default zone are shown for normal group and user selection on the group administration pages. Zones cannot be managed from the administration pages of Share.

Zones are intended to have a tree structure defined by naming convention. Zones are grouped into two areas: Application-related zones and authentication-related zones.

Within a zone, a group is considered to be a root group if it is not contained by another group in the same zone.

Alfresco Content Services uses a model for persisting people, groups, and zones. A Person node represents each person, and an AuthorityContainer represents groups, which can be used for other authority groupings such as roles. AuthorityContainer and Person are sub-classes of Authority and as such can be in any number of Zones.

  • Application-related zones
    Application-related zones, other than the default, hide groups that implement RBAC like roles. Application zones, by convention, start APP. and include:
  • Authorization-related zones
    Zones are also used to record the primary source of person and group information. They can be held within Alfresco Content Services or some external source. While authorities can be in many zones, it makes sense for an authority to be in only one authentication-related zone.

Application-related zones, other than the default, hide groups that implement RBAC like roles. Application zones, by convention, start APP. and include:

  • APP.DEFAULT is for person and group nodes to be found by a normal search. If no zone is specified for a person or group node, they will be a member of this default zone.
  • APP.SHARE is for hidden authorities related to Alfresco Share.
  • APP.RM will be added for authorities related to RM.

Zones are also used to record the primary source of person and group information. They can be held within Alfresco Content Services or some external source. While authorities can be in many zones, it makes sense for an authority to be in only one authentication-related zone.

  • AUTH.ALF is for authorities defined within Alfresco Content Services and not synchronized from an external source. This is the default zone for authentication.
  • AUTH.EXT. is for authorities defined externally, such as in LDAP.

Defining permissions

Permissions and their groupings are defined in an XML configuration file.

The default file is found in the distribution configuration directory as permissionDefinitions.xml. This configuration can be replaced or extended.

The following example uses the permission definitions related to the Ownable aspect.

<!-- ============================================== -->
   <!-- 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">
        <includePermissionGroup permissionGroup="SetOwner" type="cm:ownable" />
      </permissionGroup>
       
      <permissionGroup name="SetOwner" 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="SetOwner" />
        <requiredPermission on="node" type="sys:base" name="_WriteProperties" />
      </permission>
      
</permissionSet>

Permissions and permission groups are defined in a permission set, which is a sub-element of the permissions root element. A permission set is associated with a type or aspect and applies only to that type and sub-types, or aspect and sub-aspects.

A permission has a name. By convention, the names of permissions start with an underscore character. A permission, in its definition, can be granted to any number of permission groups. This means that those permission groups will include the permission. The permission might require that the type or aspect specified on the permission set be present on the node. If a permission is associated with an aspect and the requiresType property is set to true then if that aspect is not applied to a node, the permission does not apply to that node either. If an aspect-related permission definition has the requiresType property set to false, the permission applies to any node, even if the aspect has not been applied to the node.

An aspect can be applied at any time and there are no restrictions as to which aspects can be applied to a type. A permission might also require other permissions be tested on the same node, its children, or its parent. In the example, _SetOwner requires _WriteProperties. This means you cannot set ownership on a node if you are not allowed to write to its properties. You can also use this to check that all children can be deleted before deleting a folder, or to enforce that you can only read nodes for which you can read all the parents; neither are normally required. The configuration to do this is present in the standard configuration file but is commented out. The _DeleteNode permission definition (as shown in the following code snippet) is an example. If permission A requires permission B and this requirement is implied (by setting the implies attribute of the requiredPermission element to true), assigning an authority permission A will also give them permission B (as opposed to checking they have permission B).

<permission name="_DeleteNode" expose="false" >
    <grantedToGroup permissionGroup="DeleteNode" />
    <!-- Commented out parent permission check ...
    <requiredPermission on="parent" name="_ReadChildren" implies="false"/>
    <requiredPermission on="parent" name="_DeleteChildren" implies="false"/>
    <requiredPermission on="node" name="_DeleteChildren" implies="false"/>
     -->
    <!-- Recursive delete check on children --> 
    <!--  <requiredPermission on="children" name="_DeleteNode" implies="false"/>  -->
</permission>

Permissions are normally hidden inside permission groups. Permission groups are made up of permissions and other permission groups. By convention, each permission has a related permission group. Permission groups can then be combined to make other permission groups. As for permissions, a permission group can be exposed by the administration pages of Alfresco Share and might require the presence of a type or aspect to apply to a particular node. In addition, a permission group can allow full control, which grants all permissions and permission groups. As a type or aspect can extend another, a permission group defined for a type or aspect can extend one defined for one of its parent types and be assigned more permissions, include more permission groups, or change what is exposed in the administration pages of the Alfresco Share web clients.

It is unusual to extend or change the default permission model unless you are adding your own types, aspects, and related public services or you wish to make minor modifications to the existing behavior. The following code snippets show how to extend and replace the default permission model.

<bean id='permissionsModelDAO'
class="org.alfresco.repo.security.permissions.impl.model.PermissionModel" init-method="init">
        <property name="model">
<-- <value>alfresco/model/permissionDefinitions.xml</value> -->
<value>alfresco/extension/permissionDefinitions.xml</value>
        </property>
        <property name="nodeService">
            <ref bean="nodeService" />
        </property>
        <property name="dictionaryService">
            <ref bean="dictionaryService" />
        </property>
</bean>

The preceding code example shows how to replace the default permission model with one located in the alfresco/extension directory. The following code snippet shows how to extend the existing model.

<bean id="extendPermissionModel" parent="permissionModelBootstrap">
   <property name="model" value="alfresco/extension/permissionModelExtension.xml" /> 
</bean>
  • Controlling site creation permissions
    By default, any authenticated user can create sites in Share. The creator of the new site is given the Site Manager role and they control who has access to the site and in what role.

Controlling site creation permissions

By default, any authenticated user can create sites in Share. The creator of the new site is given the Site Manager role and they control who has access to the site and in what role.

The beans that enforce security to the repository services based on the currently authenticated user are defined in the public-services-security-context.xml file.

  1. Copy the following code and add it to the /custom-model-context.xml file.

    <?xml version='1.0' encoding='UTF-8'?>
    <!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
    <beans>
        <bean id="SiteService_security" class="org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor">
            <property name="authenticationManager"><ref bean="authenticationManager"/></property>
            <property name="accessDecisionManager"><ref bean="accessDecisionManager"/></property>
            <property name="afterInvocationManager"><ref bean="afterInvocationManager"/></property>
            <property name="objectDefinitionSource">
                <value>
                   org.alfresco.service.cmr.site.SiteService.cleanSitePermissions=ACL_NODE.0.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.createContainer=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.createSite=ACL_METHOD.GROUP_SITE_CREATORS
                   org.alfresco.service.cmr.site.SiteService.deleteSite=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.findSites=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.getContainer=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.listContainers=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.getMembersRole=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.getMembersRoleInfo=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.resolveSite=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.getSite=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.getSiteShortName=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.getSiteGroup=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.getSiteRoleGroup=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.getSiteRoles=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.getSiteRoot=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.hasContainer=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.hasCreateSitePermissions=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.hasSite=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.isMember=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.listMembers=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.listMembersInfo=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.listMembersPaged=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.listSiteMemberships=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.listSites=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.listSitesPaged=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
                   org.alfresco.service.cmr.site.SiteService.removeMembership=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.canAddMember=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.setMembership=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.updateSite=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.countAuthoritiesWithRole=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.isSiteAdmin=ACL_ALLOW
                   org.alfresco.service.cmr.site.SiteService.*=ACL_DENY
                </value>
            </property>
        </bean>
    </beans>
    
  2. Modify the inserted SiteService_security bean to match your requirements. For example:

    To give permission to only Administrators to create site, change:

    org.alfresco.service.cmr.site.SiteService.createSite=ACL_ALLOW
    

    to

    org.alfresco.service.cmr.site.SiteService.createSite=ACL_METHOD.ROLE_ADMINISTRATOR
    

    where, ACL_ALLOW executes a method that allows access to all users and ACL_METHOD.ROLE_ADMINISTRATOR executes a method that allows access to users who are members of the administrator group.

  3. Save the file.

  4. Restart Alfresco Content Services.

Access Control Lists

An Access Control List (ACL) is an ordered list of one or more Access Control Entries (ACE). An ACE associates a single authority to a single permission group or permission, and states whether the permission is to be allowed or denied. All nodes have an associated ACL.

There is one special, context-free, ACL defined in the XML configuration to support global permissions. An ACL specifies if it should inherit ACEs from a parent ACL. The parent ACL is associated with the primary parent node. When a new node is created it automatically inherits all ACEs defined on the parent within which it is created. Linking a node to a secondary parent has no effect on ACE inheritance; the node will continue to inherit permission changes from its primary parent (defined when it was first created).

By default, ACL inheritance is always from the primary parent. The underlying design and implementation does not mandate this. ACL inheritance does not have to follow the parent child relationship. It is possible to change this through the Java API.

There are several types of ACL defined in ACLType. The main types are:

  • DEFINING
  • SHARED
  • FIXED
  • GLOBAL

A node will be associated with an ACL. It will have a DEFINING ACL if any ACE has been set on the node. DEFINING ACLs include any ACEs inherited from the node’s primary parent and above, if inheritance is enabled. All DEFINING ACLs are associated with one SHARED ACL. This SHARED ACL includes all the ACEs that are inherited from the DEFINING ACL. If the primary children of a node with a DEFINING ACL do not themselves have any specific ACEs defined then they can be assigned the related SHARED ACL. For the primary children of a node with a SHARED ACL that also have no specific ACEs set they can use the same SHARED ACL. A single SHARED ACL can be associated with many nodes. When a DEFINING ACL is updated, it will cascade update any related ACLs by using the ACL relationships rather than walk the node structure. If a DEFINING ACL inherits ACEs, then these will come from the SHARED ACL related to another DEFINING ACL.

ACLs and nodes have two linked tree structures.

FIXED ACLs are not associated with a node but found by name. A node ACL could be defined to inherit from a fixed ACL. A GLOBAL ACL is a special case of a FIXED ACL with a well known name. It will be used to hold the global ACE currently defined in XML.

ACEs comprise an authority, a permission, and a deny/allow flag. They are ordered in an ACL.

  • ACL ordering and evaluation
    The ACEs within an ACL are ordered and contain positional information reflecting how an ACE was inherited. DEFINING ACLs have entries at even positions; SHARED ACLs have entries at odd positions. For a DEFINING ACL, any ACEs defined for that ACL have position 0, any inherited from the parent ACL have position two, and so on. For a SHARED ACL, ACEs defined on the ACL from which it inherits will have position one.
  • An ACL example
    This example relates a tree of nodes to two corresponding trees of ACLs. The nodes in the node tree are identified by number and are shown filled in black if they have any ACEs set, or white/clear if not. Primary child relationships are drawn as black lines and secondary child relationships as dashed lines. ACLs in the ACL trees are identified by letter, DEFINING ACLs are shown filled in black, and SHARED ACLs are shown as clear. Under each node on the node tree the related ACL is referenced.

ACL ordering and evaluation

The ACEs within an ACL are ordered and contain positional information reflecting how an ACE was inherited. DEFINING ACLs have entries at even positions; SHARED ACLs have entries at odd positions. For a DEFINING ACL, any ACEs defined for that ACL have position 0, any inherited from the parent ACL have position two, and so on. For a SHARED ACL, ACEs defined on the ACL from which it inherits will have position one.

When Alfresco Content Services makes permission checks, ACEs are considered in order with the lowest position first. Deny entries take precedence over allow entries at the same position. Once a deny entry is found for a specific authority and permission combination, any matching ACE, at a higher position from further up the inheritance chain, is denied. A deny for one authority does not deny an assignment for a different authority. If a group is denied Read permission, a person who is a member of that group can still be assigned Read permission using another group or directly with their person userName. However, if an authority is granted Read (made up of ReadContent and ReadProperties) and the same authority denied ReadContent, they will just be granted ReadProperties permission. The administration pages of Alfresco Share do not expose deny.

The default configuration is any deny denies. This is set by adding the following property to the alfresco-global.properties file:

security.anyDenyDenies=true  

You can alter the configuration to support any allow allows. This is set by adding the following property to the alfresco-global.properties file:

security.anyDenyDenies=false

An ACL example

This example relates a tree of nodes to two corresponding trees of ACLs. The nodes in the node tree are identified by number and are shown filled in black if they have any ACEs set, or white/clear if not. Primary child relationships are drawn as black lines and secondary child relationships as dashed lines. ACLs in the ACL trees are identified by letter, DEFINING ACLs are shown filled in black, and SHARED ACLs are shown as clear. Under each node on the node tree the related ACL is referenced.

The table describes the ACEs in each ACL and their position.

ACL format Authority Permission Allow/Deny Position
ACL A (Defining, no inheritance) All Read Allow 0
ACL B (Shared, inherits from ACL A) All Read Allow 1
ACL C (Defining, inherits from ACL B) All Read Allow 2
ROLE_OWNER All Allow 0  
GROUP_A Write Allow 0  
GROUP_A CreateChildren Allow 0  
ACL D (Shared, inherits from ACL C) ALL Read Allow 3
ROLE_OWNER All Allow 1  
GROUP_A Write Allow 1  
GROUP_A CreateChildren Allow 1  
ACL E (Defining, inherits from ACL B) All Read Allow 2
Andy All Allow 0  
Bob Write Allow 0  
Bob WriteContent Deny 0  
ACL F (Shared, inherits from ACL E) All Read Allow 3
Andy All Allow 1  
Bob Write Allow 1  
Bob WriteContent Deny 1  
ACL G (Defining, no inheritance) Bob All Allow 0
ACL H (Shared, inherits from ACL G) Bob All Allow 1

ACL A, and any ACL that inherits from it, allows Read for everyone (All) unless permissions are subsequently denied for everyone (All). If ACL A is changed, all the ACLs that inherit from ACL A in the ACL tree will reflect this change. In the example, nodes 1-12 would be affected by such a change. Nodes 13 and 14 would not inherit the change due to the definition of ACL G.

ACL C adds Contributor and Editor permissions for any authority in GROUP_A.

Note: The GROUP_ prefix is normally hidden by the administration pages of Alfresco Share.

Anyone in GROUP_A can edit existing content or create new content. The owner ACE means that anyone who creates content then has full rights to it. The ACE assignment for owner is not normally required as all rights are given to node owners in the context-free ACL defined in the default permission configuration.

ACL E adds some specific user ACEs in addition to those defined in ACL A. As an example, it allows Bob Write but also denies WriteContent. Write is made up of WriteContent and WriteProperties. Bob will only be allowed WriteProperties.

ACL G does not inherit and starts a new ACL tree unaffected by any other ACL tree unless an inheritance link is subsequently made.

If a new node was created beneath node 13 or 14 it would inherit ACL H. If a new node was created beneath nodes 1, 6, 7, or 8 it would inherit ACL B.

If a node that has a shared ACL has an ACE set, a new defining ACL and a related shared ACL are inserted in the ACL tree. If a defining ACL has all its position 0 ACEs removed, it still remains a defining ACL: There is no automatic clean up of no-op defining ACLs.

Modifying access control

Modifying access control can involve changing definitions, adding services, defining types and aspects, or adding definitions to new or existing security interceptors.

Main functions include:

  • Changing the definition of existing security interceptors to check for different conditions
  • Adding new public services and related security interceptors
  • Defining new types and aspects and their related permissions
  • Adding new definitions to the security interceptor by implementing an ACEGI AccessDecisionVoter and/or AfterInvocationProvider (in extreme cases)

A few constraints and design patterns should be observed when modifying access control. Permissions apply to the node as whole. In particular, the same Read rights apply to all properties and content. You should check that methods can be executed and not that a user has a particular permission. The access control restrictions for a public service method can change. Follow the design pattern to implement RBAC roles.

When modifying access control, do not try to split ReadProperties and ReadContent. This does not make sense for search. A node and all of its properties, including content, are indexed as one entity. Splitting the evaluation of access for content and properties is not possible. Search would have to apply both criteria so as to not leak information. Other services, such as copy, might not behave as expected or might produce nodes in an odd state.

Permissions are assigned at the node level, not at the attribute level. Again, this makes sense with the search capabilities. 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 and not individual properties. 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 metadata. Queries would have to be done in parts and joined by hand, as there is no native support for SQL-like join.

Check that method execution is allowed and not that the user has a fixed permission. Rather than checking for Read permission in code, check that the appropriate method can be called using the PublicServiceAccessService bean. This avoids hard coding to a specific permission implementation and is essential if you intend to mix records management and the content repository.The access restrictions for public service methods can change. The PublicServiceAccessService bean allows you to test if any public service method can be invoked successfully with a given set of arguments. It checks all the entry criteria for the method and, assuming these have not changed, the method can be called successfully. The method call can still fail if the conditions for the returned object are not met or some security configuration has changed, such as an ACE is removed, a user is removed from a group, or the method fails for a non-authorization reason.

For those coming from an RBAC background, Alfresco Content Services has roles in the RBAC sense only for limited internal use. To implement RBAC use zoned groups. These groups will not appear in the administration pages of Alfresco Share as normal groups (unless you also add them to the APP.DEFAULT zone) but can be used to assign users and groups to roles. This approach has been taken to support roles in Alfresco Share. To map RBAC terminology to Alfresco Content Services: operations map to method calls on public service beans, objects map to method arguments including nodes (folders, documents, and so on). Users and permissions/privileges map directly. Alfresco Content Services allows the assignment of permissions to users or groups.

By default, the owner of an object can manage any aspect of its ACL. Users with ChangePermissions rights for a node can also change its ACL. If users have the ability to alter the ACL associated with an object, they can allow other users to do the same. There is no restriction on the permissions they can assign. The Alfresco Content Services model supports liberal discretionary access control with multi-level grant. A user who can grant access can pass on this right without any restriction. In addition, anyone who can change permissions can carry out the revocation of rights: it is not restricted to the original granter. Normally, when someone can perform an operation you would not expect it is because they own the node and therefore have all permissions for that node.

Public services

Security is enforced around public services. Web services, web scripts, Alfresco Share, CIFS, WebDAV, FTP, CMIS, and more, all use public services, and therefore include security enforcement.

Public services are defined in public-services-context.xml.

Access control allows or prevents users or processes acting on behalf of a user, 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, or that the user has a particular authority.

For example, on the NodeService bean, the readProperties method checks that the current user has Read permission for the node before invoking the method and returning the node’s properties. On the SearchService query method, the results are restricted to return only the nodes for which a user has Read permission.

  • Public services configuration
    Security is enforced in the Spring configuration by defining proxies for each internal service implementation and adding a method interceptor to enforce security for each public service proxy.
  • Method-level security definition
    Method access is defined in the normal ACEGI manner with some additions.

Public services configuration

Security is enforced in the Spring configuration by defining proxies for each internal service implementation and adding a method interceptor to enforce security for each public service proxy.These interceptors also have other roles. When a method is called on a public service, the security interceptor is called before the method it wraps. At this stage, the interceptor can examine the function arguments to the method and check that the user has the appropriate rights for each argument 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 they do not have the permission, a security exception is raised. If all the entry criteria are met, the method goes ahead.

In a similar manner, after a method has executed the interceptor can examine the returned object and decide if it should return it to the caller. For example, a search method could return a list of nodes. The security interceptor could filter this list for only those nodes for which the current user has Read permission.

It is also possible to configure a method so that it can be called by all users, only by users with the admin role, or only by specific users or groups. This can also be enforced by the security method interceptor.

Access control interceptor definitions for public services are included in \tomcat\webapps\alfresco\WEB-INF\classes\alfresco\public-services-security-context.xml along with any other supporting beans. This configuration file also defines the location from which the permission model is loaded. The interceptors are wired up to the public services in \tomcat\webapps\alfresco\WEB-INF\classes\alfresco\public-services-context.xml. The public services are the only Spring beans to have access control.

Method-level security definition

Method access is defined in the normal ACEGI manner with some additions.

The beans required to support Spring ACEGI-based security around method invocation are defined in public-services-security-context.xml. This configures two specific beans: A voter that can authorize method execution based on the permissions granted to the current user for specific arguments to the method, and an after invocation provider to apply security to objects returned by methods.

For the following information detailing preconditions and postconditions, these factors are all relevant:

  • ****

    Represents an authority (user name or group).

  • <#>

    Represents a method argument index.

  • ****

    Represents the string representation of a permission.

Preconditions take one of the following forms:

  • **ACL_METHOD.**

    Restricts access to the method to those with the given authority. This could be a user name, role or group. Dynamic authorities are not supported.

  • **ACL_NODE.<#>.**

    Restricts access control to users who have the specified permission for the node at the identified argument. If the argument is a NodeRef, it will be used; if it is a StoreRef, the root node for the store will be used; if it is a ChildAssociationRef, the child node will be used.

  • **ACL_PARENT.<#>.**

    Restricts access control to users who have 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, the parent node will be used.

  • ROLE

    Checks for an authority starting with ROLE_.

  • GROUP

    Checks for an authority starting with GROUP_.

Here are some examples of method level security parameters:

  • ACL_METHOD.ROLE_ADMINISTRATOR: Executes a method that allows access to users who are members of the administrator group.
  • ACL_ALLOW: Executes a method that allows access to all users.

If more than one ACL_NODE.<#>. , ACL_PARENT.<#>., or ACL_METHOD. entry is present, then all of the ACL_NODE and ACL_PARENT permissions must be present and any one of the ACL_METHOD restrictions, if present, 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.

  • **AFTER_ACL_PARENT.**

    Similar to ACL_PARENT.<#>. but the restriction applies to the return argument.

The support return types are:

  • StoreRef
  • ChildAssociationRef
  • Collections of StoreRef, NodeRef, ChildAssociationRef, and FileInfo
  • FileInfo
  • NodeRef
  • Arrays of StoreRef, NodeRef, ChildAssociationRef, and FileInfo
  • PagingLuceneResultSet
  • QueryEngineResults
  • ResultSet

The post-conditions will create access denied exceptions for return types such as NodeRef, StoreRef, ChildAssociationRef, and FileInfo. For collections, arrays, and result sets, their members will be filtered based on the access conditions applied to each member.

Continuing the example from the permissions defined for the Ownable aspect, the definition for the security interceptor for the related OwnableService is shown in the following code snippet.

<bean id="OwnableService_security"
  class="org.alfresco.repo.security.permissions.impl.acegi.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.security.OwnableService.getOwner=ACL_NODE.0.sys:base.ReadProperties
    org.alfresco.service.cmr.security.OwnableService.setOwner=ACL_NODE.0.cm:ownable.SetOwner
     org.alfresco.service.cmr.security.OwnableService.takeOwnership=ACL_NODE.0.cm:ownable.TakeOwnership
      org.alfresco.service.cmr.security.OwnableService.hasOwner=ACL_NODE.0.sys:base.ReadProperties
      org.alfresco.service.cmr.security.OwnableService.*=ACL_DENY
      </value>
    </property>
</bean>

Security for the four methods on the OwnableService is defined. To invoke the OwnableService getOwner() method on a node, the invoker must have permission to read the properties of the target node. To set the owner of a node, a user must have been explicitly assigned the SetOwner permission or have all rights to the node. A user can have all rights to a node by using the context-free ACL or be assigned a permission, which grants all permission or includes SetOwner. With the default configuration, a user will own any node they create and therefore be able to give ownership to anyone else and possibly not have the right to take ownership back.

The last entry catches and denies access for any other method calls other than those listed. If any additional methods were added to this service and no security configuration explicitly defined for the new methods, these methods would always deny access.

Implementation and services

Alfresco Content Services enforces security services for managing authentication information.

The following key services are involved in access control:

  • AuthenticationService: responsible for authenticating user name and password.
  • PersonService: responsible for obtaining a reference to the Person node for a given user name. It also creates, deletes and updates personal information.
  • AuthorityService: responsible for managing authorities.
  • PermissionService: responsible for managing ACLs and ACEs, and for checking if a user has been assigned a permission for a particular node.
  • OwnableService: manages object ownership and is used in evaluation the dynamic ROLE_OWNER authority.

Let’s consider a possible scenario to understand how the security services work. A user logs in using the authentication service, which determines the user’s authorities, such as their user name (which is a USER authority). The authority service adds and manages the relevant groups and roles. The permission service maps those users, groups and roles to operations on particular nodes. It also controls the inheritance of permissions and provides a common set of default permissions. The owner service is related to the special OWNER role and it determines the owner of a node. The person service deals with the special case of person nodes, which identify users.

The protection of public services methods is implemented using the Spring method interceptors defined as part of the related ACEGI 0.8.2 security package. The Alfresco Content Services implementation adds new implementations of the ACEGI interfaces AccessDecisionVoter and AfterInvocationProvider, which support the configuration elements that have already been described (for example, ACL_NODE.<#>.). These extension classes make use of the key services.

  • Authentication service
    Use this information to understand and configure authentication service.
  • Person service
    Use this information to understand and configure of person service.
  • Authority service
    Use this information to understand and configure authority service, using the authority-services-context.xml file.
  • Permission service
    Use this information to understand and configure permission service.
  • Ownable service
    Use this information to understand and configure ownable service.

Authentication service

Use this information to understand and configure authentication service.

The authentication service provides an API for:

  • Authenticating using a user name and password
  • Authenticating using a ticket
  • Creating, updating and deleting authentication information
  • Clearing the current authentication
  • Invalidating a ticket
  • Getting the user name for currently authenticated users
  • Getting a ticket for subsequent re-authentication

The authenticated user name is used as the key to obtain other security information, such as group membership, the details about the person or to record a user as the owner of an object. It is one of the identifiers against which permissions can be assigned.

The authentication service does not provide any details about a user other than authentication. It stores authentication information on the calling thread. Application developers should ensure that this information is cleared.

The authentication service brings together three components:

  • authentication component, which supports authentication;
  • authentication DAO, which provides an API to create, delete and update authentication information; and
  • ticket component, which manages and stores tickets that can be obtained after authentication and used in place of authentication.

The implementation and configuration for this service can be found in the authentication-services-context.xml file. This default implementation coordinates two service providers for 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.

  • Configuring multiple tickets for authentication
    For each authentication attempt, Alfresco Content Services returns a different session ID, but the same ticket for each user. You can configure multiple tickets using the authentication.ticket.useSingleTicketPerUser option.

Configuring multiple tickets for authentication

For each authentication attempt, Alfresco Content Services returns a different session ID, but the same ticket for each user. You can configure multiple tickets using the authentication.ticket.useSingleTicketPerUser option.

The TicketComponent configuration setting, in alfresco-global.properties, has an option called authentication.ticket.useSingleTicketPerUser. This option has a default setting of true, which means that only one ticket is created for each user, and this ticket is returned for every authentication attempt by that user. If the ticket is invalidated, the user is required to re-authenticate before using the repository.

To set multiple tickets for each user, set authentication.ticket.useSingleTicketPerUser=false.

Person service

Use this information to understand and configure of person service.

The PersonService interface is the API by which nodes of the person type, as defined in contentModel.xml, should be accessed.

The PersonService is responsible for all of the following:

  • 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

The beans to support the PersonService 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 by using NTLM or some other external user repository.

Authority service

Use this information to understand and configure authority service, using the authority-services-context.xml file.

The authority service is responsible for:

  • Creating and deleting authorities
  • Querying for authorities
  • Structuring authorities into hierarchies
  • Supporting queries for membership
  • Finding all the authorities that apply to the current authenticated user
  • Determining if the current authenticated user has admin rights
  • Managing zones and the assignment of authorities to zones

The default implementation allows a list of group names to define both administration groups and guest groups. Each authentication component defines its own default administrative user(s), which can also be set explicitly. The default service is defined in the authority-services-context.xml file.

  • Using guestGroups and adminGroups properties
    The authority-services-context.xml, bean id authorityService provides the property configuration of the Authority Service implementation. This configuration also allows the designation of specific groups with admin or guest permissions in the system.

Using guestGroups and adminGroups properties

The authority-services-context.xml, bean id authorityService provides the property configuration of the Authority Service implementation. This configuration also allows the designation of specific groups with admin or guest permissions in the system.

By listing a group under the guestGroups property (case insensitive), the users in that group will only be allowed guest permission. Likewise, by listing a group under the adminGroups property (case insensitive), the users in that group will be provided admin permission.

For example, assume that you are synchronizing users into Alfresco Content Services and you specifically want to specify some groups as only guest users in the system. You would override the authority-services-context.xml file adding those groups to the guestGroups list (case insensitive). As a result, users in those groups will have authenticated logins but limited to guest authorization. For details, see Configuring guestGroups and adminGroups properties.

Configuring guestGroups and adminGroups properties

Use this information to configure the guestGroups and adminGroups properties.

  1. Download the authority-services-context.xml file.

  2. Paste this file into the  directory.

  3. Open the authority-services-context.xml file.

    1. To specify some groups as only guest users, add them to the guestGroups property list.

      <!-- A list of groups with guest rights.    -->
      <!--                                        -->
              <property name="guestGroups">
                  <set>
                  </set>
              </property>
      
    2. To assign admin rights to some groups, add them to the adminGroups property list.

      <!-- A list of groups with admin rights.    -->
      <!--                                        -->
              <property name="adminGroups">
                  <set>
                      <value>ALFRESCO_ADMINISTRATORS</value>
                  </set>
              </property>
      
  4. Save the file and then restart the server.

Permission service

Use this information to understand and configure 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
  • Determining if the current, authenticated user has a permission for a node

The PermissionService interface defines constants for well-known permissions and authorities.

The default implementation coordinates implementations of two service provider interfaces: a ModelDAO and a PermissionsDAO. A permission is simply 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. The default permission model is in XML and defines permission sets, and their related permission groups and permissions. Global permissions are part of the permission model. There can be more than one permission model defined in XML; they are in practice merged into one permission model. A module can extend the permission model.

The available permissions are defined in the permission model. This is defined in permissionDefinitions.xml. This configuration is loaded in a bean definition in public-services-security-context.xml. 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 in order to apply the changes.

Ownable service

Use this information to understand and configure ownable service.

The idea of file ownership is present in both UNIX and Windows. In Alfresco Content Services, the repository has the concept of node ownership. This ownership is optional and is implemented as an aspect.

The owner of a node can have specific ACLs granted to them. Ownership is implemented as the dynamic authority, ROLE_OWNER, and is evaluated in the context of each node for which an authorization request is made. The Ownable aspect, if present, defines a node’s owner by storing a userName; if the Ownable aspect is not present, the creator is used as the default owner. If the userName of the current user matches, including case, the userName stored as the owner of the node, the current user will be granted all permissions assigned to the authority ROLE_OWNER.

The OwnableService is responsible for all of the following:

  • Determining the owner of a node
  • Setting the owner of a node
  • Determining if a node has an owner
  • Allowing the current user to take ownership of a node

The OwnableService is supported by an Ownable aspect defined in \tomcat\webapps\alfresco\WEB-INF\classes\alfresco\model\contentModel.xml.

There are permissions and permission groups associated with the Ownable aspect in the permission model and related access controls applied to the methods on the public OwnableService.

Admin password in default authentication

The Admin user password is used by the default authentication system.

The Admin password for default authentication is set as a part of the initial bootstrap. This is located in config\alfresco\bootstrap\alfrescoUserStore.xml. The password is MD4 encoded, as required by NTLM.

Note: Choose a strong, unique password for your admin account, and consider changing it regularly.

How to reset the Admin password?

If you lose or forget the password for the Admin user, you can reset the password in the database using one of the following methods:

  • If you know the password of at least one user, then:
  1. Assign Admin rights to this known user by adding the following line in the alfresco-global.properties file.

    alfresco_user_store.adminusername=username
    

    where, username is the user name of the user whose password is known.

  2. Restart the repository.
  3. Log in as the known user.
  4. Reset the Admin user’s password.
  5. Reset the configuration.
  • Reset the Admin password without knowing any user password:
  1. Configure the authentication component to accept all logins using org.alfresco.repo.security.authentication.SimpleAcceptOrRejectAllAuthenticationComponentImpl.
  2. Login as a user with Admin rights.
  3. Reset the Admin user’s password.
  4. Revert the configuration.
  • Change the password directly in the database (for Alfresco Content Services version 3.1 to 5.0):
  1. Run the following command to find out the identifying parameters for how the Admin password is stored. Check that 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'
    

    The output shows the current MD4 hashed password for the Admin user. Here’s an example output:

    +---------+----------+----------------------------------+
    | node_id | qname_id | string_value |                     
    +---------+----------+----------------------------------+
    | 4 | 10 | 209c6174da490caeb422f3fa5a7ae634 |                
    +---------+----------+----------------------------------+
    1 row in set (0.00 sec)
    
  2. To update the password, use the following command:

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

    Replace THENODEIDABOVE and THEQNAMEVALUEABOVE with the result values of node_id and qname_id, obtained in the previous step. In this example, it is 4 and 10, respectively.

    Note: Ensure that you use appropriate AND conditions in the UPDATE query.

  3. Restart Alfresco Content Services.

  • Change the password directly in the database (for Alfresco Content Services version 5.1 onwards):
  1. Run the following query to find out which encoder is being used to store the Admin password. Check that you have only one row in the output.

    Note: You must encode the password using the result of the query.

    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 = '**hashIndicator**'
    AND aq2.local_name = 'username'
    AND anp2.string_value = 'admin';
    

    The output shows the current password encoding being used.

    +---------+----------+--------------+
    | node_id | qname_id | string_value |
    +---------+----------+--------------+
    |       4 |       94 | **bcrypt10**   |
    +---------+----------+--------------+
    1 row in set (0.01 sec)
    

    If no rows are returned, set the password using the instructions shown above (md4 encoding).

    If a row is returned, encode the password using the result of the query, which can either be md4 or sha256 or bcrypt10 encoding.

    Run the following query to find the identifying parameters for how the Admin password is stored.

    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 = '**passwordHash**'
    AND aq2.local_name = 'username'
    AND anp2.string_value = 'admin';
    

    The output shows the current hashed password for the Admin user. Here’s an example output:

    +---------+----------+--------------------------------------------------------------+
    | node_id | qname_id | string_value                                                 |
    +---------+----------+--------------------------------------------------------------+
    |       4 |       93 |**$2a$10$dq/2zNUA.MmECYipl1WMoOyGHYbaygh23PUa3Ox5xDHH7Z0guqF42**|
    +---------+----------+--------------------------------------------------------------+
    1 row in set (0.00 sec)
    
  2. To update the password, use the following command:

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

    Replace THENODEIDABOVE and THEQNAMEVALUEABOVE with the result values of node_id and qname_id, obtained in the previous step. In this example, it is 4 and 93, respectively.

    Note: Ensure that you use appropriate AND conditions in the UPDATE query.

  3. Restart Alfresco Content Services.

Security policies and filters

You can configure a number of policies and filters in Alfresco Share to mitigate security attacks. You can also configure filters in Alfresco Repository to mitigate security attacks when the Alfresco Content Services ReST API is accessed externally.

Important: Cross-Site Request Forgery (CSRF)

The Open Web Application Security Project (OWASP) describes Cross-Site Request Forgery (CSRF) as a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user’s web browser to perform an unwanted action on a trusted site for which the user is currently authenticated (see the Cross-Site_Request_Forgery Prevention_Cheat_Sheet).

The Share application must be accessible on the network to be available to users, and so it is protected with a CSRF filter. You should then also ensure that /alfresco is protected behind a firewall. If another user interface client is used (i.e. not Share), such as an ADF application that directly accesses the Alfresco Content Services ReST API, then /alfresco need to be proteced with a CSRF filter.

If you want to protect those areas against CSRF attacks, then you will need to implement a solution similar to one of those listed on the CSRF prevention cheat sheet. Of particular interest is a solution based on Apache with mod_csrf because of efficiency and its loose coupling with the applications to protect.

Alfresco Share Security policies and filters

You can configure a number of policies and filters in Alfresco Share to mitigate security attacks.

The Share application must be accessible on the network to be available to users, and so it should be protected with a CSRF filter. You should then also ensure that /alfresco is protected behind a firewall. If another user interface client is used (i.e. not Share), such as an ADF application, then you need to also protect the Alfresco Repository with a CSRF filter.

Cross-Site Request Forgery (CSRF) filters for Share

You can configure CSRFPolicy in Alfresco Share to prevent CSRF attacks that allow malicious requests to be unknowingly loaded by a user.

You can configure the CSRF filter to run with third party plugins and to stop specific repository services from being accessible directly through the Share proxy.

The filter is implemented in the org.alfresco.web.site.servlet.CSRFFilter that reads the CSRFPolicy configuration section in share-security-config.xml.

CSRFPolicy describes how and when the filter mitigates CSRF attacks:

  • Each logged in user receives a secret CSRF token

  • The token is communicated to the browser using a Alfresco-CSRF-Token cookie

  • When a logged in user performs a POST, PUT or DELETE HTTP request against Share the token must be passed in the request using one of the following methods:

    • As a custom HTTP request header called Alfresco-CSRF-Token
    • As a URL parameter called Alfresco-CSRF-Token

      Note: Usually the header is required, but in some circumstances a header cannot be used and in this case the token can be passed using a URL parameter. The default config only accepts the URL parameter when the Content-Type header starts with multipart/.

  • Every time the logged in user visits a new Share page the token is renewed
  • The filter checks that the referrer and original HTTP request headers match the current domain (if this is present in the request)

Do I need to alter my custom code?

Generally, you should not need to alter your custom code, for example, the following cases need no code alteration:

  • You are reading data using GET requests only
  • You are using the standard Alfresco.util.Ajax, alfresco/core/CoreXhr or Alfresco.forms.Form javascript classes when creating, updating or deleting data
  • You are writing a non-browser client (for example, a mobile application)

However, in these situations you will need to alter your code:

  1. You are making an XMLHttpRequest with POST, PUT or DELETE methods without using the Alfresco.util.Ajax or alfresco/core/CoreXhr classes. If you are using the native XMLHttpRequest object or a third party library such as jQuery, add code to pass the token, for example:

    if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
    {
       xhrHeadersObject[Alfresco.util.CSRFPolicy.getHeader()] = Alfresco.util.CSRFPolicy.getToken();
    } 
    

    If you are using YAHOO.util.DataSource to load data with POST requests, add code similar to this example:

    if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
    {
       yuiDataSource.connMgr.initHeader(Alfresco.util.CSRFPolicy.getHeader(), Alfresco.util.CSRFPolicy.getToken(), false);
    } 
    
  2. You are making a form upload with enctype multipart/form-data without using Alfresco.forms.Form.

    When you upload a file by submitting a form with enctype multipart/form-data it is not possible to set a header on the request because it is not possible to set a header on any form submission in the browser. Pass the token as a URL parameter instead. If you are using the Alfresco.forms.Form class, this is handled for you automatically, otherwise add the token as a URL parameter, for example:

     if (Alfresco.util.CSRFPolicy && Alfresco.util.CSRFPolicy.isFilterEnabled())
    {
       url += "?" + Alfresco.util.CSRFPolicy.getParameter() + "=" + encodeURIComponent(Alfresco.util.CSRFPolicy.getToken());
    } 
    
  3. You are using a Flash movie inside Share to send HTTP requests with method POST.

    If you are using a Flash movie to upload files, using the flash.net.FileReference ActionScript class to perform a multipart/form-data request, add the token as a URL parameter in your Javascript before passing in the URL to the Flash movie. If your Flash movie is performing application/json or other text based POST requests, using the flash.net.URLRequest and/or flash.net.navigateToURL ActionScript classes and methods, pass the token and the name of the header so that it can be set from the Flash movie.

When else might I need to make code updates?

If servers from other domains are allowed to POST requests to your system, then you need to reconfigure CSRFPolicy in your share-config-custom.xml file so that the token or header is not checked:

  1. Copy the CSRFPolicy configuration from share-security-config.xml and paste it into share-config-custom.xml.
  2. Update the Replace element to be true:

     <config evaluator="string-compare"
          condition="CSRFPolicy" **replace="true"**> 
    
  3. Find all <action name="assertReferer"> elements and add the following child elements:

     <param name="referer">http://www.proxy1.com/.*|http://www.proxy2.com/.*</param>
    
  4. Find all <action name="assertOrigin"> elements and add the following child elements:

     <param name="origin">http://www.proxy1.com|http://www.proxy2.com</param>
    

The CSRF filter compares the incoming request with the rule request elements to find one that matches and then invokes the defined actions for that rule before normal Share processing begins.

How to set a blocking CSRF filter

If you want to completely block certain services in the repository, you can add these URLs to the CSRF filter:

  1. Copy the CSRFPolicy configuration in share-security-config.xml.
  2. Paste the configuration into your share-config-custom.xml file, ensuring that it is replacing the old configuration section:

     <config evaluator="string-compare"
          condition="CSRFPolicy" **replace="true"**> 
    
  3. Copy the following code and add it as the first child of the   element:

        
         <rule>
       <request>
          <path>/proxy/alfresco/acme/special/services/.*</path>
       </request>
       <action name="throwError">
          <param name="message">It is not allowed to access this url from your browser</param>
       </action>
    </rule>
              
    

Iframes and phishing attack mitigation

You can configure IFramePolicy to protect users against a phishing attack, which attempts to acquire information such as user names or passwords by simulating a trustworthy entity.

Alfresco Content Services allows you to control which domain pages or content are included in Alfresco Share to create a whitelist of allowed domains. A whitelist is a list of email addresses or IP addresses that are considered to be safe for use within your organisation.

This IFramePolicy is applied when Share includes an <iframe> tag while constructing the Web View dashlet. The dashlet will allow only those URLs that have been added to the whitelist. Developers can use the Alfresco.util.IFramePolicy.isUrlAllowed() method to check if a URL is allowed for custom implementations of a Web View or <iframe> tag is included.

Note: If you have a previous installation which includes a URL from a third-party domain, you will get an error message in your production environment prompting you to configure your IFramePolicy configuration by adding the domain to the whitelist.

Note: URLs pointing to the same domain, such as documents or wiki pages inside Share, will continue to work as usual by default.

The whitelist of allowed domains is set in the /classes/alfresco/share-security-config.xml configuration file:

<config evaluator="string-compare" condition="IFramePolicy">
 <same-domain>allow</same-domain>
  <cross-domain>
    <url>*</url>
  </cross-domain>
</config>

To deny URLs from the current domain, override the existing code in the share-config-custom.xml file with the following code:

<config evaluator="string-compare" condition="IFramePolicy" replace="true">
  <same-domain>deny</same-domain>
</config>

To allow all cross domain URLs, override the existing code in the share-config-custom.xml file with the following code:

<config evaluator="string-compare" condition="IFramePolicy" replace="true">
 <cross-domain>
   <url>*</url>
 </cross-domain>
</config>

To allow specific cross domain URLs, override the existing code in the share-config-custom.xml file with the following code:

<config evaluator="string-compare" condition="IFramePolicy" replace="true">
 <cross-domain>
   <url>https://www.owasp.org/</url>
 </cross-domain>
</config>

Security filters and clickjacking mitigation

You can configure a security filter, SecurityHeadersPolicy, that mitigates clickjacking attacks in Alfresco Share.

SecurityHeadersPolicy is a Java Servlet filter that applies HTTP response headers to incoming requests in Share. The headers that are returned are defined in a configuration section called SecurityHeadersPolicy in alfresco-security-config.xml.

Three headers are added by default; X-Frame-Options, X-Content-Type-Options and X-XSS-Protection:


<config evaluator="string-compare" condition="SecurityHeadersPolicy">
  <headers>
    <header>
      <name>**X-Frame-Options**</name>
      <value>SAMEORIGIN</value>
    </header>
    <header>
      <name>**X-Content-Type-Options**</name>
      <value>nosniff</value>
    </header>
    <header>
      <name>**X-XSS-Protection**</name>
      <value>1; mode=block</value>
    </header>
  </headers>
</config>

X-Frame-Options header

Adding this header to an HTTP response tells the browser whether Share pages are permitted inside iframes. In our default configuration we have set this to SAMEORIGIN which means that Share pages are only permitted inside iFrames inside Share or in other web applications that live under the same domain. For example, it is possible to include http://www.acme.com/share inside an iframe on http://www.acme.com/portal.

You can override the configuration and set the header to return DENY instead, by placing the following configuration in your share-config-custom.xml file:

<config evaluator="string-compare" condition="SecurityHeadersPolicy">
  <headers>
    <header>
      <name>X-Frame-Options</name>
      <value>**DENY**</value>
    </header>
  </headers>
</config>

X-Content-Type-Options

This header is valid for Internet Explorer (IE) only. Older versions of IE (8 and below) sniff the content of a returned resource and then execute the content as the content type that IE thinks the resource has, instead of the content type that the server returned. To stop IE from doing this, nosniff is returned in the header.

X-XSS-Protection

This header is provided by Internet Explorer (IE) to rectify “sanitization” logic that can be used by an attacker to introduce an XSS flaw on your site.

By default Share returns 1; mode=block for this header, which stops IE from executing sanitized code.

It is also possible to set the value to 0 which stops IE from inspecting the code for XSS attacks.

Adding other headers

Alfresco Content Services supports adding other headers to the configuration, for example, the Strict-Transport-Security header forces the browser to allow only https communication. This header is not provided by Share, but can be added by using this code:

<config evaluator="string-compare" condition="SecurityHeadersPolicy">
  <headers>
    <header>
      <name>**Strict-Transport-Security**</name>
      <value>**max-age=31536000**</value>
    </header>
  </headers>
</config>

Alfresco Repository Security policies and filters

You can configure filters in Alfresco Repository to mitigate security attacks when the Alfresco Content Services ReST API is accessed externally.

The Alfresco Content Services ReST API must be accessible on the network when the user inteface is implemented with the Alfresco Application Development Framework (ADF). The /alfresco URL path then need to be protected with a CSRF filter.

Cross-Site Request Forgery (CSRF) filters for Repository

You can configure the repository in Alfresco Content Services with a filter to prevent CSRF attacks that allow malicious requests to be unknowingly loaded by a user.

The CSRF filter can be configured in the web-client-security-config.xml file, which is located in the alfresco.war file (see tomcat/webapps/alfresco/WEB-INF/classes/alfresco/web-client-security-config.xml). To override values in this file, create a file called web-scripts-config-custom.xml in the tomcat/shared/classes/alfresco/extension directory with the updated configuration.

For example, to disable the CSRF filter all together, create the file and add the following configuration to it:

<alfresco-config>
    <config evaluator="string-compare" condition="CSRFPolicy" replace="true">
        <filter/>
    </config>
</alfresco-config>

Restart the server for the changes to take effect. There is no need to update the alfresco.war file.

If you want to configure the CSRF filter and allow a specific domain and port to access the server, then copy the original configuration from web-client-security-config.xml to web-scripts-config-custom.xml and then make necessary changes. Ensure that it is replacing the out-of-the-box configuration section:

 <config evaluator="string-compare"
      condition="CSRFPolicy" **replace="true"**> 

For example, when running Alfresco Content Services behind a reverse proxy, such as Nginx, and accessing Admin Console at http://localhost/alfresco/service/enterprise/admin, configure the CSRFPolicy filter as follows (add your rule as the first child of the   element):

<alfresco-config>
  <config evaluator="string-compare" condition="CSRFPolicy" replace="true">
    <session>true</session>

    <properties>
      <token>alf-csrftoken</token>
      <referer>http://localhost/.*</referer>
      <origin>http://localhost</origin>
    </properties>

    <client>
      <cookie>{token}</cookie>
      <header>{token}</header>
      <parameter>{token}</parameter>
    </client>

    <!-- The first rule with a matching request will get its action invoked, the remaining rules will be ignored. -->        
    <filter>
     <rule>
      <request>
        <method>POST</method>
        <path>/page/trusted/call/1|/page/trusted/call/2</path>
      </request>
      <action name="assertReferer">
        <param name="always">false</param>
        <param name="referer">{referer}</param>
      </action>
      <action name="assertOrigin">
        <param name="always">false</param>
        <param name="origin">{origin}</param>
      </action>
    </rule>

    <!-- Refresh token on each new page visit -->
    <rule>
      <request>
        <method>GET</method>
        <path>/service/enterprise/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
       </action>
    </rule>

    <rule>
      <request>
        <method>GET</method>
        <path>/s/enterprise/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>GET</method>
        <path>/wcservice/enterprise/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>GET</method>
        <path>/wcs/enterprise/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>GET</method>
        <path>/service/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>
  
    <rule>
      <request>
        <method>GET</method>
        <path>/s/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>GET</method>
        <path>/wcservice/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>GET</method>
        <path>/wcs/admin/.*</path>
      </request>
      <action name="generateToken">
        <param name="session">{token}</param>
        <param name="cookie">{token}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>POST</method>
        <header name="Content-Type">multipart/.+</header>
      </request>
      <action name="assertToken">
        <param name="session">{token}</param>
        <param name="parameter">{token}</param>
      </action>
      <action name="assertReferer">
        <param name="referer">{referer}</param>
      </action>
      <action name="assertOrigin">
        <param name="origin">{origin}</param>
      </action>
    </rule>

    <rule>
      <request>
        <method>POST|PUT|DELETE</method>
      </request>
      <action name="assertToken">
        <param name="session">{token}</param>
        <param name="header">{token}</param>
      </action>
      <action name="assertReferer">
        <param name="referer">{referer}</param>
      </action>
      <action name="assertOrigin">
        <param name="origin">{origin}</param>
      </action>
    </rule>
  </filter>
</config>
</alfresco-config>

Cross-Origin Resource Sharing (CORS) filters

Use this information to enable Cross-Origin Resource Sharing (CORS) in Alfresco Content Services, so that the repository accepts incoming requests from applications that are running on a different webserver.

  1. To enable CORS in Alfresco Content Services, set the filter and filter-mapping in <CATALINA_HOME>/webapps/alfresco/WEB-INF/web.xml:

    <filter>
        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
        <init-param>
            <param-name>cors.allowGenericHttpRequests</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.allowOrigin</param-name>
            <param-value>http://localhost:8081,http://myapp.example.com</param-value>
        </init-param>
        <init-param>
            <param-name>cors.allowSubdomains</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportedMethods</param-name>
            <param-value>GET, HEAD, POST, PUT, DELETE, OPTIONS</param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportedHeaders</param-name>
            <param-value>origin, authorization, x-file-size, x-file-name, content-type, accept, x-file-type, range</param-value>
        </init-param>
        <init-param>
            <param-name>cors.exposedHeaders</param-name>
            <param-value>Accept-Ranges, Content-Encoding, Content-Length, Content-Range</param-value>
        </init-param>
        <init-param>
            <param-name>cors.supportsCredentials</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
             <param-name>cors.maxAge</param-name>
             <param-value>3600</param-value>
        </init-param>
    </filter>
        
    <filter-mapping>
        <filter-name>CORS</filter-name>
        <url-pattern>/api/*</url-pattern>
        <url-pattern>/service/*</url-pattern>
        <url-pattern>/s/*</url-pattern>
        <url-pattern>/cmisbrowser/*</url-pattern>
        <url-pattern>/definitions/*</url-pattern>
    </filter-mapping>
    

    This will make CORS available only to certain origins (e.g. http://localhost:8081, http://myapp.example.com).

  2. For development, a wildcard can be used to allow all origins:

        <init-param>
            <param-name>cors.allowOrigin</param-name>
            <param-value>*</param-value>
        </init-param>
    

To disable CORS, comment out the CORS filter and filter-mapping settings.

Edit this page

Suggest an edit on GitHub
This website uses cookies in order to offer you the most relevant information. Please accept cookies for optimal performance. This documentation is subject to the Alfresco documentation terms.