Was this page helpful?

Managing Roles - programmatically

What are Custom Roles?

Custom roles allow the administrator to restrict access of users to certain resources. Roles follow a whitelisting approach, wherein everything that a user is allowed to do, needs to be explicitly defined.

Along with a name and description, a role contains, permissions and policies. Permissions are basic rules that define whether (or not) a user can access content types, settings, and entries. It can be specified if a user can read and/or manage those.

On the other hand, there are policies that are used to allow or deny access to resources in a very fine-grained fashion. For example, with policies it is possible to limit read access to only entries of a specific content type or write access to only certain parts of an entry (e.g. for a specific locale).

Note: A role name must be unique within the space.

Create a Role

Endpoint: [POST] /spaces/{{ spaceId }}/roles

Base URL: https://api.contentful.com

Description: This endpoint is used to create a custom role.

Response:

{
  "sys": {
    "type": "Role",
    "id": "0xvkNW6WdQ8JkWlWZ8BC4x",
    "version": 0,
    "createdAt": "2015-05-18T11:29:46.809Z",
    "createdBy": {
      "sys": {
        "type": "Link",
        "linkType": "User",
        "id": "7BslKh9TdKGOK41VmLDjFZ"
      }
    },
    "updatedAt": "2015-05-18T11:29:46.809Z",
    "updatedBy": {
      "sys": {
        "type": "Link",
        "linkType": "User",
        "id": "4FLrUHftHW3v2BLi9fzfjU"
      }
    }
  },
  "name": "Some role",
  "description": "Test role",
  "permissions": {
    "ContentModel": [
      "read"
    ],
    "Settings": "all"
    "ContentDelivery": "all",
    "Environments": "all",
    "EnvironmentAliases": "all",
    "Tags": "all"
  },
  "policies": [
    {
      "effect": "allow",
      "actions": [
        "read",
        "create",
        "update",
        "delete",
        "publish",
        "unpublish",
        "archive",
        "unarchive"
      ],
      "constraint": {
        "equals": [
          {
            "doc": "sys.type"
          },
          "Entry"
        ]
      }
    }
  ]
}

To enable all the permissions pertaining to a role, use the "all" keyword. It enables all possible values for the respective permission.

{
  "name": "Name of the role",
  "description": "Description of the role",
  "permissions": {
    "ContentDelivery": "all"
    "ContentModel": "all",
    "Settings": "all"
  },
  "policies": [
    {"effect": "allow", "actions": "all"}
  ]
}

Instead of "all", it is possible to define an array of allowed actions, thereby providing partial access to the system.

{
  "name": "Name of the role",
  "description": "Description of the role",
  "permissions": {
    "ContentDelivery": [
      "read", // Allows reading of api keys
      "manage" // Allows creation/deletion of api keys
    ],
    "ContentModel": [
      "read", // Allows reading of content types. Note that you cannot read entries without reading content types.
      "manage" // Allows creation/manipulation/deletion of content types
    ],
    "Settings": [
      "manage" // Allows full access to the settings section in the web UI
    ]
  },
  "policies": [
    {"effect": "allow", "actions": "all"}
  ]
}

Constraints

Constraints are fine-grained and content-focused access rules defined with the role's policies. For example,

{
  "name": "Name of the role",
  "description": "Description of the role",
  "permissions": { "ContentDelivery": "all", "ContentModel": "all", "Settings": "all" },
  "policies": [
    {
      "effect": "allow", // With "allow" we are enabling a user to do certain things. With "deny" we take away what we previously allowed
      "actions": "all"   // The keyword "all" is an alias for ['read', 'create', 'update', 'delete', 'archive', 'unarchive', 'publish', 'unpublish'],
      "constraint": {    // A constraint can be defined to restrict the effect and the actions to a subset of entities.
        <ConstraintKeyword>: <ConstraintValues>
      }
    }
  ]
}

A constraint is made of a keyword and the values that keyword requires. Some keywords accept constraints as values, allowing nested constraints. Supported keywords are equals, and, or, not, in, all, range, paths. You can specify nested constraints for and, or, not. The paths constraint specifies which content paths can be modified and is only used for the update action. The list operators in, all only work for content paths that contain lists. The following table show usage of the supported constraints:

Keyword Values Description and Use cases
equals List containing a content path and content value The equals constraint can be used to compare the content path value against a specific value. For instance to match the type of a document or to match entries of a content type:
{ "equals": [{ "doc": "sys.type" }, "Asset"] }
and List of constraints The and constraint is satisfied if all its sub-constraints are satisfied. The value of the and constraint should be an array of constraints.
For instance, to specify both type and content type:
{ 
"and": [
{ "equals": [{ "doc": "sys.type" }, "Entry"] },
{ "equals": [{ "doc": "sys.contentType.sys.id" },
"content-type-id"] }
]
}
not One constraint The not constraint inverts the result of its value. The value of the not constraint must be another constraint. A typical use case for the not constraint is the inversion of an allow list to deny list.
For example, allow a user to access all content types except one:
{
"and":[
{ "equals": [{ "doc": "sys.type" }, "Entry"] },
{ "not": { "equals": [{ "doc": "sys.contentType.sys.id" },
"content-type-id"] } }
]
}
Without the not constraint you would instead need to list a possibly much longer and constantly changing set of content types.
or List of constraints The or constaint is satisfied if one if its conditions is satisfied. The value of the or constraint should be an array of constraints.
Or constraints are used to enable an effect for various different resources. For example, if a user is only allowed to read entries of a specific content type or all assets:
{
"or": [
{"and": [
{ "equals": [{ "doc": "sys.type" }, "Entry"] },
{ "not": { "equals": [{ "doc": "sys.contentType.sys.id" },
"content-type-id"] } }
]},
{ "equals": [{ "doc": "sys.type" }, "Asset"] }
]
}
all List containing a content path and a list of values The all constraint is only meaningful for list types. The only content path that contains a list is metadata.tags. To reference some part of the item in the list, access the path as if it was not list. For instance with the content path metadata.tags.sys.id the all constraint will consider the sys.id sub-path of every item in the metadata.tags list. The all constraint is satisfied when every item in the list of values from the content is found in the constraint. The constraint is still satisfied if it specifies other items not found in the content list. For example:
{ "all": [{ "doc": "metadata.tags.sys.id" }, 
["tagA", "tagB"] }
This would match content with just tagA, just tagB, or both. It would not match content with tagA, tagB, and tagC.
in List with content path and a list of values The in constraint is only meaningful for list types. The only content path that contains a list is metadata.tags. To reference some part of the item in the list, access the path as if it was not list. For instance with the content path metadata.tags.sys.id the in constraint will consider the sys.id sub-path of every item in the metadata.tags list. The in constraint is satisfied when at least one item from the content path is in the list of values in the constraint:
{ "in": [{ "doc": "metadata.tags.sys.id" }, 
["tagA", "tagB"] }
This would match any content with at least tagA or tagB, and any number of other tags.
range List with content path and an object containing at least one range operator. The range constraint treats the content path like a number and tests the specified operators/values on that number. Allowed operators are gte, gt, lte, lt:
{ range: [{ doc: 'fields.total.en-US' }, { gte: 2 }] }
and also:
{ range: [{ doc: 'fields.pi.en-US' }, { gt: 3, lt: 4 }] }
paths List of content paths Paths are only considered on update. To satisfy the path constraint the changed paths must be in the list of paths from the constraint. The wildcard % can be used to match many content paths at once, and many paths can be listed in each path constraint:
{ paths: [
{ doc: 'fields.total.en-US' },
{ doc: 'metadata.%' },
{ doc: 'fields.%.de-DE' },
{ doc: 'fields.pi.%' }
] }
Note that wildcard paths only work in a path constraint, other constraints require a complete content path.

Important Notes about Roles and Constraints

  • Whenever content is accessed or searched for, read policies must be satisfied. Policies with complex constraints could slow down access to content.

  • There are no paths constraints for the create action so users that can create content can always create content with values for any path.

  • If a content path is missing, the constraint will not be satisfied. If the constraint is nested, other constraints may still be satisfied. For instance, if one or constraint references a missing content path but the other or constraint is satisfied, then the entire or constraint is satisfied.

  • "allow" policies expand the set of possible operations a user can perform. "deny" policies reduce that set. Each "allow" policy has its set of operations reduced by every "deny" policy. If the user has more than one role, the "deny" policies from each role will reduce the "allow" policy operations all of their roles. This can lead to surprising results depending on how the policies are constructed. If one role grants access to all operations with an allow rule and denies access to half of those operations with a deny rule, and another role grants access to all operations with an allow rule and denies access to a different half of those operations with a deny rule, then the combination of the two roles will result in no access at all. Because the deny rules are global and each denies a different half of all operations, they combine to deny everything. If instead one role had an allow rule that granted access to half of all operations, and the other role granted access to a different half of all operations, then the combination of those two roles would lead to complete access.

Update a Role

Endpoint: PUT /spaces/{{ spaceId }}/roles/{{ roleId }}

Base URL: https://api.contentful.com

Description: This endpoint is used to update an existing custom role. This endpoint can also be used to create a new role with a specific id.

Response:

{
  "sys":{
    "type":"Role",
    "id":"48x6PYyasXaAuN3kQgposV",
    "version":0,
    "space":{
      "sys":{
        "type":"Link",
        "linkType":"Space",
        "id":"296guvxfpn71"
      }
    },
    "createdBy":{...},
    "createdAt":"2013-07-04T13:06:57Z",
    "updatedBy":{...},
    "updatedAt":"2013-07-04T13:06:57Z"
  },
  "name":"Editor",
  "description":"Allows editing of all Entries",
  "policies":[
    {
      "effect":"allow",
      "actions":"all"
    }
  ],
  "permissions":{
    "ContentModel":[
      "read"
    ],
    "Settings":[],
    "ContentDelivery":[]
  }
}

It is possible to apply the constraints while updating a role.

Delete a Role

Endpoint: PUT /spaces/{{ spaceId }}/roles/{{ roleId }}

Base URL: https://api.contentful.com

Description: This endpoint is used to delete an existing custom role. It is not possible to delete a role, if the user corresponding to this role does not have any other assigned roles.

Response: The server responds with a http code '204' in case of successful deletion of the role. If the user is assigned only with the role that is to deleted, the server responds with a http code '412'.

Next steps

Not what you’re looking for? Try our FAQ.