Skip to main content

Create resources and roles with a Node.js policy script

Write a Node.js script that defines your resources and roles in code and applies them to a Permit.io environment with the Node.js SDK. The script creates each resource and role that doesn't exist yet, and updates the permissions of roles that already exist. You can run the script again after you change the definitions. This guide is for backend developers who keep the policy schema in source control instead of editing it in the Permit dashboard.

Prerequisites

Parts of the policy script

The full policy script has four parts, in this order.

1. Create a Permit client

The script imports Permit from the permitio package and creates a client with an API key read from the PERMIT_API_KEY environment variable. The script calls only the Permit API, so it needs no PDP URL. The API key selects the environment that the script changes.

An API key in the script changes that environment's policy

Anyone with your environment API key can change that environment's policy and data through the Permit API. Export the key in the shell that runs the script (export PERMIT_API_KEY=<YOUR_API_KEY>), and don't commit the key to your repository.

2. Define resources and roles

The script defines two arrays. Each item in resources is a ResourceCreate object with key, name, and actions. Each item in roles is a RoleCreate object with key, name, and permissions in the format resource:action. The three resources in the script share one actions object, because they have the same actions. Define every resource and action that the permissions of your roles reference. For all fields, see Create a resource with the Node.js SDK and Create a role with the Node.js SDK.

3. Apply the definitions in createPolicy

The createPolicy function loops over both arrays. For each resource, the function reads the resource with permit.api.resources.get() and creates it when the read fails. For each role, the function reads the role, creates it when the read fails, and otherwise compares the role's current permissions with the defined permissions and changes only the difference.

4. Run createPolicy and log the result

The script calls createPolicy(), prints done when every resource and role is applied, and prints the error otherwise.

Full policy script

The full script defines three resources (employee, task, and folder) with create, read-all, delete, and export actions, and a manager role. For each resource and role, the script works as follows:

ItemExists in PermitWhat the script does
ResourceNoCreates the resource with permit.api.resources.create().
ResourceYesLogs resource already exists and leaves the resource unchanged. The script doesn't update the actions of an existing resource.
RoleNoCreates the role with permit.api.roles.create().
RoleYes, with the same permissionsLogs permissions for role: <key> are up to date.
RoleYes, with different permissionsRemoves the permissions the role has and the definition doesn't with permit.api.roles.removePermissions(), then adds the permissions the definition has and the role doesn't with permit.api.roles.assignPermissions().

The script treats any error from permit.api.resources.get() or permit.api.roles.get() as "doesn't exist" and tries to create the item.

const { Permit } = require('permitio');

// The script calls only the Permit API, so it needs an API key and no PDP URL.
// Read the API key from the environment instead of hardcoding it in the script.
const permit = new Permit({
token: process.env.PERMIT_API_KEY,
});

// Every resource in this script has the same four actions.
const actions = {
'create': { name: 'create' },
'read-all': { name: 'read-all' },
'delete': { name: 'delete' },
'export': { name: 'export' },
};

const resources = [
{ key: 'employee', name: 'Employee', actions },
{ key: 'task', name: 'Task', actions },
{ key: 'folder', name: 'Folder', actions },
];

const roles = [
{
key: 'manager',
name: 'Manager',
description: 'Can export and delete employees',
permissions: ['employee:export', 'employee:delete'],
},
];

const createPolicy = async () => {
// create each resource that does not exist yet
for (const resource of resources) {
let resourceExists;
try {
resourceExists = await permit.api.resources.get(resource.key);
} catch (err) {
console.log('no resource: ' + resource.key);
}

if (!resourceExists) {
console.log('creating resource: ' + resource.key);
await permit.api.resources.create(resource);
} else {
console.log('resource already exists: ' + resource.key);
}
}

// create each role that does not exist yet, and align the permissions of the roles that do
for (const role of roles) {
let roleExists;
try {
roleExists = await permit.api.roles.get(role.key);
} catch (err) {
console.log('no role: ' + role.key);
}

if (!roleExists) {
console.log('creating role: ' + role.key);
await permit.api.roles.create(role);
continue;
}

console.log('role already exists: ' + role.key);
const current = roleExists.permissions ?? [];
const toAdd = role.permissions.filter((permission) => !current.includes(permission));
const toRemove = current.filter((permission) => !role.permissions.includes(permission));

if (toAdd.length === 0 && toRemove.length === 0) {
console.log('permissions for role: ' + role.key + ' are up to date');
continue;
}

console.log('updating permissions for role: ' + role.key);
if (toRemove.length > 0) {
await permit.api.roles.removePermissions(role.key, toRemove);
}
if (toAdd.length > 0) {
await permit.api.roles.assignPermissions(role.key, toAdd);
}
}
};

createPolicy()
.then(() => {
console.log('done');
})
.catch((err) => {
console.log(err);
});

Verify the policy script

  1. Export your API key, save the full script as sync-policy.js, and run it. Replace <YOUR_API_KEY> with the API key of the environment you want to change:

    export PERMIT_API_KEY=<YOUR_API_KEY>
    node sync-policy.js

    In an empty environment, the first run logs a no resource: <key> line and a creating resource: <key> line for each of employee, task, and folder, then no role: manager and creating role: manager, and ends with done.

  2. Run the script a second time. The second run logs resource already exists: <key> for each resource, role already exists: manager, permissions for role: manager are up to date, and done.

  3. In the Permit dashboard, open the Policy Editor. The employee, task, and folder resources and the manager role appear, and manager has the export and delete permissions on employee.

Next steps