The official AAM 6.0.0-beta.3 release is out. Download it from the official WordPress repository.

Access & Security Policy

Introduction

The Access & Security Policy feature is available with basic AAM version 5.7 or higher.

Managing access and security for the WordPress website sometimes is quite challenging task. Even when everything is based on simple concept of Roles & Capabilities, it is not always clear, how to give the correct permissions to the WordPress website users so they can do only what is necessary.

Have you ever been in situation when you needed to grant temporary access to your website backend for a freelance developer however you did not want to give her administrator privileges? Or maybe you had an employee that was terminated on bad terms and in order to revoke access to your website you had to delete that user (which probably caused issues with data integrity and historical audit logs)?

You might also spent hours and hours learning about different types of capabilities and got even more overwhelmed when found that thousands and thousands WordPress plugin and themes introduce also custom capabilities and roles.

You probably also learned hard way that giving too much access and permissions to users may significantly increase the risk for user errors or for your website to be compromised.

So let's admit here the fact that access and security management is not easy and that is why with AAM v5.7 or higher we introduced the concept of Access & Security Policy.

Access & Security Policy is a JSON document that defines various resources and actions that your users, roles or applications have access to. The concept is very similar to the Amazon Access Policy documents however has a lot of enhancements tailored to the WordPress eco-system.

When policy is defined, you can attach it to any WordPress role, individual user, visitors or even programmatic service; and you can revoke it at any time without going through the overhead of deleting physical records or manually changing user permissions.

Because Access & Security Policy is a stand-alone JSON document, you can apply it to any other WordPress website that has AAM plugin installed.

Access & Security Policy is the revolutionary way to manage access and security for the WordPress websites and we are really excited to give this flexibility and power to your website administrative needs.

Access Policy UI #back to top

Each policy is an independent post type aam_policy record that is managed the same way as any other WordPress post or page. The only difference is that it can be accessed only by the user who also has access to manage AAM page (typically Administrators only).

AAM Policy Type UI
The list of all policies can be located on the AAM ≫ Access Policies page.

On the AAM page, you can also find the user interface to manage Access & Security policies. The Access Policies contains table with the list of all policies that you've defined. You can assign/revoke or edit any policy here.

AAM Access Policy
Please note! Currently this is the initial version of the policy management UI and more features will be added with next releases. Make sure that you follow us on twitter @aamplugin or subscribed to our newsletters.

Policy Anatomy #back to top

Access & Security policy is a valid JSON document that consists of several properties like policy attributes like Title, Description or Version and policy Statement(s) or Params that define what principal (user, role, process etc.) can or can not do and under what conditions.

Here is the basic example of a simple policy that restrict access to the entire Posts backend menu and all submenus as well as removes the widget with list of pages on the frontend and finally does not allows to have a level_7 capability.

{
    "Version": "0.0.1",
    "Title": "Some policy title",
    "Description": "Longer policy description",
    "Dependency": {
        "wordpress": ">=4.0",
        "advanced-access-manager": ">=5.7"
    },
    "Statement": [
        {
            "Effect": "deny",
            "Resource": [
                "BackendMenu:3124793302",
                "Widget:WP_Widget_Pages",
                "Capability:level_7"
            ]
        }
    ]
}

Now you can attach this policy to any role, user, visitors or even to all users and without any additional configurations AAM will apply those statements to attached principal.

As you can see, it becomes extremely easy to define access level with a policy and apply it to any user of your website. It is also becomes a portable access statements that can be used on other websites.

Version #back to top

We strongly recommend to use Semantic Versioning standard for policy version however you are free to define your own standard.

Title (optional) #back to top

Any reasonable title that is self-explanatory and defines what exactly a policy should be used for.

Description (optional) #back to top

Longer description for a policy. There is no limit how long it can be however we recommend to keep it within 1000 characters. If policy has a lot of statements and requires longer description, it is recommended to split it on multiple smaller policies.

Dependency #back to top

Policy statement define constrains that are applied to assignee of the policy. However there has to be a programmatic piece(s) of code that enforces those statement. That is why each policy may have one or more dependencies.

Dependency property contains the list of key/value pairs of depending plugins where key is plugin's slug, while value is a plugin's version condition.

"Dependency": {
    "wordpress": ">=4.0",
    "advanced-access-manager": ">=5.7"
}
Plugin's version definition uses semantic versioning guidelines and to make it even more flexible, AAM will adopt how NPM manages dependency versioning.

