REST API Permissions

Permissions handling in the REST API

This document contains information about the permission system used by the REST API data model.

1. Permission System

The REST API has a permission system that allows fine-grained control of user permissions.

Application developers can use the permission settings to configure users that meet specific security requirements.

Each route in the REST API has associated permission settings; and each user has permission settings for each route.

2. Types of Users

The REST API data model has three types of users:

  • Tenant user
  • System admin user
  • System Service user

At the heart of the REST API data model is the tenant object. The tenant holds information about users, devices, packet definitions, and application firmwares.

A tenant can have one or more users. When creating a tenant, a tenant admin user is created automatically. The tenant admin user can then create additional tenant users.

Tenants are created by the system admin user.

Tenant users typically access to the system by logging into a web-based Management Console.

The System Service user is used internally by the system for communication with the REST API from IoT devices.

3. Creating Tenant Users

An application developer typically authenticates using the tenant admin user to manage tenant data and create additional users for the given tenant.

When creating a new tenant users, the developer can choose between two preconfigured permission profiles:

  • admin - a tenant user with full permissions for the given tenant
  • viewer - a tenant user with restricted permissions

In addition, REST API calls can be made to set the individual permissions of an individual user. Permissions can either be added or revoked.

Permissions can be writeable or read-only. Only writable permissions can be modified. Read-only permissions may not be modified by any user (neither the admin user, nor tenant admin users). Read-only permissions are set by the system administrator, and cannot be mofified by the REST API.

4. Permission Entries

A user permission entry contains characters (single character strings) from the acronym “CRUDO” - create (“C”), read (“R”), update (“U”), delete (“D”) and options (“O”) - that directly correlate to the REST API access methods POST, GET, PUT, DELETE and OPTIONS respectively.

Each permission entry corresponds to a REST API route.

4.1. Example: /auth

auth = ["R", "O"]

The user can call GET and OPTIONS on the /auth route.

4.2. Example: /tenant/{id}/firmware_appl

tenant.x.firmware_appl = ["C", "R", "O"]

The user can create new firmware application entries (POST) and read (GET) the list of firmware application entries, and call OPTIONS on the /tenant/{id}/firmware_appl route.

4.3. Example: /tenant/{id}/user/{id}

tenant.x.user.x = ["R", "O"]

The user can read (GET) information about another user and call OPTIONS on the route.

4.4. Example: Permission for Own User (self)

Permissions for the /tenant/{id}/user/{id} family of routes come in two variants; one for general access to users, and one for access to the own user (self).

The underscore character is used in place of “x” to designate the “self” user identity, as shown in the following example:

tenant.x.user._ = ["R", "U", "O"]

The user can read (GET) and update (PUT) information about itself, in additions to call OPTIONS.

5. Default Permissions

The following tables provide a complete listing of all permissions in the system, and their default values for tenant admin and viewer users.

5.1. Tenant Admin User

PermissionValueWritable
auth["R","O"]No
global["R","O"]No
global.keys["R","O"]No
global.meta["R","O"]No
image["R","O"]No
image.x["R","O"]No
image.x.keys["R","O"]No
modem["R","O"]No
modem.x["R","O"]No
modem.x.keys["R","O"]No
modem.x.meta["R","O"]No
firmware_core["R","O"]No
firmware_core.x["R","O"]No
firmware_core.x.keys["R","O"]No
microcontroller["R","O"]No
microcontroller.x["R","O"]No
microcontroller.x.keys["R","O"]No
microcontroller.x.meta["R","O"]No
tenant["O"]No
tenant.x["R","U","O"]Yes
tenant.x.keys["R","U","D","O"]Yes
tenant.x.meta["R","O"]Yes
tenant.x.user["C","R","O"]Yes
tenant.x.user.x["R","U","D","O"]Yes
tenant.x.user.x.keys["R","U","D","O"]Yes
tenant.x.user.x.permissions["R","U","O"]Yes
tenant.x.user.x.meta["R","O"]Yes
tenant.x.user._["R","U","O"]Yes
tenant.x.user._.keys["R","U","D","O"]Yes
tenant.x.user._.permissions["R","O"]Yes
tenant.x.user._.meta["R","O"]Yes
tenant.x.device["C","R","O"]Yes
tenant.x.device.x["R","U","D","O"]Yes
tenant.x.device.x.keys["R","U","D","O"]Yes
tenant.x.device.x.meta["R","O"]Yes
tenant.x.packet["C","R","O"]Yes
tenant.x.packet.x["R","U","D","O"]Yes
tenant.x.packet.x.keys["R","U","D","O"]Yes
tenant.x.packet.x.meta["R","O"]Yes
tenant.x.firmware_appl["C","R","O"]Yes
tenant.x.firmware_appl.x["R","D","O"]Yes
tenant.x.firmware_appl.x.keys["R","O"]Yes
tenant.x.interface["C","R","O"]Yes
tenant.x.interface.x["R","U","D","O"]Yes
tenant.x.interface.x.keys["R","U","D","O"]Yes
tenant.x.interface.x.meta["R","O"]Yes
service[]No

