Adobe Experience Manager is designed to cater for content authoring of multiple sites by multiple content authors. Naturally, this process needs to be governed by strict Access Control Lists (ACLs) to manage who is allowed to do what at any given time. In this post, I’ll cover various approaches that can be used to manage authorizables and ACLs in AEM that should help you make a more informed decision when picking a permissions management strategy for your next project.
Basics of Roles and Permissions
Before we get started, we need to cover some basic terminology of permissions management in AEM. Throughout this post, I will refer to Users, Groups, Authorizables and Permissions quite a lot so let’s make sure we’re all on the same page.
A user is a unique account that is used to log in to the system and holds basic details such as name, password, email, etc. They may be part of multiple groups and can also hold their own privileges (although that’s not recommended).
A group is a collection of users whose primary purpose is to apply access rights to those users based on a particular role. AEM comes with a set of out-of-the-box groups but it’s suggested to create groups to fit a given organisation’s content authoring processes.
Permissions are used to identify who is allowed to do what on a given resource and are the result of evaluating Access Control Lists.
Now that we’re covered that off, we can get to the crux of this post and evaluate different ways of creating users, groups and their associated permissions. I like to always keep in mind maintainability, scalability and ease of use when assessing these sorts of strategies.
The ACL and Authorizable Packagers
Several companies use a combination of ACS Commons’ pre-defined package definitions (a.k.a Packagers) to manage their groups and ACLs. More specifically, the ACL Packager and the Authorizable Packager. This strategy is great to get something up and running quickly to manage roles and permissions. However, once the original packages are created from an accepted source of truth (usually production), they can become quite difficult to manage.
More often than not, permissions are tweaked to get a new feature working in a development environment and are never documented. This means the required permission changes never make their way to production due to a lack of communication. Add to this multiple development and staging environments and you can imagine how unwieldy this process becomes.
Moreover, content tends to differ between instances, which means package definitions may exist for content paths that do not exist. Due to the inflexible nature of content packages, this results in invalid content pages being created to inject policy nodes as depicted by the screenshots below.
Another issue with content packages is that the existing ACL entries on the target system remain untouched so there is never any clean-up of obsolete groups. The uncertainty in the contents of those packages leads teams to enter a stage where changing the package definition or re-building the package is avoided at all cost. For this approach to work, teams need to put in place strict role/permission change policies, document the roles and permissions, and communicate effectively any necessary changes so they can be properly managed.
Access Control Tool
An alternative approach that I have explored in the last year or so uses an excellent tool developed by Netcentric called AC Tool (a.k.a Access Control Tool). It allows developers to specify roles and permissions using YAML configuration files. The config is deployed as part of a standard CRX package and picked up by an installation hook which creates the relevant groups, ACLs and users.
What I love about this approach is that it’s self documenting, provides version history (as it’s managed through code) and ensures permissions are aligned across environments, because it cleans up existing ACLs of all roles managed by AC Tool.
The great thing about adopting AC Tool is that it forces you to think carefully about how you architect your roles and permissions, thereby allowing your implementation to scale to multiple sites and roles with ease. Unlike traditional approaches where out of the box “blanket” rules are re-used, it encourages you to take a closer look at which roles are allowed to perform which actions, thereby hardening your platform.
To get started with AC Tool, I find it best to study their recommended best practice structure, use their real project example and customise it to your liking from there.
Something worth noting is that AC Tool encourages permissions to be broken up into reusable “fragments” to be used as building blocks. There is nothing special about fragments, they are just like normal groups that contain the minimum number of entries necessary to achieve the desired outcome.
A file organisation that has worked well for me is (please note that this example is not complete for readability purposes):
/apps/acls
/config
/fragments-content.yaml
/fragments-functional.yaml
/fragments-global.yaml
/global.yaml
/obsolete.yaml
/roles.yaml
/config.dev
/users-acme.yaml
global.yaml
This file is used to set global AC Tool properties like the minimum version required or how to handle relationships with groups managed outside of the tool.
This file is used to set global AC Tool properties like the minimum version required or how to handle relationships with groups managed outside of the tool.
- global_config:
minRequiredVersion: 2.0.1
obsolete.yaml
This configuration contains authorizables that should be cleaned up from a target system on install. This is handy for cleaning up legacy users and groups that are not applicable with the new configuration.
This configuration contains authorizables that should be cleaned up from a target system on install. This is handy for cleaning up legacy users and groups that are not applicable with the new configuration.
- obsolete_authorizables:
- some-legacy-group
fragments-global.yaml
This configuration contains the global permission fragments that are used to form the basis of the other functional fragments. This permissions in this file should be customised based on your requirements but can largely be based off the Netcentric’s real world example. Note: The “DEF” keyword defines a variable to be used within the configuration file.
This configuration contains the global permission fragments that are used to form the basis of the other functional fragments. This permissions in this file should be customised based on your requirements but can largely be based off the Netcentric’s real world example. Note: The “DEF” keyword defines a variable to be used within the configuration file.
- DEF fragmentPath="global/fragments"
- group_config:
- fragment-basic-allow:
- name:
memberOf:
path: ${fragmentPath}
- fragment-restrict-for-everyone:
- name:
memberOf:
path: ${fragmentPath}
- ace_config:
- fragment-basic-allow:
- path: /
permission: allow
actions: read
privileges:
repGlob:
- path: /etc
permission: allow
actions:
privileges: jcr:read,jcr:readAccessControl
repGlob: ""
- fragment-restrict-for-everyone:
- path: /
permission: allow
actions: read
privileges:
repGlob:
- path: /content
permission: deny
actions:
privileges: jcr:read,jcr:readAccessControl
repGlob:
When using the Netcentric example, it’s worth noting that the configured deny ACLs may not apply to your version of AEM as navigation paths may have changed.
fragments-functional.yaml
This configuration contains only the permissions necessary to access various UI functionality not specific to any project. For example, having the ability to access the DAM view but not the ability to modify the contents of the DAM.
Breaking permissions up between content and functionality allows for greater reuse and flexibility when making up roles for the organisation. I recommend creating generic functional fragments (i.e. tagging, commerce, user admin, etc) and then creating role fragments that might make up one or more functional fragments (i.e. contentmanager, poweruser).
This configuration contains only the permissions necessary to access various UI functionality not specific to any project. For example, having the ability to access the DAM view but not the ability to modify the contents of the DAM.
Breaking permissions up between content and functionality allows for greater reuse and flexibility when making up roles for the organisation. I recommend creating generic functional fragments (i.e. tagging, commerce, user admin, etc) and then creating role fragments that might make up one or more functional fragments (i.e. contentmanager, poweruser).
- DEF fragmentPath="global/fragments"
- group_config:
- fragment-siteadmin:
- name:
memberOf:
path: ${fragmentPath}
- fragment-dam:
- name:
memberOf:
path: ${fragmentPath}
- ace_config:
- fragment-siteadmin:
- path: /libs/wcm/core/content/siteadmin
permission: allow
actions: read
privileges:
repGlob:
- path: /libs/cq/core/content/nav/sites
permission: allow
actions: read
privileges:
repGlob:
- fragment-dam:
- path: /libs/wcm/core/content/damadmin
permission: allow
actions: read
privileges:
repGlob:
- path: /libs/cq/core/content/nav/assets
permission: allow
actions: read
privileges:
repGlob:
fragments-content.yamlThis configuration contains only the permissions necessary to access various content specific to a particular site or business unit. Note: the “FOR-IN” is a loop syntax to iterate through a set of values specified in the array.
- group_config:
- FOR brand IN [ brandA, brandB, brandC ]:
- content-${brand}:
- name:
memberOf:
path: ${brand}/fragments
- dam-${brand}:
- name:
memberOf:
path: ${brand}/fragments
- ace_config:
- FOR brand IN [ brandA, brandB, brandC ]:
- content-${brand}:
- path: /content/${brand}
permission: allow
actions: read,modify,create,delete,acl_read,replicate
privileges:
repGlob:
- dam-${brand}:
- path: /content/dam/${brand}
permission: allow
actions: read,modify,create,delete,acl_read,replicate
privileges:
repGlob:
roles.yaml
This configuration is used to create the various roles that users will be assigned to. It’s in this file that functional fragments will be matched up with content fragments. In other words, it’s the location where the ability to access a feature in AEM will be matched up with the ability to modify project content. It’s also a situation where looping through the managed brands is very useful (provided that each project has similar authoring requirements).
This configuration is used to create the various roles that users will be assigned to. It’s in this file that functional fragments will be matched up with content fragments. In other words, it’s the location where the ability to access a feature in AEM will be matched up with the ability to modify project content. It’s also a situation where looping through the managed brands is very useful (provided that each project has similar authoring requirements).
- group_config:
- FOR brand IN [ brandA, brandB, brandC ]:
- ${brand}-authors:
- name:
memberOf: fragment-contentmanager,content-${brand},dam-${brand}
path: ${brand}
- ${brand}-publishers:
- name:
memberOf: fragment-poweruser,content-${brand},dam-${brand}
path: ${brand}
users-acme.yaml
While this is purely optional, I find it useful to setup test users in development environments especially when dealing with workflows. As a developer, it removes the hassle of creating the user(s) manually and it’s always good to check that a feature being developed works with the intended end user (instead of working with admin rights).
While this is purely optional, I find it useful to setup test users in development environments especially when dealing with workflows. As a developer, it removes the hassle of creating the user(s) manually and it’s always good to check that a feature being developed works with the intended end user (instead of working with admin rights).
- DEF basePath="test"
- user_config:
- test-author:
- name: test-author
memberOf: brandA-authors
password: password
path: ${basePath}
- test-publisher:
- name: test-publisher
memberOf: brandA-publishers
password: password
path: ${basePath}
It’s possible to create system (a.k.a service) users via AC Tool but depending on how your code base makes use of them, you may want to have a configuration file dedicated to the creation of those users (i.e. “users-system.yaml”).
As far as downsides are concerned, the only thing I can think of is that AC Tool needs to be installed only once prior to the config files being installed. If you were thinking of installing AC Tool as a sub package of the package containing your ACLs, this is currently not possible as it’s designed to be installed only once per AEM instance. The hook provided by the application will not exist at the time the config package is installing so no authorizables will be generated. As a result, this makes it quite difficult to update to a newer version and in more mature cloud environments, this means it needs to be installed in the instance provisioning process.
Due to the use of fragments to build up group permissions, the use of this tool tends to generate an excessive amount of groups. It’s for this reason that I would recommend bundling all the brand-specific fragments into corresponding folders like so:
/home/groups
/brandA
brandA-authors
brandA-publishers
/fragments
dam-brandA
content-brandA
/brandB
brandB-authors
brandB-publishers
/fragments
dam-brandB
content-brandB
Whilst this is all good for greenfield AEM projects, mature organisations may find it difficult to move to such a model unless their existing permission matrix has been well documented in advance. In the case it hasn’t, a “dump” of ACLs and authorizables could be taken using the AC Tool JMX beans but this will not output YAML configuration that is considered best practice, thereby curbing the benefits of adopting AC Tool in the first place.
Conclusion
Permissions management is a tricky problem to solve in any web content management system where content is ever changing. In my opinion, AC Tool currently provides the best solution due to its powerful syntax that can be leveraged to create the most complex policies, its documented best practices and more importantly the guarantee it provides that permissions will be consistent across environments.
No comments:
Post a Comment
If you have any doubts or questions, please let us know.