20 December, 2017

Take control of token access permissions with flexible ACLs [UPD: Apr 30, 2019]

Manage the scope of elements and modules that a user can access within the flespi platform.

Not everyone needs access to all data and all objects within the flespi platform, right? Access control is a handy mechanism to give different permissions to different types of platform users.

Not so long ago we added access restrictions for API tokens using access control lists (ACLs). Now it’s easy to customize permissions for various tokens (see the Tokens menu item on flespi.io) depending on the requirements: e.g. token working with specific channels/devices/streams only, token with read-only access to collect statistics from the platform components, etc.

ACL basics

What’s not allowed is forbidden. ACL mechanism only grants access — by default all actions are denied for the token with enabled ACL and any action not specified in ACL is forbidden.

Token types. The access property determines the "power" of the token — i.e. how far it can reach.

  • Standard — a basic token sufficient for working with all Telematics hub features of the flespi platform (does not allow creating other tokens).
  • Master — the almighty token granting access to the flespi platform API and allowing the creation of other tokens. 
  • ACL — a flexible type of token allowing customization of permissions by module and object type. There is no way to allow tokens to create other tokens via ACL. ACL mechanism is not designed for flespi administration.

flespi token type

Token ACL consists of access control entries (ACEs). ACE contains all the necessary data to allow the specific action for the token. Since all the ACL stuff applies to the flespi platform REST API, the ACE structure contains module names or object types, HTTP requests names, and optional object identifiers. Let's take a closer look at the ACE fields:

  • Module name or object type (URI field in API). The first value you should select when creating a new ACE in ACL. It defines what specific platform module or object type the ACE applies to: flespi token aclAfter picking the module/object type, click the Add button to customize the new ACE.
  • Allowed HTTP requests (methods field in API). Determines which of the four HTTP request methods (GET, POST, PUT, DELETE) can apply to the specified module or objects (see the previous step).
    flespi token acl http method
  • Objects identifiers (ids field in API) [optional]. Grant access to a specific set of objects. Obvious note: Only identifiers belonging to the current customer account are allowed.

    Possible object identifier values:
    • specific ids — grant access to the objects with listed ids
      flespi token acl object id
    • all keyword — allows the token to access all existing objects of the specified type and all objects of this type created in the future (for the current customer)
      flespi token acl all
    • disabled (eye icon crossed) — enables the user to not only access but also manage (create) objects of the given type.
  • Submodules (NEW!) — you can separately fine-tune permissions for different submodules within a module. E.g. you can grant access to the device settings while not allowing to modify the device itself:
    flespi token acl submodule

Note: You can add several ACEs for a single token. To do so just pick another module in the acl dropdown and click Add — the new ACE will be added to the list. To delete one of the ACEs from the token click the red trash bin icon at the bottom of the ACE configuration.

flespi token several aces

Token for MQTT broker

One of the standard scenarios is the need for a token to access the flespi MQTT broker. To create one, pick the mqtt module from the drop-down and customize the topic, actions, and methods depending on the roles. You may need to create several tokens — one with full privileges, one for consuming messages only, etc.

flespi token mqtt broker

ACLs for the flespi platform

There is a subset of MQTT topics starting with flespi/... carrying real-time and statistical information about the elements of the flespi platform in your account.

To create a token capable of getting this data from respective MQTT topics, you need to create REST ACLs to the desired elements.

NOTE: you shouldn't create an "mqtt" ACL!

E.g. you want to access the stats on the channel with id=10101.  Your ACL should grant access to "gw/channels/", since you only want to read the stats, tick GET method only, and list the "10101" in the id field.

Here's how it looks:
acl token to flespi platform

Now, you'll be able to get current traffic value (flespi/state/gw/channels/10101/traffic topic), channel status (flespi/state/gw/channels/10101/enabled topic), and a lot more:

flespi channel state topics

The same holds true for other flespi elements (e.g. devices, streams, etc.) as well as for the platform as a whole.

Notes

Requests with keyword all in place of identifiers list in the URI work fine in ACL. In such a case, a token only works with the objects it has enough permissions for.