Statement #back to top

One or more policy statements that define access to specific resources and actions that can be applied to resources.

Param #back to top

Available since AAM 5.8.3

The policy may also carry certain set of configurations (parameters) that support statements or can be used by other plugins/themes.

Statement Anatomy #back to top

Policy statement is a single atomic definition if access is allowed or denied to specified resource(s) and action(s).

{
    "Effect": "deny",
    "Resource": [
        "Capability:edit_posts",
        "Capability:edit_pages"
    ],
    "Action": "AAM:toggle",
    "Condition": {}
}

The Statement property may contain one statement object or array of statement object and each statement consists of Effect, Resource and Action properties. Each of them is explained below.

Effect #back to top

Effect property determines if access is allowed or denied to specified resources and actions with allow and deny keywords respectively.

Resource #back to top

Resource property contains either a single string or array of strings that represent resource name. Typically resource name consists of two parts separated by colon ":" (e.g. BackendMenu:193563359, Metabox:submitdiv, Post:124) where the first part is the type of resource and the second part is unique resource id.

Action #back to top

While you may allow or deny access to the entire resource, sometimes it is useful to have more granular way to define access to a very specific actions that can be applied to resources.

For example, you can give permissions for an user to manage list of capabilities, however restrict ability to assign/deprive very specific list of capabilities with AAM:toggle action.

{
    "Effect": "deny",
    "Resource": [
        "Capability:edit_posts",
        "Capability:edit_pages"
    ],
    "Action": "AAM:toggle"
}

On the screenshot below you'll notice that edit_posts and edit_pages checkbox is grayed out (disabled).

AAM Toggle Capabilities

Condition #back to top

Set of conditions that current user should satisfy in order for the statement to be enforced.

Implemented with AAM v5.7.1 version or higher.

Metadata #back to top

Implemented with AAM v5.7.2 version or higher.

Define additional attributes for the statement that will be passed to the code when statement is evaluated. For example with URI Access resource you can define additional information about the way the redirect is managed when access is denied.

Resources & Actions #back to top

Access & Security Policy statements define if access is allowed or defined to specified resources as well as actions that can be taken upon them.

For example you might want to delegate the responsibility to update all WordPress plugins on your website except one. In this case you can explicitely restrict access to update that specific plugin by using Plugin:update action.

List of all currently supported resources and actions are listed below.

Available with AAM v5.8.2 or higher.

Each WordPress role is treated by AAM as resource and can be applied to any user our even group of users with the policy. This also can be used to revoke a very specific role from assignee.

Example:

{
    "Effect": "allow",
    "Resource": [
        "Role:editor"
    ],
}
Note! Be careful with this resource as any role may contain any number of capabilities. For example the contributor role may have all the capabilities assigned to it if somebody made a mistake.

Capability #back to top

For more information about the concept of a WordPress capability please check What is a WordPress capability article.

AAM treats a WordPress capability as a stand-alone resource that can be assigned/deprived to any role or user; and updated or deleted by authorized user. You can use following actions to give more granular access to a capability:

  • - AAM:toggle. Ability to assign or deprive a capability to any role or user;
  • - AAM:update. Ability to update capability's slug;
  • - AAM:delete. Ability to delete capability.
  • - AAM:list (>= v5.7.1). Ability to see capability on the list of Capabilities.
Note! These actions manage abilities to manipulate capabilities with AAM plugin only. This will not affect any other third-party plugin that manage capabilities.

Example:

{
    "Effect": "allow",
    "Resource": [
        "Capability:edit_posts",
        "Capability:edit_pages"
    ],
    "Action": [
        "AAM:toggle"
    ]
},
{
    "Effect": "deny",
    "Resource": [
        "Capability:level_1",
        "Capability:level_2",
        "Capability:level_3",
    ],
    "Action": [
        "AAM:update",
        "AAM:delete",
    ]
}

Backend Menu #back to top

Natively, WordPress code does not have a true concept of a unique menu id. The uniqueness of each menu or submenu item is defined by registered URI or custom page id. For example edit.php?post_type=page, options-reading.php or aam. Basically it is very easy to mistype or figure out what is the correct menu item identification.

That is why with AAM version 5.6.1 or higher, we added the menu id, which is a unique CRC32 hash value that consists of few digits.

AAM Menu Id

You should use this number to define policy statements. For example in the example below, we are going to restrict access to the entire Posts branch and Settings->Media menu.

Example:

