Setting ACL

This tutorial will show you how to set ACL rules for different groups of users and for different types of resources.

Components

Using Appmixer ACL feature you can control access to certain components. All of that can be configured from Backoffice or through the API.

If you open Appmixer Backoffice you can see an ACL section in the left menu. Then you can choose among routes and components.

When you open components section you can see default ACL rules. Users with scope admin, user and tester can use all non-private components. Component can be hidden using private property, then you can make these private components visible to certain users using ACL.

This is the default ACL setting you will see.

If you want to set ACL for different component start with deleting the general rule that allows users to access all components. Because if you did not do this step, the ultimate rule user - * - * would overrule any other rule you would create for user scope.

If you open Appmixer now (signed in as ordinary user) you will see no components in Designer.

Let's now add all components from appmixer vendor back with the following rule:

All appmixer components allowed to be used by all users.

When you refresh Appmixer now, you will see all the Appmixer components back.

Let's break down those four properties you can set for each ACL rule.

  • role: admin | user - those are the default roles/scopes in the system.

  • resource: component type prefix (appmixer.google.gmail* for example). This allows to create rules for components belonging to certain vendor, service or module. In the example above we created a rule for all appmixer components. The resource string was appmixer* which will cover all appmixer components.

  • action: action the rule is for. In case of components the only action is use. You can keep it to *. There are more actions when it comes to rules for API routes.

  • attributes: private or non-private. If set to non-private the rule will apply to component that do not have private: true set in component.json. If set to private it will allow users to see private components as well.

Let's try more examples. Show only utils, google and slack components. We're going to add the following three rules:

When you refresh Appmixer Designer you can see this:

Only utils, google and slack components available.

Because we used attribute non-private for all our rules we can see only components where private: true is not set in their manifest. Most appmixer component modules user private auxiliary components for listing items into menus (slack channels for example). Therefore we don't see those private components in this menu:

Let's change the ACL rule for slack module to see those private components as well.

User can see private components now.

When you start writing your own components you should use your own vendor (not appmixer). We are adding Hello World component for example under vendor acme. Its component type is acme.custom.component.HelloWorld. Then we have to publish the component into Appmixer and create new ACL rule that will make it accessible to users.

At this point we can find the new Hello World component among other components.

Another example will show how you can user resource property to display only gmail module.

Routes

You can define ACLs to restrict access to Flows API. The default setting is similar to the one for Components. All roles can access all actions on flows resource.

If you want to limit users from certain role, first you need to delete the general rule. We will show it on user role.

With this setting, any request to any /flows endpoint will result in 403 response code. The following example will show you how to limit access to /flows API for user scope to read only operations.

Read only access to /flows endpoint.

Those rules are store in mongo DB in the following form:

{
"type" : "routes",
"acl" : [
{
"role" : "admin",
"resource" : "flows",
"action" : [
"*"
],
"attributes" : [
"*"
]
},
{
"role" : "tester",
"resource" : "flows",
"action" : [
"*"
],
"attributes" : [
"*"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"read"
],
"attributes" : [
"*"
]
}
]
}

Attributes

The next example is not supported in the Backoffice UI, but can be implemented directly using ACL API. Flows may contain custom metadata. You can use ACL to restrict access to those metadata. We can create a flow with custom metadata like this:

{
"_id" : ObjectId("5f3696f502ea801d405bb112"),
"userId" : ObjectId("5f3696f102ea801d405bb10e"),
"flowId" : "c57f1d30-52fc-4a48-97d9-5d1c296064da",
"stage" : "stopped",
"name" : "Flow with metadata",
"btime" : ISODate("2020-08-14T13:51:49.900Z"),
"mtime" : ISODate("2020-09-01T14:57:43.536Z"),
"flow" : {
},
"mode" : "module",
"sharedWith" : [],
"customFields" : {
"category" : "test-example"
}
}

where flow.customFields.category = "test-example". This can be done through API. But we want only admin users to be able to do so. The following set of ACL rules will allow admins to create custom fields, but it will allow users to only read those fields.

{
"type" : "routes",
"acl" : [
{
"role" : "admin",
"resource" : "flows",
"action" : [
"*"
],
"attributes" : [
"*"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"read"
],
"attributes" : [
"* // are able to read all attributes, including custom fields
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"create"
],
"attributes" : [
"*", "!customFields" // but they're not able to create them
]
}
]
}

So if you try to send an API request to create new flow with the following body (as a user with user scope).

{
"flow":{},
"name":"New flow",
"customFields": {
"category": "test-category"
}
}

The custom field category won't be created. Property customFields will be an empty object. Although if you create the same flow with admin user that customFields property with category will be created and your user with user role will be able to read it (API will return it).

With the following set of rules, users with user role won't be able to create and read property customFields.

{
"type" : "routes",
"acl" : [
{
"role" : "admin",
"resource" : "flows",
"action" : [
"*"
],
"attributes" : [
"*"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"read"
],
"attributes" : [
"*",
"!customFields"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"create"
],
"attributes" : [
"*",
"!customFields"
]
}
]
}

The same way you restrict access to each property on flows collection.

The next JSON shows different syntax with similar functionality. User with role user can read/create flows with properties flow, name, flowId, stage, sharedWith, but they won't be able to create customFields property.

{
"type" : "routes",
"acl" : [
{
"role" : "admin",
"resource" : "flows",
"action" : [
"*"
],
"attributes" : [
"*"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"read",
"create"
],
"attributes" : [
"flow",
"name",
"flowId",
"stage",
"sharedWith"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"!create"
],
"attributes" : [
"customFields"
]
}
]
}

You can also use nested objects as attributes. Let's say the customFields object contains two properties:

{
"_id" : ObjectId("5f4e654c2c2d2f4d59ca89f9"),
"userId" : ObjectId("5f3696f102ea801d405bb10e"),
"flowId" : "46d80c28-72c3-42f1-94ba-2c161e26f041",
"stage" : "stopped",
"name" : "New flow",
"btime" : ISODate("2020-09-01T15:14:20.999Z"),
"mtime" : ISODate("2020-09-01T15:14:20.999Z"),
"flow" : {},
"mode" : "module",
"sharedWith" : [],
"customFields" : {
"visible" : "test visible",
"not-visible" : "test non visible"
}
}

And we want the users to be able to see only visible property, the rules would look like this:

{
"type" : "routes",
"acl" : [
{
"role" : "admin",
"resource" : "flows",
"action" : [
"*"
],
"attributes" : [
"*"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"read"
],
"attributes" : [
"*",
"!customFields.not-visible"
]
},
{
"role" : "user",
"resource" : "flows",
"action" : [
"create"
],
"attributes" : [
"*",
"!customFields"
]
}
]
}

Users are not able to create customFields, they are allowed to read customFields.visible property, but not the customFields.not-visible.