Core
Guides v1.x
1

You are currently looking at the documentation of a previous version of Kuzzle. We strongly recommend that you use the latest version. You can also use the version selector in the top menu.

Dynamic Roles Definitions #

In the Getting Started Guide, we discussed how to assign basic permissions to users through roles and profiles. We are now going to look at more complex and dynamic permissions.

By definition, permissions only control to what API actions users can access depending on their profiles, without conditions.

But there are times when more dynamic rules are needed, for instance when an API action can only be executed if the payload matches some arbitrary business rules.

This can be address either with a Pipe Plugin, or Permission Closures.


Permission Closures #

Deprecated since 1.4.0

Permission closures are deprecated in favor of Pipe Plugin, which are more flexible and simpler to use.

With Permission Closures, instead of hard-coding the permission boolean value, we assign a function (or closure) that evaluates to a boolean value and determines whether or not an action is permitted.

For example, here is a role definition that limits access to documents such that a document can only be modified by its owner:

Copied to clipboard!
{
  "controllers": {
    "document": {
      "actions": {
        "update": {
          "args": {
            "document": {
              "index": "$request.input.resource.index",
              "collection": "$request.input.resource.collection",
              "action": {
                "get": "$currentId"
              }
            }
          },
          "test": "return args.document.content.user.id === $currentUserId"
        }
      }
    }
  }
}

In the definition above:


The Permission Function #

The permission function is executed in a sandbox with a limited context. It is defined in the test parameter of the role definition and must return a boolean value.

The permission function has the following signature:

Copied to clipboard!
/**
 * @param {Request} $request              The current action request.
 * @param {string} $currentUserId         The current user kuid. Shortcut to request.context.token.userId
 * @param {Object} args                   The result of the evaluated args definition.
 *
 * @return {Boolean}
 */
function ($request, $currentUserId, args) {
  // the function body is built from the "test" parameter.
  // Example, with the sample role above:
  return args.document.content.user.id === $currentUserId;
};

Permission Function Predefined Variables #

There are a set of predefined variables which are automatically accessible in a Permission Function, these are:

  • $request: The complete request object being evaluated.
  • $currentUserId: The current user <kuid> (equivalent to request.context.token.userId).

Permission Function Args #

The main purpose of the "closure" behavior is to grant permissions based on the current state of the storage layer. This means that, in order to determine if an action can be granted, we will first need to fetch documents used in the permission function from the storage layer.

Documents fetched from the storage layer are stored in the args object as defined by the fetch definition.

Each args object will look like:

Copied to clipboard!
{
  "content": {}, // the document itself
  "id": "<document id>"
}

In the sample role above (return args.document.content.user.id === $currentUserId), the update action is allowed only if the fetched document contains an attribute user.id with value equal to the current user's id.


The Fetch Definition #

The Fetch Definition allows you to pass documents fetched from the persistence layer to your Permission Function.

In our sample role above, the document variable references the document we want to update and it is used in the Permission Function to test if the current user is the document owner.

Fetch Definition Predefined Variables #

There are a set of predefined variables which are automatically accessible in a Fetch Definition, these are:

  • $request: The complete request object being evaluated.
  • $currentId: The current request document id (equivalent to $request.input.resource._id).

Fetch Definition Args #

We define the Fetch Definition in a args object with the following structure:

Copied to clipboard!
{
  "args": {
    "<some variable>": {
      "index": "<index from which to fetch the document(s)>",
      "collection": "<collection from which to fetch the document(s)>",
      "action": {
        "<action type (get|mget|search)>": {} // <action type specific parameters>
      }
    },
    "<another variable>": {
      // ...
    },
    // ...
  }
}

You can define one or more variables inside the args object and, for each variable, define the action used to populate it. Each of these variables will then be available in the permission function, accessible in the args object as follows: args.<variable>.

Fetch Definition Actions #

GET #

The get action type fetches a document by its id. For example:

Copied to clipboard!
{
  "args": {
    "currentDocument": {
      "index": "$request.input.resource.index",
      "collection": "$request.input.resource.collection",
      "action": {
        "get": "$currentId"
      }
    },
    "anotherDocument": {
      "index": "myIndex",
      "collection": "myCollection",
      "action": {
        "get": "document_id"
      }
    }
  }
}

In the args object above, we declare the following Fetch Definition:

  • currentDocument which represents the document that the user wants to update and whose Fetch Definition is composed of:
    • index: the index where the collection resides
    • collection: the collection in the index
    • $currentId: the document id as defined in the get request
  • anotherDocument which represents another document, just as an example, fetched the same way as the previous one but with different parameters.
MGET #

The mget action type accepts a list of document ids and returns the list of matching documents.

Copied to clipboard!
{
  "args": {
    "myDocuments": {
      "index": "myIndex",
      "collection": "myCollection",
      "action": {
        "mget": [
          "id_1",
          "id_2",
          // ...
        ]
      }
    }
  }
}

In the args object, we declare a multi-valued Fetch Definition. Notice how the mget action takes an array of ids rather than a single value.

These documents are then accessed in the Permission Function as follows:

Copied to clipboard!
args.myDocuments = [
  {
    id: "id_1", content: {name: "Document 1", description: "Cum sociis natoque penatibus et magnis dis parturient montes"},
  }
  {
    id: "id_2", content: {name: "Document 2", description: "nascetur ridiculus mus. Nulla nunc velit"},
  }
  ...
]

The search action type performs a search on the persistence layer and returns the resulting documents. It is a typical document search. For example:

Copied to clipboard!
{
  "args": {
    "myDocuments": {
      "index": "myIndex",
      "collection": "myCollection",
      "action": {
        "search": {
          "filter": {
            "match": {
              "name": "$request.input.body.name"
            }
          }
        }
      }
    }
  }
}

The search results are available in the Permission Function as an array of documents fetched from myIndex/myCollection, for which the name attribute matches the name attribute of the request:

Copied to clipboard!
args.myDocuments = [
  { id: "id_1", content: {name: "foo", description: "Cum sociis natoque penatibus et magnis dis parturient montes"}},
  { id: "id_2", content: {name: "foo bar", description: "nascetur ridiculus mus. Nulla nunc velit"}},
  ...
]

The content of action.search is passed directly to Elasticsearch.

Please refer to our Elasticsearch Cookbook for more help on how to build your search query.