Notice that the tenant admin user has permission to modify the permissions of other tenant users: but not for itself.

tenant.x.user.x.permissions = ["R", "U", "O"]

But it can not modify the permissions for itself:

tenant.x.user._.permissions = ["R", "O"]

5.2. Tenant Viewer User

PermissionValueWritable
auth["R","O"]No
global["R","O"]No
global.keys["R","O"]No
global.meta["R","O"]No
image["R","O"]No
image.x["R","O"]No
image.x.keys["R","O"]No
modem["R","O"]No
modem.x["R","O"]No
modem.x.keys["R","O"]No
modem.x.meta["R","O"]No
firmware_core["R","O"]No
firmware_core.x["R","O"]No
firmware_core.x.keys["R","O"]No
microcontroller["R","O"]No
microcontroller.x["R","O"]No
microcontroller.x.keys["R","O"]No
microcontroller.x.meta["R","O"]No
tenant["O"]No
tenant.x["R","O"]Yes
tenant.x.keys["R","O"]Yes
tenant.x.meta["R","O"]Yes
tenant.x.user["R","O"]Yes
tenant.x.user.x["R","O"]Yes
tenant.x.user.x.keys["R","O"]Yes
tenant.x.user.x.permissions["R","O"]Yes
tenant.x.user.x.meta["R","O"]Yes
tenant.x.user._["R","U","O"]Yes
tenant.x.user._.keys["R","U","D","O"]Yes
tenant.x.user._.permissions["R","O"]Yes
tenant.x.user._.meta["R","O"]Yes
tenant.x.device["R","O"]Yes
tenant.x.device.x["R","O"]Yes
tenant.x.device.x.keys["R","O"]Yes
tenant.x.device.x.meta["R","O"]Yes
tenant.x.packet["R","O"]Yes
tenant.x.packet.x["R","O"]Yes
tenant.x.packet.x.keys["R","O"]Yes
tenant.x.packet.x.meta["R","O"]Yes
tenant.x.firmware_appl["R","O"]Yes
tenant.x.firmware_appl.x["R","O"]Yes
tenant.x.firmware_appl.x.keys["R","O"]Yes
tenant.x.interface["R","O"]Yes
tenant.x.interface.x["R","O"]Yes
tenant.x.interface.x.keys["R","O"]Yes
tenant.x.interface.x.meta["R","O"]Yes
service[]No

Notice that the tenant viewer user cannot modify any permissions (not for itself or other users):

tenant.x.user.x.permissions = ["R", "O"]
tenant.x.user._.permissions = ["R", "O"]

6. How to Read and Update Permissions

The following code examples use the REST API PHP library in file: tools/lib/rest_api_lib.php

6.1. Get Permissions for Authenticated User

Use the /auth route to get a JSON response with permissions for the authenticated user (self):

$response_json = rest_api_get("/auth");
$info = json_decode($response_json);
var_dump($info->permissions);

The permission settings returned by the /auth route are equal to the settings returned by a GET request on the current user (see next section).

Full code example is in file: guide/examples/auth_permissions.php

Click to view auth_permissions.php
File: guide/examples/auth_permissions.php