{
    "Effect": "deny",
    "Resource": [
      "BackendMenu:3124793302",
      "BackendMenu:277636429"
    ]
}

Admin Toolbar #back to top

Because top admin toolbar is just a convenient set of shotcodes to the actual admin pages, AAM does not restrict access to the page a toolbar's shortcode links to. That is why you have to apply additional access and security measurements in order to restrict access to a linked page.

Each item on the top admin toolbar is identified by unique slug that can be found on the Admin Toolbar tab. Use this slug to define policy statements.

AAM Toolbar Id

Example:

{
    "Effect": "deny",
    "Resource": [
      "Toolbar:new-post",
      "Toolbar:documentation"
    ]
}

Metabox #back to top

Metaboxes are small functional blogs that typically are rendered on the edit post page that are identified by the unique hash (the concept is very similar to the way Backend Menu).

Note! AAM only filters out metaboxes that are not allowed. It does not take in consideration functionality that facilitates those metaboxes. This means that your end users may reverse engineer the way your website is setup and submit data that restricted metaboxes collect.

Below is example of the policy statement that removes Excerpt metabox on the backend side.

Example:

{
    "Effect": "deny",
    "Resource":"Metabox:3623465870"
}

Widget #back to top

Widgets are very similar to Metaboxes however they are rendered either on the frontend side of a website or on the Dashboard page of the backend side. They are also identified by the unique hash (the concept is exactly the same as for Metabox).

Note! AAM only filters out widgets that are not allowed. It does not take in consideration functionality that facilitates those widgets. This means that your end users may reverse engineer the way your website is setup and submit data that restricted widgets collect.

Below is example of the policy statement that removes search widget from the frontend sidebar.

Example:

{
    "Effect": "deny",
    "Resource":"Widget:3237588047"
}

Plugin #back to top

Keep in mind that managing access to plugins and actions that can be applied upon then is one of the most important aspects of the WordPress website security.

AAM treats each plugin as a separate resource that is identified by unique plugin's slug. The easier way to find the correct plugin's slug is to go to wp-content/plugins folder and the name of the plugin's folder is actually the slug.

The other way is to go to Plugins page and hover on desired plugin. The URL will contain the query parameter with plugin's slug as shown on the screenshot below.

AAM Plugin Slug

There are several actions that can be taked upon a plugin as following:

  • - WP:activate. Ability to activate specific plugin;
  • - WP:deactivate. Ability to deactivate specific plugin;
  • - WP:delete. An alias to delete_plugins capability. Ability to delete any plugin;
  • - WP:install. An alias to install_plugins capability. Ability to install any plugin;
  • - WP:update. An alias to update_plugins capability. Ability to update any plugin;
  • - WP:edit. An alias to edit_plugins capability. Ability to edit any plugin;

In the example below we restrict ability to deactivate AAM plugin and physically edit plugins files.

Example:

{
    "Effect": "deny",
    "Resource": "Plugin:advanced-access-manager",
    "Action": "WP:deactivate"
},
{
    "Effect": "deny",
    "Resource": "Plugin",
    "Action": "WP:edit"
}
Available with AAM v5.7.2 or higher.

Redefine how others can access posts and under post we mean any post, page, media or custom post type.

Each post is identified internally by three critical attributes: post type (e.g. post, page, product, attachment), post ID (e.g. 174, 12, 89) and permalink (also known as slug or post name). When you define Post resource, you have to explicitly provide post type as well as either post ID or permalink. For example if you need to restrict ability to edit and delete page Contact (ID 78 and permalink contact) you can define following statement:

{
    "Effect": "deny",
    "Resource": "Post:page:78",
    "Action": ["Edit", "Delete"]
}

As you can see above, the resource Post:page:78 consists of three part: Post as the resource type, page is the post type and 78 is the page ID.

Note! You may use the permalink (slug) for post identification. It has been introduced in AAM core with version 5.7.2 so you can have the ability to keep policy compatible across multiple websites that follow the same naming convention (e.g. all Contact Us pages have contact-us permalink or About Us - about-us permalink).

Below is the list of all available actions that you can define with Post resource:

  • - Read. Ability to read post on the Frontend or fetch via API;
  • - List. Ability to see post on any Frontend, Backend or API lists (hide post however allow access with direct URL or post identity);
  • - Comment. Ability to comment on a post on Frontend or via API;
  • - Edit. Ability to edit (update) post in the Backend or via API;
  • - Delete. Ability to delete (move to trash) post in the Backend or via API;
  • - Publish. Ability to publish post in the Backend or via API (when restricted, post can be only submitted for review);
