WHAT'S IN THIS CHAPTER?
Configuring authentication
Working with users, groups, and zones
Configuring security
Implementing permissions
Implementing the ACEGI Spring security framework and services
This chapter discusses the authentication and security functionality built into Alfresco for user and group management, user authentication, permissions, and access control. In addition, this chapter provides instructions on how you can customize Alfresco for your own user base and needs using configurable modules for LDAP, NTLM, Kerberos, and other commonly used authentication protocols.
The first time you access a vanilla Alfresco installation through the Alfresco Explorer Web client, Alfresco identifies you as a guest user. You can identify yourself as another user by clicking the Login link and entering a new user name and password in the Login window. If you log in with the credentials of a user with administrator privileges (Alfresco uses admin
as the default user name and password), you can use the Administration Console to create additional users and assign them passwords.
In this out-of-the-box setup, you can manage the user base and their passwords manually from within Alfresco, and unauthenticated users still have limited access as the guest user.
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:
Disable unauthenticated guest access
Enable automatic sign-on using operating system credentials or a single sign-on (SSO) server to remove the need for a Login page
Delegate authentication responsibility to a central directory server to remove the need to set up users manually in the Administration Console
You will learn how to achieve these different levels of customization in various examples presented later in this chapter. To more fully understand the examples, you will first look at an overview of the Alfresco authentication subsystems.
The Alfresco authentication and identity management functionality is provided by a set of configurable software modules called subsystems. An authentication subsystem provides the following functions to Alfresco:
Password-based authentication for Web browsing, Microsoft SharePoint protocol, FTP, and WebDAV
CIFS and NFS file system authentication
Web browser, Microsoft SharePoint protocol, and WebDAV single sign-on (SSO)
User registry export (the automatic population of the Alfresco user and authority database)
A number of alternative authentication subsystem types exist for the most commonly used authentication protocols. These are each identified by a unique type name and summarized in Table 6-1.
Table 6.1. Authentication Subsystem Types
TYPE | DESCRIPTION | SINGLE SIGN-ON (SSO) | CIFS AUTHENTICATION | USER REGISTRY EXPORT? |
---|---|---|---|---|
| Native Alfresco authentication | Yes, NTLM | Yes | No |
| Authentication and user registry export through the LDAP protocol (for example, OpenLDAP) | No | No | Yes |
| Authentication and user registry export from Active Directory through the LDAP protocol | No | No | Yes |
| Authentication through a Windows domain server | Yes, NTLM | Yes | No |
| Authentication through a Kerberos realm | Yes, SPNEGO | Yes | No |
| Authentication through an external SSO mechanism | Yes | No | No |
The following sections show how these subsystem types enable you to tie Alfresco to some of the most widely used authentication infrastructures.
It is very likely that at least one of the authentication subsystem types previously discussed will allow you to integrate Alfresco with one of the authentication servers in use in your enterprise. However, integrating Alfresco with just one of these systems may not be enough. For various reasons, you might want to mix and match multiple authentication protocols against a collection of servers.
That is why Alfresco has a built-in authentication chain. In simple terms, this is a priority-ordered list of authentication subsystem instances. A subsystem instance is a configuration of one of the subsystem types. Each subsystem instance has:
A type
A unique name that makes it distinguishable from other instances of the same type
A set of property values provided by user configuration
The following sections demonstrate the authentication chain in use.
The examples in this section 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. All the examples adopt the following structured approach:
Decide the authentication chain composition (required subsystem types, instance names, order of precedence) and express this in alfresco-global.properties.
For each subsystem instance:
Locate the properties files for its subsystem type. These define the configurable properties for that subsystem type and their default values.
Create a folder named after the subsystem instance under the alfresco extension folders.
Copy the properties files into your new folder.
Edit the properties files to record the desired configuration of the subsystem instance.
An authentication chain containing a single subsystem instance of type alfrescoNtlm
provides the Alfresco default authentication behavior. This means that Alfresco performs all user account management and password validation. As implied by the type name, alfrescoNtlm
subsystem instances support automatic sign-on to internal Alfresco accounts through the NTLM protocol. But you do not have to use NTLM at all; in fact, it is turned off by default.
This example shows how to achieve two of the basic customizations using an instance of alfrescoNtlm
:
Disabling unauthenticated guest access
Enabling automatic sign-on
The first task is to declare your customized authentication chain to Alfresco. To do so, you must edit the configuration file alfresco-global.properties. In a default Alfresco installation, this is located in the following path:
<installLocation>sharedclassesalfresco-global.properties
You are still only relying on the internal capabilities of Alfresco, so you only need one alfrescoNtlm
subsystem instance in your authentication chain. In this case, the subsystem instance name is alfinst
. The name you choose does not really matter, as long as it is meaningful to you and unique within the authentication chain.
In alfresco-global.properties, add the following line:
authentication.chain=alfinst:alfrescoNtlm
Here you can see that the authentication.chain
property declares the authentication chain to Alfresco. Its value is a comma-separated list of authentication chain instances. Each instance is declared by an instance name, followed by a colon, and then followed by the subsystem type.
Now you will create the property files to configure your subsystem instance. First, you must create an appropriately named directory in the Alfresco extension location.
mkdir <installLocation
>sharedclassesalfrescoextensionsubsystems AuthenticationalfrescoNtlmalfinst cd /d <installLocation
>sharedclassesalfrescoextensionsubsystems AuthenticationalfrescoNtlmalfinst
Like other Alfresco configurations, the subsystem instance configuration lives in a directory below alfresco/extension in the application server's classpath. Below this path, subsystem configuration is further organized by category, type, and instance name. A subsystem category is a broad categorization given to a set of subsystem types. All authentication subsystem types have the category Authentication
. To be precise, the configuration for a particular subsystem instance of category sc
, type st
, and name sn
should be under a path alfresco/extension/subsystems/sc/st/sn
.
Now you will source the properties files that define the configurable properties of your instance.
copy <installLocation
>webappsalfrescoWEB-INFclassesalfrescosubsystems
AuthenticationalfrescoNtlm*.properties
As you can see, the default properties for a subsystem of category sc
, type st
, and name sn
are under alfresco/subsystems/sc/st/*.properties
in the Alfresco WAR file.
Never edit the properties files in the WAR file or under the Tomcat webapps directory. Always create your own copies in the extension classpath as shown in this example. Otherwise, any customizations you make would be lost whenever you upgraded Alfresco.
Two separate properties files appear in your alfinst directory after running one of the previous commands. These are:
alfresco-authentication.properties
ntlm-filter.properties
This demonstrates how the properties of a subsystem may be spread across multiple properties files. The number of files and their names do not matter, as long as they end with the suffix .properties. For the alfrescoNtlm
subsystem type, alfresco-authentication.properties contains properties relating to core authentication capabilities, whereas ntlm-filter.properties groups together those relating to automatic sign-on.
Now you are set to configure your alfinst
authentication subsystem instance.
To disable unauthenticated guest access, open the alfresco-authentication.properties file from the alfinst directory in a text editor and locate the following line:
alfresco.authentication.allowGuestLogin=true
Edit this line to disable guest access; for example:
alfresco.authentication.allowGuestLogin=false
To activate NTLM-based single sign-on (SSO), open the ntlm-filter.properties file from the alfinst directory in a text editor, and locate the following line:
ntlm.authentication.sso.enabled=false
Edit this line to enable SSO; for example:
ntlm.authentication.sso.enabled=true
Start or restart the Alfresco server. If you enter the Alfresco Explorer URL http://localhost:8080/alfresco/
in your browser, you should find that the guest home page does not appear, as this has been disabled. Instead, a browser authentication window appears. Although you enabled NTLM-based sign-on, this happens because your browser cannot log you in automatically as there is not yet an account in Alfresco whose credentials match your operating system credentials.
To remedy this, log in as the admin user (using admin
as the user name and password). Use the Administration Console link at the top of the home page, and create a user with a user name and password that matches those of your operating system account. Close and restart your browser and try accessing Alfresco again. If your browser supports NTLM and its security settings allow, it will automatically log you in using your operating system account name.
This rather simplistic demonstration of SSO still involved the manual creation of users in Alfresco and the duplication of password information in two systems. To achieve truly enterprise-grade authentication, you will have to use some of the different subsystem types, as demonstrated in Example 2.
This example addresses the more advanced goal of delegating authentication responsibility to a centralized directory server. Most organizations maintain their user database in a directory server supporting the LDAP protocol, such as Active Directory or OpenLDAP. When integrated with an LDAP server, Alfresco can delegate both the password checking and account setup to the LDAP server, thus opening up Alfresco to your entire enterprise. This avoids the need for an administrator to manually set up user accounts or to store passwords outside of the directory server.
To integrate Alfresco with a directory server, you simply need to include an instance of the ldap
or ldap-ad
subsystem types in the authentication chain. Both subsystem types offer exactly the same capabilities and should work with virtually any directory server supporting the LDAP protocol. Their only differences are the default values configured for their attributes. The ldap
type is preconfigured with defaults appropriate for OpenLDAP, whereas ldap-ad
is preconfigured with defaults appropriate for Active Directory.
This example uses an Active Directory server and therefore will configure in an instance of the ldap-ad
subsystem.
You have two choices in this scenario. You can replace or add to the authentication chain.
Replace the authentication chain.
You could remove alfinst
from the previous example and instead add an instance of ldap-ad
. This would hand over all authentication responsibility to Active Directory and would mean that the built-in accounts, such as admin and guest, could not be used.
In this scenario, it would be important to configure at least one user who exists in Active Directory as an administrator and enable the guest account in Active Directory if guest access were required. Furthermore, because ldap-ad
cannot support CIFS authentication (as it requires an MD5 password hash exchange), it would rule out use of the CIFS server for all users and the CIFS server would be disabled.
Add to the authentication chain.
You could instead supplement the existing capabilities of alfinst
by inserting an ldap-ad
instance before or after alfinst
in the chain. This means that you could use the built-in accounts alongside those accounts in the directory server. Furthermore, the built-in accounts could access Alfresco through the CIFS server, since alfrescoNtlm
is able to drive CIFS authentication.
In this scenario, where you chose to position your ldap-ad
instance in the chain determines how overlaps or collisions between user accounts are resolved. If an admin account existed in both Alfresco and Active Directory, then admin would be Alfresco if alfinst
came first, or Active Directory if the ldap-ad
instance came first.
This example uses the second option to append an instance of ldap-ad
to the authentication chain. This instance name is ldap1
and is declared by changing the authentication.chain
property in alfresco-global.properties as follows:
authentication.chain=alfinst:alfrescoNtlm,ldap1:ldap-ad
First, you will undo a previous modification to alfinst
and disable NTLM-based SSO. This is done because the ldap-ad
and ldap
subsystem types cannot participate in the NTLM handshake; therefore, leaving SSO enabled would prevent any of the Active Directory users from logging in. You will see how to get around this in the next example.
For now, disable SSO by opening the ntlm-filter.properties file in the alfinst directory in a text editor, and editing the property ntlm.authentication.sso.enabled
as follows:
ntlm.authentication.sso.enabled=false
Next, create the properties files to configure ldap1
:
mkdir <installLocation
>sharedclassesalfrescoextensionsubsystems Authenticationldap-adldap1 cd /d <installLocation
>sharedclassesalfrescoextensionsubsystems Authenticationldap-adldap1 copy <installLocation
>webappsalfrescoWEB-INFclassesalfrescosubsystems Authenticationldap-ad*.properties
A single file called ldap-ad-authentication.properties now appears in your ldap1 directory. You can now edit this file to define your LDAP setup.
When you open ldap-ad-authentication.properties, the large number of configurable properties may alarm you. This demonstrates the flexibility of the Alfresco LDAP infrastructure. Luckily, because ldap-ad
already has sensible defaults configured for a typical Active Directory setup, there are only a few edits you must make to tailor the subsystem instance to your needs.
The following lines show the set of properties you will typically need to edit and how you might set them for a domain controller for a fictitious domain called domain.com
.
ldap.authentication.allowGuestLogin=false ldap.authentication.userNameFormat=%[email protected] ldap.authentication.java.naming.provider.url=ldap://domaincontroller.domain.com:389 ldap.authentication.defaultAdministratorUserNames=Administrator,alfresco ldap.synchronization.java.naming.security.principal=alfresco@domain.com ldap.synchronization.java.naming.security.credentials=secret ldap.synchronization.groupSearchBase=ou=Security Groups,ou=Alfresco ,dc=domain,dc=com ldap.synchronization.userSearchBase=ou=User Accounts,ou=Alfresco,dc=domain,dc=com
Here is a brief description of the settings that have been changed:
ldap.authentication.allowGuestLogin
— Enables/disables unauthenticated access to Alfresco.
ldap.authentication.userNameFormat
— A template that defines how Alfresco user IDs are expanded into Active Directory User Principal Names (UPNs) containing a placeholder %s
, which stands for the unexpanded user ID. A UPN generally consists of the user's account ID followed by an @
sign and then the domain's UPN suffix. You can check the appropriate UPN suffix for your domain by connecting to the directory with an LDAP browser, browsing to a user account, and looking at the value of the userPrincipalName
attribute.
ldap.authentication.java.naming.provider.url
— An LDAP URL containing the host name and LDAP port number (usually 389) of your Active Directory server.
ldap.authentication.defaultAdministratorUserNames
— A list of user IDs who should be given Alfresco administrator privileges by default. Another administrator can include more users as administrators by adding those users to the ALFRESCO_ADMINISTRATORS group.
ldap.synchronization.java.naming.security.principal
— The UPN for an account with privileges to see all users and groups. This account is used by Alfresco to retrieve the details of all users and groups in the directory so that it can synchronize its internal user and authority database. Passwords are never compromised and remain in the directory server.
ldap.synchronization.java.naming.security.credentials
— The password for the previous account.
ldap.synchronization.groupSearchBase
— The Distinguished Name (DN) of the Organizational Unit (OU) below which security groups can be found. You can determine the appropriate DN by browsing to security groups in an LDAP browser.
ldap.synchronization.userSearchBase
— The Distinguished name (DN) of the Organizational Unit (OU) below which user accounts can be found. You can determine the appropriate DN by browsing to user accounts in an LDAP browser.
Restart the Alfresco server. If you watch the output from Tomcat in alfresco.log in the installation directory, you will eventually see lines similar to the following:
13:01:31,225 INFO [org.alfresco.repo.management.subsystems.ChildApplicationContextFactory] Starting 'Synchronization' subsystem, ID: [Synchronization, default] ... 13:01:49,084 INFO [org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizer] Finished synchronizing users and groups with user registry 'ldap1'
13:01:49,084 INFO [org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizer] 177 user(s) and 19 group(s) processed 13:01:49,131 INFO [org.alfresco.repo.management.subsystems.ChildApplicationContextFactory] Startup of 'Synchronization' subsystem, ID: [Synchronization, default] complete
What you are seeing is output is from the Synchronization
subsystem. This is another Alfresco subsystem responsible for synchronizing the Alfresco internal user and authority database with all user registries in the authentication chain. Since the authentication chain now provides a user registry, the Synchronization
subsystem has some work to do when Alfresco starts up.
From the previous logs, notice that the Synchronization
subsystem automatically created 177 users and 19 groups using attributes, such as email address and group memberships, retrieved from Active Directory through an LDAP query. This has eliminated a lot of work for the admin user!
The Synchronization subsystem uses an incremental timestamp-based synchronization strategy, meaning that it only queries for changes since the last synchronization run. So after the first startup, further synchronization runs can be almost instantaneous. Because synchronization runs are also triggered by a scheduled nightly job, whenever an unknown user successfully authenticates you should find that Alfresco stays synchronized with hardly any effort.
Now, if you enter the Alfresco Explorer URL http://localhost:8080/alfresco/into
your browser, you can log in using the ID and password of any of the Active Directory users.
Passwords are validated through an LDAP bind operation on Active Directory in real time. Passwords for Active Directory users are not stored locally.
If you navigate to a user profile, notice that attributes such as email address were populated automatically from Active Directory.
You are somewhat closer now to the ideal of delegating authentication responsibility to Active Directory, but you still do not have the automatic sign-on and CIFS browsing capabilities that internal Alfresco users enjoyed in the first example. Is there anything more you can do? The next example demonstrates this.
In Example 2, you saw that the authentication capabilities offered by the ldap-ad
subsystem type were not capable of supporting CIFS and NTLM authentication. Instead, you had to settle for form-based login for all users, and only Alfresco internal users could access CIFS. This is the compromise you would have to make if the directory server did not support any other authentication protocol. But for Active Directory, which also supports NTLM and Kerberos authentication, you can overcome this limitation by using either the passthru
or the kerberos
subsystem types.
As passthru
is simpler to set up, this is the one used for this example. The passthru
subsystem supports SSO, CIFS, and password authentication against a Windows domain server using the NTLM v1 protocol. Many prefer Kerberos for its enhanced security and you should certainly consider it as an alternative.
Append an instance of passthru
to the authentication chain for this example. Call the instance passthru1
and declare it by changing the authentication.chain
property in alfresco-global.properties as follows:
authentication.chain=alfinst:alfrescoNtlm,ldap1:ldap-ad,passthru1:passthru
First, ensure that CIFS authentication is no longer targeted at your internal alfrescoNtlm
subsystem instance, alfinst
.
Open the alfresco-authentication.properties file in the alfinst directory in a text editor, and edit the alfresco.authentication.authenticateCIFS
property as follows:
alfresco.authentication.authenticateCIFS=false
Functions such as NTLM SSO and CIFS authentication can only be targeted at a single subsystem instance in the authentication chain. This is a restriction imposed by the authentication protocols themselves. For this reason, Alfresco targets these "direct" authentication functions at the first member of the authentication chain that has them enabled. By disabling CIFS in alfinst
earlier, passthru1
has a chance to handle CIFS authentication for its larger user base. SSO is also left disabled in alfinst
, which means that you can enable it in passthru1
.
Next, stop ldap1
from performing authentication. You can leave that to passthru1
, which will be authenticating against the same server using more secure protocols. This leaves the ldap1
user registry export capabilities active, which you still rely on for account synchronization.
Edit the ldap.authentication.active
property in the ldap-ad-authentication.properties file located in your ldap1 directory as follows:
ldap.authentication.active=false
Finally, create the properties files to configure passthru1
.
mkdir <installLocation
>sharedclassesalfrescoextensionsubsystems
Authenticationpassthrupassthru1
cd /d <installLocation
>sharedclassesalfrescoextensionsubsystems Authenticationpassthrupassthru1 copy <installLocation
>webappsalfrescoWEB-INFclassesalfrescosubsystems Authenticationpassthru*.properties
After running the previous commands, two separate properties files should appear in your passthru1 directory. These are:
passthru-authentication-context.properties
ntlm-filter.properties
Using a similar distinction to the alfrescoNtlm
subsystem type, passthru-authentication-context.properties contains properties relating to core authentication capabilities, whereas ntlm-filter.properties groups those properties relating to automatic sign-on. Unlike the alfrescoNtlm
subsystem type, SSO is enabled by default in passthru
subsystems so there is no need to edit ntlm-filter.properties.
The following lines show the set of properties you typically need to edit and how they might be set for a domain controller for the fictitious domain domain.com
.
passthru.authentication.servers=DOMAIN\domaincontroller.domain.com ,domaincontroller.com passthru.authentication.domain=# Leave blank passthru.authentication.guestAccess=false passthru.authentication.defaultAdministratorUserNames=Administrator,alfresco
Here is a brief description of the settings that have changed:
passthru.authentication.servers
— A comma-separated list of domain controller host names, each prefixed by the name of the domain they correspond to and a double backslash. The last member of the list is a host name without a domain prefix, and this host will be used when a client does not include a domain name in an authentication request.
passthru.authentication.domain
— This property is a less-reliable alternative to passthru.authentication.servers and should be left empty.
passthru.authentication.defaultAdministratorUserNames
— A list of user IDs who should be given Alfresco administrator privileges by default. Additional users can be made administrators by another administrator if they add those users to the ALFRESCO_ADMINISTRATORS group.
Restart the Alfresco server. The main differences to notice from last time are:
All Active Directory users can point their browser to the Alfresco server and be signed on automatically. In Internet Explorer, this requires adding the Alfresco server to the Local Intranet security zone.
All Active Directory users can access Alfresco as a CIFS file system using their Active Directory credentials.
These examples have demonstrated the flexibility and power of an Alfresco authentication chain. You can combine the strengths of a variety of different authentication protocols and keep the Alfresco user database synchronized almost transparently.
Authentication is concerned with validating that a user or principal is who or what they claim to be. Alfresco normally refers to users. 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 includes an internal, password-based, authentication implementation; the support to integrate with many external authentication environments; the option to write your own authentication integration; and the ability to use several of these options simultaneously. Alfresco 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).
Alfresco 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 may execute the operation based on the permissions that have been assigned directly to the user or indirectly through a group. An operation in Alfresco 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 may 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 may 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 alfrescomodelpermissionDefinitions.xml --> <globalPermission permission="Read" authority="GROUP_EVERYONE" />
Code snippet GlobalRead.xml
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 may 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.
Authorities are people (or persons) or groups. A group may 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 of which the user is a direct or indirect member, and any appropriate dynamic authorities. Dynamic authorities are used for internal roles.
When logging in, Alfresco validates the user's identifier and password. Alfresco employs the user's 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 may differ in case from the user identifier presented to the authentication system. After the Person
node look-up, Alfresco 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 in Alfresco. Person nodes may be:
Explicitly created
Created on demand with some default entries
Created from LDAP synchronization
Person nodes are explicitly created when using the administration pages of the Alfresco Explorer and Alfresco Share Web clients to manage users.
By default, person nodes will be auto-created if not present. If an external authentication system is configured, such as NTLM, when any user authenticates, an appropriate person node may 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 and LDAP synchronization, creates person nodes on demand, and uses case-insensitive authentication. For example, Andy could log in as "Andy" and the associated Person
node would be created with the userName
"Andy." Later, the LDAP synchronization runs and changes the userName
to "andy." From version 3.2, changes to Person
node userName
s will cause updates to other related data in Alfresco, such as ACL assignment.
Groups are collections of authorities with a name and display name. As such, groups may include other groups or people. You may include a group in one or more other groups, as long as this inclusion does not create any cyclic relationships.
All person and group nodes are in one or more zones. You can use zones for any partitioning of authorities. For example, Alfresco synchronization uses zones to record from which LDAP server users and groups have been synchronized. Zones have been used to hide some groups that provide Role Based Access Control (RBAC) role-like functionality from the administration pages of the Alfresco Explorer and Alfresco Share Web clients. Examples of hidden groups are the roles used in Alfresco Share and Records Management (RM). 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 the Alfresco Explorer and Alfresco Share Web clients.
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.
Figure 6-1 shows the model used for persisting people, groups, and zones in Alfresco. Each person is represented by a Person
node and groups are represented by an AuthorityContainer
, 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, other than the default, are used to hide groups that implement RBAC like roles. Application zones, by convention, start with APP. and include the following:
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 may be held within Alfresco 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 and not synchronized from an external source. This is the default zone for authentication.
AUTH.EXT.<ID> is for authorities defined externally, such as in LDAP.
Alfresco uses some custom roles. To implement a custom role, you create a dynamic authority for that role and assign global permissions to it. The Alfresco internal roles have not been assigned any object-specific rights. The internal roles are as follows:
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 checkout, or unlock the node.
The Alfresco Web clients support the assignment of permissions only to the owner role. You can use such things as the Java API and scripting to make other assignments.
Hierarchical and zoned roles may be added to Alfresco in the future to avoid the hidden group implementation for true roles.
Permissions and their groupings are defined in an XML configuration file. The default file is found in the distribution configuration directory as <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescomodelpermissionDefinitions.xml. This configuration can be replaced or extended and has a structure as described in <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescomodelpermissionSchema.dtd.
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 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>
Code snippet OwnablePermissions.xml
As you can see in the preceding code, 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. They may be exposed in the administration pages of the Alfresco Explorer and Alfresco Share Web clients but, by convention, are not. A permission, in its definition, may be granted to any number of permission groups. This means that those permission groups will include the permission. The permission may 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 may also require other permissions be tested on the same node, its children, or its parent. In the preceding 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 read only the nodes for which you can read all the parents; neither are normally required in Alfresco. 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 DeleteNode.xml 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>
Code snippet _DeleteNode.xml
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 may be exposed by the administration pages of the Alfresco Explorer and Alfresco Share Web clients and may require the presence of a type or aspect to apply to a particular node. In addition, a permission group may allow full control, which grants all permissions and permission groups. As a type or aspect may 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 Explorer and 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"> <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>
Code Snippet ReplacePermissionModel.xml
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>
Code Snippet ExtendPermissionModel.xml
An Access Control List (ACL) is an ordered list of Access Control Entries (ACEs). 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 but not via the administration pages of the Alfresco Explorer and Alfresco Share Web clients.
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 via 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. See the example in Figure 6-2, the ACL descriptions in Table 6-2, and the discussion in the section titled "An ACL Example."
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.
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 2, and so on. For a SHARED
ACL, ACEs defined on the ACL from which it inherits will have position 1.
When Alfresco makes permission checks, ACEs are considered in order, with the lowest position first. Deny entries take precedence over allow entries at the same position. The default configuration is that "any allow allows." 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 via another group or directly via 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 the Alfresco Explorer and Alfresco Share Web clients do not expose deny.
You can alter the configuration to support "any deny denies."
This example relates a tree of nodes to two corresponding trees of ACLs, all shown in Figure 6-2. 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
ALCs are shown as clear. Under each node on the node tree, the related ACL is referenced.
Table 6-2 describes the ACEs in each ACL and their position.
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.
The GROUP_ prefix is normally hidden by the administration pages of the Alfresco Explorer and Alfresco Share Web clients.
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
.
Table 6.2. ACL Formats
ACL FORMAT | AUTHORITY | PERMISSION | ALLOW/DENY | POSITION |
---|---|---|---|---|
ACL A ( | All |
| Allow | 0 |
ACL B ( | All |
| Allow | 1 |
ACL C ( | All |
| Allow | 2 |
ROLE_OWNER | All | Allow | 0 | |
GROUP_A |
| Allow | 0 | |
GROUP_A |
| Allow | 0 | |
ACL D ( | All |
| Allow | 3 |
ROLE_OWNER | All | Allow | 1 | |
GROUP_A |
| Allow | 1 | |
GROUP_A |
| Allow | 1 | |
ACL E ( | All |
| Allow | 2 |
Andy | All | Allow | 0 | |
Bob |
| Allow | 0 | |
Bob |
| Deny | 0 | |
ACL F ( | All |
| Allow | 3 |
Andy | All | Allow | 1 | |
Bob |
| Allow | 1 | |
Bob |
| Deny | 1 | |
ACL G ( | Bob | All | Allow | 0 |
ACL H ( | Bob | All | Allow | 1 |
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 were created beneath node 13 or 14, it would inherit ACL H. If a new node were 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 cleanup of no-op defining ACLs.
Security is enforced around public services. Web services, Web scripts, Alfresco Explorer and Alfresco Share Web clients, CIFS, WebDAV, FTP, CMIS, and more all use public services, so include security enforcement. Public services are defined in <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescopublic-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.
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 discussed elsewhere. 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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescopublic-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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescopublic-services-context.xml. The public services are the only Spring beans to have access control.
The beans required to support Spring ACEGI-based security around method invocation are defined in <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescopublic-services-security-context.xml. This configures two Alfresco-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. Method access is defined in the normal ACEGI manner with some additions.
For the following information detailing pre-conditions and post-conditions, these factors are all relevant:
<authority> — Represents an authority (user name or group)
<#> — Represents a method argument index
<permission> — Represents the string representation of a permission
Pre-conditions take one of the following forms:
ACL_METHOD.<authority> — Restricts access to the method to those with the given authority in Alfresco. This could be a user name or group. Dynamic authorities are not supported.
ACL_NODE.<#>.<permission> — 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.<#>.<permission> — 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_
If more than one ACL_NODE.<#>.<permission>, ACL_PARENT.<#>.<permission>, or ACL_METHOD.<permission> entry is present, then all of the ACL_NODE and ACL_PARENT permissions must be present as well as any one of the ACL_METHOD restrictions, if present, for the method to execute.
Post-conditions take the forms:
AFTER_ACL_NODE.<permission> — Similar to ACL_NODE.<#>.<permission> but the restriction applies to the return argument
AFTER_ACL_PARENT.<permission> — Similar to ACL_PARENT.<#>.<permission> 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>
Code Snippet OwnableServiceSecurity.xml
Here, 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 may have all rights to a node via 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.
Modifying access control may involve:
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 may 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
, may not behave as expected or may 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 individual properties are not 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 child 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; do not check 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. In any case, the access restrictions for public service methods may 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 may still fail if the conditions for the returned object are not met or some security configuration has changed, such as an ACE has been removed, a user has been removed from a group, or the method has failed for a non-authorization reason.
If you are coming from an RBAC background, Alfresco has roles in the RBAC sense only for limited internal use. To implement RBAC in Alfresco, use zoned groups. These groups will not appear in the administration pages of the Alfresco Explorer and Alfresco Share Web clients 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 in Alfresco to support roles in Alfresco Share and records management. Here is how RBAC terminology maps to Alfresco: 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 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 may assign. The Alfresco 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.
The access control model described so far is used for all nodes in the content repository except those related to the Records Management extension. Records Management is used as an example here to outline how to extend access control.
The Records Management authorization is based on a fixed set of capabilities that are part of the DOD 5015.2 specification. These capabilities describe records management operations for which there is not a direct mapping to an Alfresco public service method call. There are separate Records Management implementations of the ACEGI AccessDecisionVoter
and AfterInvocationProvider
interfaces to support this mapping. The AccessDecisionVoter
allows or denies access on method entry. The AfterInvocationProvider
allows or denies access based on the method return value; it can also alter the return value. All Records Management nodes carry a marker aspect (an aspect that defines no properties or associations). If this marker is present, the default voter will abstain; if this marker is absent, the Records Management voter will abstain.
Public services are protected for Records Management in the same manner as already described but with two sets of configuration: one for each of the two different implementations. It is more complex to map the Records Management capabilities and caveats (for example, security clearance) to public service method calls and to enforce the restrictions. For example, the node service updateProperties
method has to incorporate the idea of updating declared and undeclared records, allow updates to selected properties, and restrict access to some properties that should be updated only as part of state management. The Records Management voter has additional Records Management hard-coded policies to protect the public services in order to encapsulate the logic for this and related use cases.
In Records Management, normal users cannot pass on their rights to other users.
There are four key services involved in access control: the PersonService
, the AuthorityService
; the PermissionService
, and the OwnableService
. The PersonService
and the AuthorityService
are responsible for managing authorities. The PermissionService
is responsible for managing ACLs and ACEs and for checking if a user has been assigned a permission for a particular node. The OwnableService
manages object ownership and is used in evaluation the dynamic ROLE_OWNER
authority.
The protection of public services methods is implemented using Spring method interceptors defined as part of the related ACEGI 0.8.2 security package. The Alfresco 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.<#>.<permission>). These extension classes make use of the four key services.
The PersonService
interface is the API by which nodes of the person type, as defined in <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescomodelcontentModel.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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescoauthentication-services-context.xml. The principle configuration options are around how people are created on demand if users are managed via NTLM or some other external user repository.
The AuthorityService
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 authority service does not support user authentication or user management. This is done by the AuthenticationService. Person
nodes are managed via the PersonService
.
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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescoauthority-services-context.xml.
The PermissionService
is responsible for all of the following:
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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescopublic-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 may 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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescomodelpermissionDefinitions.xml. This configuration is loaded in a bean definition in <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescopublic-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.
The idea of file ownership is present in both UNIX and Windows. In Alfresco, the repository has the concept of node ownership. This ownership is optional and is implemented as an aspect.
The owner of a node may 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 <installLocation> omcatwebappsalfrescoWEB-INFclassesalfrescomodelcontentModel.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
.