<?php
/*
File: auth_permissions.php

This example uses a GET request to obtain info about permissions
for the current user.

Returned JSON data is parsed and printed.

Usage:

    php auth_permissions.php LOGINFILE

Example:

    php auth_permissions.php mylogin.json
*/

require_once __DIR__ . "/../../tools/lib/rest_api_lib.php";

// Read config file with user credentials
if ($argc == 2)
{
  $login_config_file = $argv[1];
  $config_path = get_absolute_path($login_config_file);
  rest_api_read_login_config($config_path);
}
else
{
  echo "USAGE:" . PHP_EOL;
  echo "php auth_permissions.php LOGINFILE" . PHP_EOL;
  echo "EXAMPLE:" . PHP_EOL;
  echo "php auth_permissions.php mylogin.json" . PHP_EOL;
  die();
}

// Get user info
$response_json = rest_api_get("/auth");
$user_info = json_decode($response_json);
$permissions = $user_info->permissions;

// List permissions
echo "AVAILABLE PERMISSIONS:\n";
echo "(C = Create, R = Read, U = Update, D = Delete, O = Options)\n";
foreach ($permissions as $route => $array)
{
  echo $route . " : ";
  foreach ($array as $permission)
  {
    echo $permission . " ";
  }
  echo "\n";
}

// It is considered best practice to not close the <?php tag
// in a PHP-only file.

6.2. Get Permissions for Users

Use one of the following routes to get the permissions for a specific user:

  • GET tenant/{id}/user/{id} (get all information about the user, including permissions)
  • GET tenant/{id}/user/{id}/permissions (get all permissions)
  • GET tenant/{id}/user/{id}/permissions/{permission_name} (get individual permission)

When calling the above routes with the user id of the currently autheticated user, the permisson settings are equal to those returned by the /auth request (see previous section).

6.2.1. Get User Info

Get all info for the default tenant admin user (user id 1) and display permissions:

$tenant_id = rest_api_tenant_get_id();
$response_json = rest_api_get("/tenant/{$tenant_id}/user/1");
$info = json_decode($response_json);
var_dump($info->permissions);

6.2.2. Get All Permissions

Get all permissions for the default tenant admin user (user id 1):

$tenant_id = rest_api_tenant_get_id();
$response_json = rest_api_get("/tenant/{$tenant_id}/user/1/permissions");
$permissions = json_decode($response_json);
var_dump($permissions);

6.2.3. Get Individual Permission

Get an individual permission for the default tenant admin user (user id 1):

$tenant_id = rest_api_tenant_get_id();
$response_json = rest_api_get("/tenant/{$tenant_id}/user/1/permissions/tenant.x");
$permission = json_decode($response_json);
var_dump($permission);

6.3. Set Permissions for Users

Use one of these routes to set the permissions for a specific user:

  • PUT tenant/{id}/user/{id} (set all/partial information about the user, including permissions)
  • PUT tenant/{id}/user/{id}/permissions (set all/selected permissions)
  • PUT tenant/{id}/user/{id}/permissions/{permission_name} (set individual permission)

The following examples illustrate the use of each of the above routes.

6.3.1. Set Permission - Partial User Info Object

Set partial user info object with permissions for user with id 2 to create and delete other users:

$tenant_id = rest_api_tenant_get_id();

$data = new stdClass;
$data->permissions = new stdClass;
$data->permissions->{"tenant.x.user.x"} = ["R", "U", "D", "O"];

rest_api_put(
  "/tenant/{$tenant_id}/user/2",
  json_encode($data);,
  "application/json");

$response_code = rest_api_get_http_response_code();
var_dump($response_code); // should be 202

6.3.2. Set Permission - Partial Permissions Object

Set partial permission object with permissions for user with id 2 to create and delete other users:

$tenant_id = rest_api_tenant_get_id();

$permissions = new stdClass;
$permissions->{"tenant.x.user.x"} = ["R", "U", "D", "O"];

rest_api_put(
  "/tenant/{$tenant_id}/user/2/permissions",
  json_encode($permissions);,
  "application/json");

$response_code = rest_api_get_http_response_code();
var_dump($response_code); // should be 202

6.3.3. Set Individual Permission

Set individual permission for user with id 2 to create and delete other users:

$tenant_id = rest_api_tenant_get_id();

rest_api_put(
  "/tenant/{$tenant_id}/user/2/permissions/tenant.x.user.x",
  json_encode(["R", "U", "D", "O"]),
  "application/json");

$response_code = rest_api_get_http_response_code();
var_dump($response_code); // should be 202

7. Permission Groups

A tenant object consists of multiple sub-objects (user, device, packet, application firmware), and the routes for objects have sub-routes for access to keys and meta fields. Permissions for certain routes are are linked to and depend on each other according to a parent/child inheritance relation. For example, setting “R” on a permission for a sub-route implies to it to be “R” on the parent route.

There are two set of rules that are applied when updating permissions for tenant routes and sub-routes, which are described in the following sub-sections.

7.1. Rules for “R” and “U” Permissions

  • When updating permissions and the “R” or “U” permission is not on any of the sub-routes, then the corresponding permission is removed from the parent object route.

  • When updating permissions and the “R” or “U” permission is on any of the sub-routes, then the corresponding permission is added to the parent object route.

The above rules apply to the following permission groups:

tenant.x                       (object route)
tenant.x.keys                  (sub-route)
tenant.x.meta                  (subfields)

tenant.x.user.x                (object route)
tenant.x.user.x.keys           (sub-route)
tenant.x.user.x.meta           (sub-route)
tenant.x.user.x.permissions    (sub-route)

tenant.x.user._                (object route)
tenant.x.user._.keys           (sub-route)
tenant.x.user._.meta           (sub-route)
tenant.x.user._.permissions    (sub-route)

tenant.x.device.x              (object route)
tenant.x.device.x.keys         (sub-route)
tenant.x.device.x.meta         (sub-route)

tenant.x.packet.x              (object route)
tenant.x.packet.x.keys         (sub-route)
tenant.x.packet.x.meta         (sub-route)

tenant.x.firmware_appl.x       (object route)
tenant.x.firmware_appl.x.keys  (sub-route)

7.2. Rules for Collections - “R” Permissions

The following rules apply to routes that read (GET) collections and objects, respectively.

  • When updating permissions and the “R” permission is not on the object route, then the corresponding permission is removed from the parent collection route.

  • When updating permissions and the “R” permission is on the object route, then the corresponding permission is added to the parent collection route.

The above rules apply to the following permission groups:

tenant.x.user                  (collection route)
tenant.x.user.x                (object route)

tenant.x.device                (collection route)
tenant.x.device.x              (object route)

tenant.x.packet                (collection route)
tenant.x.packet.x              (object route)

tenant.x.firmware_appl         (collection route)
tenant.x.firmware_appl.x       (object route)

8. Permissions Schema

The schema for a user contains the maximum permissions allowed for tenant users.

8.1. Schema Permission Entries

In the schema, permission entries always contain all permission options - an uppercase permission means that the permission can be enabled, and a lowercase permission means that the permission cannot be enabled.

As an example, here is the schema entry for accessing devices:

tenant.x.device.x = ["c","R","U","D","O"]

The corresponding route is:

/tenant/{id}/device/{id}

The maximum permission allowed by this schema entry is (“C” is not allowed):

[“R”,“U”,“D”,“O”]

8.2. How to Obtain the Permissions Schema

The permissions schema is fetched using an OPTIONS request.

This code example shows how to get the permissions schema for the default tenant admin user (tenant user with id 1):

$tenant_id = rest_api_tenant_get_id();
$response_json = rest_api_options("/tenant/{$tenant_id}/user/1/permissions");
$permissions_schema = json_decode($response_json);
var_dump($permissions_schema);

8.3. All Schema Permission Entries

The following table shows a list of all entries in the permissions schema for tenant users.

Uppercase permission options represent the maximum permissions allowed for a tenant user. Permissions for lowercase option may not be enabled.

Entries that are not writable are read-only and cannot be modified (not even by the system administrator).