During the validation process, only one ACE is selected to verify the access for the token. This one ACE is the most suitable for the current request by the following criteria:

  • it matches the request URI better than other ACEs (e.g. for request by URI /gw/channels/all/messages ACE with URI field gw/channels matches better than ACE with URI field gw; for the same request ACE with URI field gw/channels and with specified identifiers matches better than ACE with URI field gw/channels and without identifiers)
  • it matches the request HTTP method.

These criteria apply to the ACEs in ACL sequentially. The first and the only one matching ACE is selected to continue the validation process. If the matching ACE does not contain identifiers value, the current request processing continues as is. Otherwise, access only for the specified identifiers is allowed. If there are more ACEs matching the selection criteria, they are not taken into account. If no matching ACE is found, access to the current request is denied at all.

From the previous paragraph, it follows that ACL can contain any number of ACEs with the same specified platform module or objects and their identifiers. It allows to reduce the size and to improve the readability of an ACE. But the current implementation requires putting specifications for the same objects or modules and the same request type into one ACE.
Short example. Say there is an ACL with two ACEs: the first allows PUT requests for gw/devices objects with identifiers 1 and 2, the second allows PUT requests for gw/devices objects with identifiers 1, 2, and 3. The attempt to modify the device 3 via PUT request would be declined because the first most suitable ACE allows using PUT with devices 1 and 2 only.

Examples

So, for now, I have four tokens in my account:

  • master token (used to login to the flespi panel and to access to the flespi platform API)
  • token with granted access (read via GET, and update via PUT) to the two of my channels
  • token with granted read-only access to the whole storage module
  • token with full permissions to storage/containers
curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nafbfWtmEYCjHva4PqsloIQeLgWOSAc3yf2E2jOw7gEEBEwhVPs9n7bWdYATmBKK' \
'https://flespi.io/platform/customer/tokens/all?fields=key,acl'
{
"result": [
{
"acl": [],
"key": "nafbfWtmEYCjHva4PqsloIQeLgWOSAc3yf2E2jOw7gEEBEwhVPs9n7bWdYATmBKK"
},
{
"acl": [
{
"ids": [
2025,
2026
],
"methods": [
"GET",
"PUT"
],
"uri": "gw/channels"
}
],
"key": "UGw2y0OswbAdg4OjjfLplZ0pfqfr9KNRf8JBWlTimjlqcYzj18yaXnp1DviB3Tg6"
},
{
"acl": [
{
"methods": [
"GET"
],
"uri": "storage"
}
],
"key": "nFkUSDbhXAiCgGfrn35rMfANB2wIls5xVdhDEgJsEPUKl01wRXNu0d5r5p0fFTCo"
},
{
"acl": [
{
"methods": [
"GET",
"POST",
"PUT",
"DELETE"
],
"uri": "storage/containers"
}
],
"key": "G13pmI0T2CuPAOPydVvvmqXVi3nD1pWveQKooz7fZsSp6xL7g6qqkcct5nWVAIgE"
}
]
}

Let's work with three tokens with enabled ACL and compare the results against the master token.

curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nafbfWtmEYCjHva4PqsloIQeLgWOSAc3yf2E2jOw7gEEBEwhVPs9n7bWdYATmBKK' \
'https://flespi.io/gw/channels/all?fields=id'
{"result":[{"id":2024},{"id":2025},{"id":2026}]}
curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken UGw2y0OswbAdg4OjjfLplZ0pfqfr9KNRf8JBWlTimjlqcYzj18yaXnp1DviB3Tg6' \
'https://flespi.io/gw/channels/all?fields=id'
{"result":[{"id":2025},{"id":2026}]}

As you can see, the second token requested all tokens but received only those allowed for access by this token. What about the third channel?

curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken UGw2y0OswbAdg4OjjfLplZ0pfqfr9KNRf8JBWlTimjlqcYzj18yaXnp1DviB3Tg6' \
'https://flespi.io/gw/channels/2024?fields=id'
{"errors":[{"code":6,"id":2024,"reason":"access denied to '\/channels\/2024'"}],"result":[]}

No access — as we expected. Now let's try to disable the channels.