There is no reason to define access settings separately for Frontend, Backend and API level (you can do that with AAM UI on Posts & Terms tab). This is because Access & Security Policy was designed to be level-agnostic. Which means it does not differentiate between Frontend, Backend and API levels as this significantly simplify and enhance your website access and security management.

Example:

Restrict ability to read and see "Christma Promotion" (ID 134), "End Of Year" (ID 135) pages after Dec 31, 21018.

{
    "Effect": "deny",
    "Resource": [
        "Post:page:134",
        "Post:page:135"
    ],
    "Action": ["Read", "List"],
    "Condition": {
        "GreaterOrEquals" : {
            "${DATETIME:y-m-d}": "2019-01-01"
        }
    }
}
Available with AAM v5.7.2 or higher and premium Plus Package extension version 3.9 or higher.

Redefine how others can access hierarchical terms and child posts that belong to those terms.

Each term is identified internally by three critical attributes: taxonomy (e.g. category, page_category), term ID (e.g. 34, 14, 78) and slug. When you define Term resource, you have to explicitly provide taxonomy as well as either term ID or slug. For example if you need to restrict ability to assign any posts to a "Houses" category (slug houses) you can define following statement:

{
    "Effect": "deny",
    "Resource": "Term:category:houses",
    "Action": "Assign"
}
Note! You may use the slug for term identification.

Below is the list of all available actions that you can define with Term resource:

  • - Browse. Ability to browse terms content (list of assigned posts) on both Frontend and API levels;
  • - List. Ability to see term on any Frontend, Backend or API lists (hide term however allow access with direct URL or term identity);
  • - Edit. Ability to edit (update) term in the Backend or via API;
  • - Delete. Ability to delete term in the Backend or via API;
  • - Assign. Ability to any existint post to term (term still will be listed on the list of available terms to select from);

To use rich AAM access settings inheritance mechanism, you have the ability to redefine access to all child posts for any specific term with :posts suffix as following:

{
    "Effect": "deny",
    "Resource": "Term:category:34:posts",
    "Action": ["List", "Read", "Commend"]
}

Post Type #back to top

Available with AAM v5.7.2 or higher and premium Plus Package extension version 3.9 or higher.

Define how others can interact with the entire post type and hierarchical terms that are related to a post type. For example you can restrict the ability to show all the pages by default or disallow to edit them.

Post type is defined by the slug. This is the first argument for the register_post_type() function and every post, page, media or custom post type uses this function to let WordPress core be aware of it.

There is no real need to manage access to the instance of the post type itself as this is just a virtual identity. However it make sense to manage access to all posts and terms that are related to a post type with :posts and :terms suffixes respectively.

For example if there is a need to restrict to delete all the pages, you can define following statement:

{
    "Effect": "deny",
    "Resource": "PostType:page:posts",
    "Action": "Delete"
}

In another example, the statement restricts the ability to edit and assign all the categories (terms) however allow to assign posts to the uncategorized category.

{
    "Effect": "deny",
    "Resource": "PostType:post:terms",
    "Action": ["Edit", "Assign"]
},
{
    "Effect": "allow",
    "Resource": "Term:category:uncategorized",
    "Action": ["Assign"]
}

API Route #back to top

Available with AAM v5.7.2 or higher.

The Route resource can be used to manage access to individual RESTful API and XML-RPC endpoints.

Each endpoint is identified by three attributes:

  • - type: either RESTful or XML;
  • - endpoint: e.g. /oembed/1.0/embed, /aam/v1/payment/ipn etc;
  • - HTTP method: GET, POST, PUT, DELETE etc

For example if you need to restrict access to the RESTful /wp/v2/categories GET endpoint, define this statement:

{
    "Effect": "deny",
    "Resource": "Route:RESTful:/wp/v2/categories:GET",
}

URI Access #back to top

Available with AAM v5.7.2 or higher and limited without premium Plus Package extension.

Manage access to any individual website's URI or group of relative URIs (wildcard * option that is available with Plus Package extension). Additionally also define how to handle redirect when access is denied for matched URI with Metadata settings.

URI is the relative path to any page on your website. For example if the full URL to a category "Authenticated User Only" is https://mydomain.com/category/authenticated-user-only, then URI will be /category/authenticated-user-only.

URI may also contain query parameters (GET params) and the order of them is irrelevant.

There are 5 different way to define how to handle redirect when access to a matching URI is denied:

Default "Access Denied" Message

No need to define Metadata. By default access will be granted based on the statement's Effect attribute.

{
    "Effect": "deny",
    "Resource": "URI:/category/authenticated-user-only/"
}

Custom Message

It can be any text or even HTML message. You can also define a message in your own language or even prepare multiple conditional statements that will be used based on user attributes (e.g. location, role etc).

{
    "Effect": "deny",
    "Resource": "URI:/category/authenticated-user-only/",
    "Metadata": {
        "Redirect": {
            "Type": "message",
            "Message": "<h3>You are not allowed to see this page!</h3>"
        }
    }
}

Redirect To Existing Page

Redirect user to any existing page by specifying either valid page Id or page Slug.

{
    "Effect": "deny",
    "Resource": "URI:/category/authenticated-user-only/",
    "Metadata": {
        "Redirect": {
            "Type": "page",
            "Slug": "access-denied"
        }
    }
}

Redirect To URL

Redirect user to any valid URL. Make sure that correct URL is entered as it is evaluated by PHP filter_input function with FILTER_VALIDATE_URL filter.

{
    "Effect": "deny",
    "Resource": "URI:/category/authenticated-user-only/",
    "Metadata": {
        "Redirect": {
            "Type": "url",
            "URL": "https://mydomain.com/restricted-access-page"
        }
    }
}

Trigger PHP Callback

Trigger PHP callback. The valid callback has to be specified. For more information check Callbacks page.

{
    "Effect": "deny",
    "Resource": "URI:/category/authenticated-user-only/",
    "Metadata": {
        "Redirect": {
            "Type": "callback",
            "Callback": "MyApp\AccessController::deny"
        }
    }
}

Param #back to top

Params are available with AAM v5.8.3 or higher.

Params has been introduced with the main idea to cover scenarios when defined access settings require additional system configures. However this can be used to carry set of configurations for any any plugin or theme.

For example, Plus Package extension uses Params to redefine the default category that will be assigned to newly created post or custom post type. The example below is quite common requirement when website master needs to hide all the categories PostType:post:terms and allow to create posts only in one specific category Term:category:requires-moderation

{
    "Version": "0.1.0",
    "Dependency": {
        "wordpress": ">=4.0",
        "advanced-access-manager": ">=5.8.3"
    },
    "Statement": {
        "Effect": "deny",
        "Resource": "PostType:post:terms",
        "Action": ["List", "Assign"]
    },
    "Statement": {
        "Effect": "allow",
        "Resource": "Term:category:requires-moderation",
        "Action": ["List", "Assign"]
    },
    "Param": [
        {
            "Key": "post:default:category",
            "Value": "requires-moderation"
        }
    ]
}

Each Param has to have at least Key and Value properties. The cool part of the Params that they also may contain conditions. So basically specified params will be applicable only when certain conditions are met.

Key is the unique name for the parameter. It can be either a string or any valid marker that is evaluated as a string.

"Param": [
    {
        "Key": "post:default:category",
        "Value": 345
    },
    {
        "Key": "${CALLBACK.getSomeValidKey}",
        "Value": true
    }
]

The key is used in the code to retrieve Value for current user, visitor or role.

// This code will return int value `345` if current user has
// Param `post:default:category` defined in attached policy
AAM::api()->getPolicyManager()->getParam('post:default:category');

Value #back to top

The Value contains the actual param's value. It can be any valid type that is supported by JSON specification or it can be the policy marker that returns whatever value is needed.

Condition #back to top

Param definition follows the same concept as Statement that is why it can be conditioned. For more information about available type of conditions, please refer to the Conditions section.

Conditions #back to top

Conditions are available with AAM v5.7.1 or higher.

Each policy Statement may have one or more conditions that current user or programmatic application has to meet in order for the statement to take effect. For example you may what to restrict access to the backend area of your website for holidays like Christmas or Easter. Or you may remove the ability to comment on all posts from certain IP range.

Below is the example of the policy that restricts access to the backend area for hours outside of the 5am and 10pm as well as for weekends.

{
    "Version": "0.1.0",
    "Dependency": {
        "wordpress": ">=4.0",
        "advanced-access-manager": ">=5.7.1"
    },
    "Statement": [
        {
            "Effect": "deny",
            "Resource": ["Capability:access_dashboard"],
            "Condition": {
              "Between": {
                "${DATETIME.G}": [[0, 4],[21,23]],
                "${DATETIME.N}": [6,7]
              }
            }
        }
    ]
}
Conditions bring Access & Security Policies to the next level because you literally have no limit to define when and how access and/or security rules will be applied to any individual user, group or users or applications.

Another great power of conditions is that it may contain dynamic markers ${marker} that are replaced with actual value before condition evaluations.

Marker is the awesome way to inject some dynamic data like user's IP address, current time or values in POST request and check if this matches condition. For example you might want to allow access to certain posts only if user's email is *@gmail.com or current month is November.

Statement may have more than one condition where conditions are grouped by type (Between, Less, Equals, RegEx etc.). As it is shown on the image below, all the conditions within one type are combined with logical OR operand while condition types are combined with AND operand. This allows to build conditions like - allow access to edit pages if user name is John Smith OR Rebecca Ross AND they are coming from UK IP space.

AAM Condition Anatomy

The left part of each condition is either static or dynamic ${...} value that is compared to the right part according to the type of condition.

The list of all available markers and even more you can find in the Markers section.

Finally, with AAM v5.8.2 or higher, you may explicitly cast any value of the left or right part of the condition. For example, if you are not sure if ${USER.ID} actually contains integer value, you may cast it to integer for Equals condition type.

{
    "Statement": [
        {
            "Effect": "deny",
            "Resource": ["Capability:access_dashboard"],
            "Condition": {
              "Equals": {
                "(*int)${USER.ID}": 5
              }
            }
        }
    ]
}

So as you can see above, to cast any expression (left or right of the condition), you can to start it with (*type) where type can be int, string, bool or boolean, ip.

The cast type (*ip) is very critical when you define condition that works with IP address. For more information about this check USERS marker section.

Between #back to top

Between condition type is a convenient way to verify that some value is within certain range. For example if you need to check that current user ID is between 10 and 50 you can define your condition following way:

"Between": {
    "${USER.ID}": [10, 50]
}

Condition evaluation

"USER.ID" >= 10 AND "USER.ID" <= 50

For more complex situations you might need to have the ability to define multiple ranges. Let's say you need to restrict access to backend for users with IDs between 10-23 and 50-100. This way you can define multiple ranges:

"Between": {
    "${USER.ID}": [[10, 23], [50, 100]]
}

Condition evaluation

("USER.ID" >= 10 AND "USER.ID" <= 23) || ("USER.ID" >= 50 AND "USER.ID" <= 100)

Equals #back to top

The Equals type does the literal comparison of left and right parts of the condition and check if two values are identical.

"Equals": {
    "${USER.Authenticated}": true,
    "${GET.p}": 6
}

Condition evaluation

"USER.Authenticated" === true || "GET.p" === 6
Note! It is a literal value comparison, which means integer value 6 will not be equal string value '6'. Keep eye on the value type.

NotEquals #back to top

The NotEquals type does the literal comparison of left and right parts of the condition and check if two value are different.

"NotEquals": {
    "${COOKIE.wp-test}": "yes"
}

Condition evaluation

"COOKIE.wp-test" === "yes"
Note! It is a literal value comparison, which means integer value 6 will not be equal string value '6'. Keep eye on the value type.

Greater #back to top

The Greater type does comparison of left and right parts of the condition and check if left value is greater than right value.

"Greater": {
    "${DATETIME.Y-m-d}": "2018-11-03"
}

Condition evaluation

"date('Y-m-d')" > "2018-11-03"

The Less type does comparison of left and right parts of the condition and check if left value is less than right value.

"Less": {
    "${CALLBACK.wp_get_current_user_id}": 10
}

Condition evaluation

"wp_get_current_user_id()" < 10

GreaterOrEquals #back to top

The GreaterOrEquals type does comparison of left and right parts of the condition and check if left value is greater or equals to the right value.

"GreaterOrEquals": {
    "${POST.amount}": 50
}

Condition evaluation

"POST.amount" >= 50

LessOrEquals #back to top

The LessOrEquals type does comparison of left and right parts of the condition and check if left value is less or equals to the right value.

"LessOrEquals": {
    "${USER.ID}": 21
}

Condition evaluation

"USER.ID" <= 21

Check if left part of the condition is in the list of values of the right part of the condition.

"In": {
    "${USER.user_email}": ["john@mydomain.com", "jully@anotherdomain.com"]
}

Condition evaluation

"USER.user_email" ∈ {"john@mydomain.com", "jully@anotherdomain.com"}
Note! Don't get confused In condition type with Between. The In condition type checks for literal match of left value in set of right values.

NotIn #back to top

Check if left part of the condition is NOT in the list of values of the right part of the condition.

"NotIn": {
    "${USER.user_email}": ["john@mydomain.com", "jully@anotherdomain.com"]
    }

Condition evaluation

"USER.user_email" ∉ {"john@mydomain.com", "jully@anotherdomain.com"}

Check if left part of the condition is similar to the right part of the condition. The * (star) symbol means any character.

"Like": {
    "${USER.display_name}": "John*"
 }

Condition evaluation

preg_match("/^{USER.display_name}.*$/")

NotLike #back to top

Check if left part of the condition is NOT similar to the right part of the condition. The * (star) symbol means any character.

"Like": {
    "${CALLBACK.GetUserCountry}": "A*"
}

Condition evaluation

preg_match("/^{GetUserCountry()}.*$/") === false

Regex #back to top

Check if left part matches the right part of the condition with regular expression.

"RegEx": {
    "${GET.page}": "/^[a-z]+$/i"
}

Condition evaluation

preg_match("/^[a-z]+$/i", "GET.page") === false

Markers #back to top

Markers is the great way to inject dynamic values to the statement conditions and there are several sources of data you can use.

The marker is defined by dollar sign ($) and curly brackets ({}) with dot (.) delimited source of data and data location/interpretation ${source.data}. For example if you need to extract current user email, you specify ${USER.user_email} marker.

Marker can be defined either on the left or on the right side of the condition. It can even be defined as one of the values for Between range or In/NotIn array.

"Between": {
    "${USER.ID}": [10, ${CALLBACK.getHighestUserId}]
}

The current user object is initialized during very early stage of the website load. It is stored in the global $current_user and contains the instance of the WP_User class. This object basically has all the information about user, and you can use it to prepare all kinds of conditions for your policy statements.

For example, you can apply some statements to a user with emails that have gmail.com domain with the Like or RegEx condition:

"Like": {
    "${USER.user_email}": "*@gmail.com"
 }

There are also two special attributes that you can use to enhance the default WP_User dataset and prepare even more sophisticated conditions.

The first attribute is ip or ipaddress that extracts current user's IP address from the request. This way you can apply statements to the current user only when he/she comes from very specific IP address or IP range (all depends on what type of condition you are planning to use). For example following condition is applicable for a user who comes to the website from the IP range 10.123.10.0 - 10.123.10.255:

"Between": {
    "(*ip)${USER.ip}": ["(*ip)10.123.10.0", "(*ip)10.123.10.255"]
 }
Please Note! The (*ip) is the reserved construct that is used for type casting. So basically it tells AAM to treat the string as IP address for any kind of comparison. Without this instruction, AAM will not be able to determine if entered expression contains IP address or a simple string of numbers and dots.

The second attribute is authenticated and contains boolean value true or false. It is less likely that you'll even need this attribute, however if so, this is how you would prepare the condition:

"Equals": {
    "${USER.authenticated}": false
 }

DATETIME #back to top

Get current date and/or and format it accordingly to the PHP date formatting rules. The table below is literal copy from the PHP official documentation.