PermissionValueWritable
auth["c","R","u","d","O"]No
global["c","R","U","d","O"]No
global.keys["c","R","U","D","O"]No
global.meta["c","R","u","d","O"]No
image["C","R","u","d","O"]No
image.x["c","R","u","D","O"]No
image.x.keys["c","R","u","d","O"]No
modem["C","R","u","d","O"]No
modem.x["c","R","U","D","O"]No
modem.x.keys["c","R","U","D","O"]No
modem.x.meta["c","R","u","d","O"]No
firmware_core["C","R","u","d","O"]No
firmware_core.x["c","R","u","D","O"]No
firmware_core.x.keys["c","R","u","d","O"]No
microcontroller["C","R","u","d","O"]No
microcontroller.x["c","R","U","D","O"]No
microcontroller.x.keys["c","R","U","D","O"]No
microcontroller.x.meta["c","R","u","d","O"]No
tenant["C","R","u","d","O"]No
tenant.x["c","R","U","d","O"]Yes
tenant.x.keys["c","R","U","D","O"]Yes
tenant.x.meta["c","R","u","d","O"]Yes
tenant.x.user["C","R","u","d","O"]Yes
tenant.x.user.x["c","R","U","D","O"]Yes
tenant.x.user.x.keys["c","R","U","D","O"]Yes
tenant.x.user.x.permissions["c","R","U","d","O"]Yes
tenant.x.user.x.meta["c","R","u","d","O"]Yes
tenant.x.user._["c","R","U","d","O"]Yes
tenant.x.user._.keys["c","R","U","D","O"]Yes
tenant.x.user._.permissions["c","R","u","d","O"]Yes
tenant.x.user._.meta["c","R","u","d","O"]Yes
tenant.x.device["C","R","u","d","O"]Yes
tenant.x.device.x["c","R","U","D","O"]Yes
tenant.x.device.x.keys["c","R","U","D","O"]Yes
tenant.x.device.x.meta["c","R","u","d","O"]Yes
tenant.x.packet["C","R","u","d","O"]Yes
tenant.x.packet.x["c","R","U","D","O"]Yes
tenant.x.packet.x.keys["c","R","U","D","O"]Yes
tenant.x.packet.x.meta["c","R","u","d","O"]Yes
tenant.x.firmware_appl["C","R","u","d","O"]Yes
tenant.x.firmware_appl.x["c","R","u","D","O"]Yes
tenant.x.firmware_appl.x.keys["c","R","u","d","O"]Yes
tenant.x.interface["C","R","u","d","O"]Yes
tenant.x.interface.x["c","R","U","D","O"]Yes
tenant.x.interface.x.keys["c","R","U","D","O"]Yes
tenant.x.interface.x.meta["c","R","u","d","O"]Yes
service["c","r","U","d","o"]No

8.4. How to Determine Permissions for Permission Settings

In a tool such as a web-based Management Console, it is desirable to use the permissions settings for the currently authenticated user to determine which actions are allowed. The user interface can then provide feedback to the user about allowed operations.

A Management Console typically also has a user interface for editing user permissions. In a permission editor, one has to determine which permissions are editable, and which permissions can be enabled.

The rules for editing permissions are as follows:

  • The logged in user can not edit its own permissions
  • Permissions that are read-only cannot be modified
  • The “permissions” permission entries governs how a user can modify permissions

The permission tenant.x.user._.permissions has settings that restrict updates of the currently logged in user. In the schema entry, the maximum permissions allowed are “R” and “O”, which means that a user cannot alter its own permissions. Here is the schema entry:

tenant.x.user._.permissions = ["c","R","u","d","O"]

The permission tenant.x.user.x.permissions controls if a user can update the permissions of other users. In the schema entry, the maximum permissions allowed are “R”, “U”, and “O”, which means that if a user has permission “U” enabled, it can edit the permissions of other tenant users. Here is the schema entry:

tenant.x.user.x.permissions</td><td>["c","R","U","d","O"]

The default settings for a viewer user does not allow for editing permissions of other users. However, a viewer user is in essence an admin tenant user with restricted permissions, so the tenant admin can “upgrade” a viewer user to have any permissions allowed by the schema.

To sum up:

  • Read-only permission cannot be edited/changed
  • The schema contains the maximum permissions allowed for any tenant user
  • Editing own permissions is not allowed