curl -X PUT -H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"enabled": false}'\
-H 'Authorization: FlespiToken UGw2y0OswbAdg4OjjfLplZ0pfqfr9KNRf8JBWlTimjlqcYzj18yaXnp1DviB3Tg6' \
'https://flespi.io/gw/channels/all?fields=id,enabled'
{"result":[{"enabled":false,"id":2025},{"enabled":false,"id":2026}]}
curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nafbfWtmEYCjHva4PqsloIQeLgWOSAc3yf2E2jOw7gEEBEwhVPs9n7bWdYATmBKK' \
'https://flespi.io/gw/channels/all?fields=id,enabled'
{"result":[{"enabled":true,"id":2024},{"enabled":false,"id":2025},{"enabled":false,"id":2026}]}

Even though I've tried to disable all the channels ACL allowed me to disable only two out of three.

And the final action. Let's clean the channel messages and see what we get.

curl -X DELETE -H 'Accept: application/json' -H 'Content-Type: application/json' \
-d '{"delete_key": 1513762868}' \
-H 'Authorization: FlespiToken UGw2y0OswbAdg4OjjfLplZ0pfqfr9KNRf8JBWlTimjlqcYzj18yaXnp1DviB3Tg6' \
'https://flespi.io/gw/channels/all/messages'
{"errors":[{"code":8,"reason":"action is not permitted by ACL"}],"result":[]}

Yes, ACL is still here guarding flespi customers against dishonest token usage.

OK, enough with channels. There are two more tokens with different permissions to the storage module. What do we have in this module?

curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nFkUSDbhXAiCgGfrn35rMfANB2wIls5xVdhDEgJsEPUKl01wRXNu0d5r5p0fFTCo' \
'https://flespi.io/gw/channels/all?fields=id'
{"errors":[{"code":8,"reason":"action is not permitted by ACL"}],"result":[]}

Oops. Yes, this token is for the storage module only so focus on it.

curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nFkUSDbhXAiCgGfrn35rMfANB2wIls5xVdhDEgJsEPUKl01wRXNu0d5r5p0fFTCo' \
'https://flespi.io/storage/containers/all?fields=id'
{"result":[{"id":5126},{"id":5127},{"id":5128}]}
curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nFkUSDbhXAiCgGfrn35rMfANB2wIls5xVdhDEgJsEPUKl01wRXNu0d5r5p0fFTCo' \
'https://flespi.io/storage/abques/all?fields=id'
{"result":[{"id":7756},{"id":7757},{"id":7758}]}

What about the last token?

curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken G13pmI0T2CuPAOPydVvvmqXVi3nD1pWveQKooz7fZsSp6xL7g6qqkcct5nWVAIgE' \
'https://flespi.io/storage/containers/all?fields=id'
{"result":[{"id":5126},{"id":5127},{"id":5128}]}
curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken G13pmI0T2CuPAOPydVvvmqXVi3nD1pWveQKooz7fZsSp6xL7g6qqkcct5nWVAIgE' \
'https://flespi.io/storage/abques/all?fields=id'
{"errors":[{"code":8,"reason":"action is not permitted by ACL"}],"result":[]}

No surprise the token is for containers only. And this token has full permissions not only for containers objects but also for the entire containers submodule.

curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '[{"name": "container 42"}]' \
-H 'Authorization: FlespiToken G13pmI0T2CuPAOPydVvvmqXVi3nD1pWveQKooz7fZsSp6xL7g6qqkcct5nWVAIgE' \
'https://flespi.io/storage/containers'
{"result":[{"flags":0,"id":5129,"name":"container 42"}]}

Success! And both storage-related tokens can see this new container too.

curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken G13pmI0T2CuPAOPydVvvmqXVi3nD1pWveQKooz7fZsSp6xL7g6qqkcct5nWVAIgE' \
'https://flespi.io/storage/containers/5129'
{"result":[{"flags":0,"id":5129,"name":"container 42"}]}
curl -X GET -H 'Accept: application/json' \
-H 'Authorization: FlespiToken nFkUSDbhXAiCgGfrn35rMfANB2wIls5xVdhDEgJsEPUKl01wRXNu0d5r5p0fFTCo' \
'https://flespi.io/storage/containers/5129'
{"result":[{"flags":0,"id":5129,"name":"container 42"}]}

***

As you can see from the numerous examples above, the access control mechanism empowers customers of the flespi platform to manage permissions to the elements of the infrastructure depending on the needs and duties of specific users. Tighten the bolts where needed and give full control where allowed.

BTW, we also prepared a video tutorial on how to create a flespi token: