WordPress access control for users with multiple roles

Advanced Access Manager supports multiple roles per user with version 5.5 or higher. Prior to v5.5, AAM was taking in consideration only the first user’s role and ignore others. Also, by default, the multiple roles support is disabled and you can enable it on the AAM Settings Area with Multiple Roles Support option.

Prehistory

For the longest time I’ve been opposed to the idea of multiple roles per user as this causes a lot of conflicts with access settings and properties. For example if Role X has access to Post A, however Role Y does not, then what should we do with user that has both roles? Shall we grand access to Post A or restrict? Depending on the website requirements we might want to grand access to restricted resource if any role allows it; and on other website we definitely want to restrict access to protected resource when any role defines so.

This type of access ambiguity was the main pain in the butt and the reason why I’ve been against multiple roles implementation. For several year I was helping a lot of webmasters to rethink their access management strategy. However that was not enough and I’ve been getting more and more requests to add multiple roles support so finally, after many hours or refactoring, project planning and testing, this feature was implemented in AAM v5.5.

What is “multiple roles”?

Generally speaking, multiple roles per user is when some active account has two or more existing roles assigned to it. The emphasis here is on existing roles because through many years supporting other projects I’ve seen guzzilian times user accounts that had corrupted data in the usermeta; and this was misleading developers.

FYI! When WordPress core initiates user account, it fetches list of capabilities from the user meta table and evaluates them through the WP_Roles::is_role method. This way any none-existing roles are treated as capabilities and not as roles.

You also have to have to keep in consideration that the WordPress website is a big collection of resources (e.g. posts, pages, menus, widgets, physical files) and depending on the website’s purpose some resources might have different “weight” or level or importance.

For instance, you want to be absolutely sure that if any role restricts access to the Backend Menu Settings, then this rule has to be enforced; however for Metaboxes & Widgets you want to have less restrictive approach. In this case you have the opportunity to define how access settings are going to be merged for each individual resource type.

How does AAM merges access settings

To keep it all backward compatible, if any of the roles restrict access to certain resource, then AAM will honor this preference however you have the ability to override this behavior with few simple ConfigPress settings. Because you might want to have different preferences based on the type of a resource (post, term, admin menu, metabox etc), you have to explicitly define them for each resource type as following:

[aam]
; Preference value can be "allow", "deny" or "override"
; The "allow" means that AAM will allow access to resource if any role allows it
; The "deny" means that AAM will deny access to resource if any role restricts it
; The "override" will simply merge all access settings (result may be unpredictable)

; Manage access preference to the Backend Menu
core.settings.menu.merge.preference = "allow"
; Manage access preference to the Top Admin Toolbar
core.settings.toolbar.merge.preference = "allow"
; Manage access preference to the Metaboxes & Widgets
core.settings.metabox.merge.preference = "allow"
; Manage access preference to posts, pages, media and any custom post type
core.settings.post.merge.preference = "allow"
; Manage access preference to hierarchical terms (categories)
core.settings.term.merge.preference = "allow"
; Manage access preference to any post type like Posts, Pages, Products etc
core.settings.type.merge.preference = "allow"
; Manage access preference to any taxonomy object
core.settings.taxonomy.merge.preference = "allow"
; Manage access preference to URI Access
core.settings.uri.merge.preference = "allow"

However Note! There are several access settings that are not merged due to its nature and these settings are: Login Redirect, Logout Redirect and Access Denied Redirect. The first role in the list of user’s roles is taken in consideration.

Get notified about important updates and new features (no more than one email per month).