format character Description Example returned values
Day --- ---
d Day of the month, 2 digits with leading zeros 01 to 31
D A textual representation of a day, three letters Mon through Sun
j Day of the month without leading zeros 1 to 31
l (lowercase 'L') A full textual representation of the day of the week Sunday through Saturday
N ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0) 1 (for Monday) through 7 (for Sunday)
S English ordinal suffix for the day of the month, 2 characters st, nd, rd or th. Works well with j
w Numeric representation of the day of the week 0 (for Sunday) through 6 (for Saturday)
z The day of the year (starting from 0) 0 through 365
Week --- ---
W ISO-8601 week number of year, weeks starting on Monday Example: 42 (the 42nd week in the year)
Month --- ---
F A full textual representation of a month, such as January or March January through December
m Numeric representation of a month, with leading zeros 01 through 12
M A short textual representation of a month, three letters Jan through Dec
n Numeric representation of a month, without leading zeros 1 through 12
t Number of days in the given month 28 through 31
Year --- ---
L Whether it's a leap year 1 if it is a leap year, 0 otherwise.
o ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0) Examples: 1999 or 2003
Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003
y A two digit representation of a year Examples: 99 or 03
Time --- ---
a Lowercase Ante meridiem and Post meridiem am or pm
A Uppercase Ante meridiem and Post meridiem AM or PM
B Swatch Internet time 000 through 999
g 12-hour format of an hour without leading zeros 1 through 12
G 24-hour format of an hour without leading zeros 0 through 23
h 12-hour format of an hour with leading zeros 01 through 12
H 24-hour format of an hour with leading zeros 00 through 23
i Minutes with leading zeros 00 to 59
s Seconds, with leading zeros 00 through 59
u Microseconds (added in PHP 5.2.2). Note that date() will always generate 000000 since it takes an integer parameter, whereas DateTime::format() does support microseconds if DateTime was created with microseconds. Example: 654321
v Milliseconds (added in PHP 7.0.0). Same note applies as for u. Example: 654
Timezone --- ---
e Timezone identifier (added in PHP 5.1.0) Examples: UTC, GMT, Atlantic/Azores
I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise.
O Difference to Greenwich time (GMT) in hours Example: +0200
P Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3) Example: +02:00
T Timezone abbreviation Examples: EST, MDT ...
Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. -43200 through 50400
Full Date/Time --- ---
c ISO 8601 date (added in PHP 5) 2004-02-12T15:19:21+00:00
r » RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200
U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) See also time()

Get any query parameter ($_GET value).

Get any POST parameter ($_POST value).

CALLBACK #back to top

Trigger any valid callback function or static method that returns some values. This is quite powerful way to enhance your policies with some dynamic calculation. Each CALLBACK marker has to have well-defined PHP callback as string. This means that it has to be either function name or static method of a class.

In the example below, we'll define a policy that restrict user to access backend area if user is not registered with some third-party authentication system (e.g. Active Directory).

{
    "Version": "0.1.0",
    "Dependency": {
        "wordpress": ">=4.0",
        "advanced-access-manager": ">=5.7.1"
    },
    "Statement": {
        "Effect": "deny",
        "Resource": "Capability:access_dashboard",
        "Condition": {
          "Equals": {
            "${CALLBACK.\MyApp\Auth::isRegistered}": false
          }
        }
    }
}
<?php
declare(strict_types=1);

namespace MyApp;

use ThirdParty\Connector;

/**
 * Adapter for some third-party authentication server
 */
class Auth {

    /**
     * Check if current user is registered with the third-party
     *
     * @return bool
     */
    public static isRegistered() : bool {
        $registered = false;
        $user       = wp_get_current_user();

        if (is_a($user, 'WP_User')) {
            // Let's assume that findUserByEmail method returns object that contains
            // details about the user and `exists` property is either true or false
            $registered = Connector::findUserByEmail($user->user_email)->exists;
        }

        return $registered;
    }
    
}

SERVER #back to top

Available since AAM v5.8.3 or higher.

Get any value from the super global $_SERVER. For example, this may be quite useful to define statements that should be applied only when user browses very specific page.

The great example where SERVER marker can be used is discussed in this forum thread.
Available since AAM v5.8.3 or higher. This is also quite experimental feature and require additional testing.

With AAM v5.8.3 the way each condition is evaluated has been completely refactored. Prior to v5.8.3, all conditions were evaluated right after all plugins loaded (the WordPress core hook plugins_loaded). This approach was not ideal because a lot of third party plugins typically extend/enhance WordPress with hooks that trigger after plugin_loaded (e.g. init, wp).

With AAM v5.8.3, conditions are evaluated every time it is checked if resource is allowed or not. It add a tiny overhead from functional stand-point however eliminate any constrains. Additionally you can explicitly inject inline arguments to defined conditions.

{
    "Version": "0.1.0",
    "Dependency": {
        "wordpress": ">=4.0",
        "advanced-access-manager": ">=5.8.3"
    },
    "Statement": {
        "Effect": "deny",
        "Resource": "Plugin",
        "Action": "WP:update"
        "Condition": {
          "Equals": {
            "${ARGS.pluginUpdates}": false
          }
        }
    }
}
$manager = AAM::api()->getPolicyManager();

//This will return false
var_dump($manager->isAllowed('Plugin:WP:update', array('pluginUpdates' => false))));
                            

Access Policy Repository #back to top

Since January 2019, we lauched dedicated page for all Access & Security Policies. You can navigate to the Policy Repository to browse all available policies or request a custom one.