The Fluidinfo permissions system

Here is a description of the permissions system used in Fluidinfo. Some terminology and things to bear in mind:

  • There are two things in Fluidinfo that are subject to permissions control: namespaces and tags. These are treated in a uniform manner.

  • Users attempt to carry out actions on namespaces and tags.

  • It is important to understand the difference between a tag itself and the (possibly zero, possibly many) occurrences of that tag on Fluidinfo objects. To illustrate, we might have a tag fred/rating that Fred intends to use to rate things. The Fred user initially creates the tag itself, but at that point has not used it to tag anything. The tag exists, but its set of occurrences on objects is still empty.

    When Fred decides to rate something, say with a 6, a tag with that value is created and attached to a Fluidinfo object. If he later rates another object as a 3, another fred/rating tag is created and attached to the other object.

    For the purposes of permissions checking, Fluidinfo considers what we'll call the tag itself to be different from the set of its occurrences on objects. Using Fluidinfo permissions you could, for example, grant permission to others to alter occurrences of fred/rating (e.g., change a 3 rating to a 4), but not permission to alter the tag itself (e.g., its description).

  • Permissions are checked every time an application, on behalf of either a user or itself, uses the Fluidinfo API to attempt to perform an action on a namespace or tag.

  • To denote the object that corresponds to a namespace or tag, e.g., fred/rating, we will use OBJ(fred/rating).

  • In case you are asking yourself about permissions on objects: Objects in Fluidinfo have no permissions.

Actions

Here are the actions that can be taken on namespaces, tags themselves, and sets of tag occurrences.

Namespaces

The available actions on namespaces are:

  • create - create namespaces or tags in a given namespace.
  • update - change the properties (e.g., description) of a namespace.
  • delete - delete the namespace, which must be empty.
  • list - see the names of contained namespaces and tags.

Tags themselves

The available actions on a tag itself are:

  • update - change the tag itself, e.g., its description.
  • delete - delete the tag (and all its occurrences).

Sets of tag occurrences

The available actions on the set of occurrences of a tag are:

  • create - add a tag to an object.
  • read - read the value of a tag on an object.
  • delete - remove a tag from an object.

Checking permissions

The permission for each action are implemented as a policy (set to either "open" or "closed"), and zero or more exceptions to the policy. An API call is allowed to proceed if the permission for the action is either

  1. an "open" policy and the user not in the exceptions, or
  2. a "closed" policy and the user in the exceptions.

Permission information for namespaces and tags is stored in instances of other Fluidinfo tags. That is, Fluidinfo uses (other) tags to store permissions about namespaces and tags. This is a conceptually simple approach, but it needs some care when thinking about changing permissions - who has permission to change a permission? We'll answer that below.

Permission tags live under the following Fluidinfo namespaces:

  • fluiddb/namespaces/permission
  • fluiddb/tags/permission
  • fluiddb/tag-values/permission

For each action for each of these categories, there are two corresponding tags: one for the policy and one for its exception list. For example, permission information about who can create a tag in a namespace is stored in instances of the two tags

  • fluiddb/namespaces/permission/create/policy
  • fluiddb/namespaces/permission/create/exceptions

So those are the tags involved in permissions checking. But what about the occurrences of those tags, encoding access to a specific namespace or tag?

Instances of the permission tags are stored on objects that corresponds to the namespaces and tags. E.g., the permissions information for a tag fred/rating is stored in permission tag instances on OBJ(fred/rating).

To be more concrete, suppose a user is trying to delete the tag fred/rating. Fluidinfo will examine the tags fluiddb/tags/permission/delete/policy and fluiddb/tags/permission/delete/exceptions on OBJ(fred/rating) for the open/closed policy and its exceptions.

As a second example, suppose there is a namespace fred/books. The object associated with it, OBJ(fred/books), will have tags fluiddb/namespaces/permission/create/policy and fluiddb/namespaces/permission/create/exceptions on it. The values of those tags indicate which Fluidinfo users are allowed to create namespaces or tags in fred/books. Similarly, OBJ(fred/books) will also have instances of fluiddb/namespaces/permission/update/policy, fluiddb/namespaces/permission/update/exceptions, etc., for each of the other actions relevant to namespaces.

Note that the permissions for a tag itself and the permissions for its occurrences are both stored on the same object. There is no possibility of collision because the tags holding permissions for the tag itself are under the fluiddb/tags/permission namespace while those for its occurrences are under the fluiddb/tag-values/permission namespace.

Changing permissions

When taking an action on a tag, we have to consider whether it is a permission tag. In the simple case, when it is not a permission tag, Fluidinfo decides whether to allow the action using the normal policy/exception permissions tags as described above.

Otherwise, the user is trying to change a permission tag. Here we do not use the regular permission system to hold information about permissions because that would be circular.

Instead, to check whether a user is allowed to alter a permission tag, two other policy/exception atttributes are consulted. These are the permission control tags, located alongside the regular permissions tags in the namespaces

  • fluiddb/namespaces/permission
  • fluiddb/tags/permission
  • fluiddb/tag-values/permission
For example, the tags controlling permission over tags are fluiddb/tags/permission/control/policy and fluiddb/tags/permission/control/exceptions. As with regular permissions tags, instances of the control tags are also stored on objects corresponding to specific namespaces and tags.

That means that there are really two kinds of permission tags: regular and control. We need to consider how instances of these two types can be changed.

Changing a regular permission tag

When a user attempts an operation on a regular (non-control) permission tag under any of

  • fluiddb/namespaces/permission
  • fluiddb/tags/permission or
  • fluiddb/tag-values/permission
they are necessarily attempting to change the permission for another namespace or tag. We know that because these permission tags only appear on objects that correspond to namespaces and tags. (Actually, this is not 100% correct: each of the permission tags is also on the user's object - to hold the user's default permissions settings for the new namespaces or tags they create.)

In these cases we consult the policy and exception list given by the instance of the control tag on the same object.

Inverting the policy on a regular permission tag

When changing a policy from "open" to "closed" on a non-control permission tag, the instance of the exceptions tag is set to the empty set. It is probably an error to leave a non-empty exceptions list intact: the point of changing a policy from "open" to "closed" is to keep users out by default, and that policy should definitely apply to the users who were already excluded. Leaving the exceptions list intact would be granting permission to those who were formerly explicitly excluded due to the previous "open" policy.

Similarly, when changing a policy from "closed" to "open", the instance of the exceptions tag is set to the empty set. Users who were on the old exceptions list (i.e., who had access) should continue to have access. Not clearing the exceptions list would deny them access under the new "open" policy.

Changing a control permission tag

When a user tries to change a permission control tag, we simply check the policy and exceptions list of the control tag.

That means that a user who has control over a namespace or tag can remove/grant control over that thing for others. It also means that a user with control can remove themselves from the group of users with control.

Inverting the policy on a control permission tag

The comments about clearing the exceptions list when a policy is changed on a regular permission tag also apply to a control permission tag, but with one variation.

If a control policy is changed to "closed", the user making the change is added to the exceptions list. This is legitimate, seeing as that user must have originally had control to even be making this change. We are simply preserving that user's control. If we did not put the user into the exceptions list, no-one would be able to control the permissions on the namespace, tag, or tag instance set.

It is still possible for someone with control to take away control for everyone, including themselves. They just have to set the policy to "closed" (if it is not already) and then remove themselves from the exceptions list. Note that this might be a desirable state of affairs. For example, one could put a tag on a number of objects and then set permissions so no further occurrences of the tag could be created by anyone. You would do this by first setting the policy of the create action for the tag to be closed, with no exceptions. Then, set the policy for the control permission of the tag to be closed. Then remove yourself from the exceptions list of the control tag. At that point no-one has permission to add the tag to objects, and no-one has the right to change that permission list. If the permissions tags are readable, others can verify this fact.

Setting permissions on new namespaces and tags

When a user creates a new namespace or tag, the policy and exceptions for each of the relevant permission actions are copied from tags on the user's own object onto the object created for the new namespace or tag.

I.e., we copy the values of fluiddb/namespaces/permission/create/policy and fluiddb/namespaces/permission/create/exceptions etc., for all permission tags from the user's object into new occurences of the same tags on the newly-created object created for the new namespace or tag.

Setting permission defaults for new users

When a new user is created in Fluidinfo, a set of permission defaults is set for them. These are the permissions that will be used on the namespaces and tags that the user creates in the future, as just described.

The values for system-wide default permissions for new users, are found in fluiddb/default/namespaces/permission/create/policy and fluiddb/default/namespaces/permission/create/exceptions etc. tags on the system administrator's object and are copied into the corresponding tags fluiddb/namespaces/permission/create/policy and fluiddb/namespaces/permission/create/exceptions etc. that are placed onto the freshly-created object for the new user.

All permission tags

Here is a list of all permissions tags. As described above, these tags appear on the objects for all users, namespaces, and tags.

  • fluiddb/namespaces/permission/create/policy
  • fluiddb/namespaces/permission/create/exceptions
  • fluiddb/namespaces/permission/update/policy
  • fluiddb/namespaces/permission/update/exceptions
  • fluiddb/namespaces/permission/delete/policy
  • fluiddb/namespaces/permission/delete/exceptions
  • fluiddb/namespaces/permission/list/policy
  • fluiddb/namespaces/permission/list/exceptions
  • fluiddb/namespaces/permission/control/policy
  • fluiddb/namespaces/permission/control/exceptions
  • fluiddb/tags/permission/update/policy
  • fluiddb/tags/permission/update/exceptions
  • fluiddb/tags/permission/delete/policy
  • fluiddb/tags/permission/delete/exceptions
  • fluiddb/tags/permission/control/policy
  • fluiddb/tags/permission/control/exceptions
  • fluiddb/tag-values/permission/write/policy
  • fluiddb/tag-values/permission/write/exceptions
  • fluiddb/tag-values/permission/read/policy
  • fluiddb/tag-values/permission/read/exceptions
  • fluiddb/tag-values/permission/delete/policy
  • fluiddb/tag-values/permission/delete/exceptions
  • fluiddb/tag-values/permission/control/policy
  • fluiddb/tag-values/permission/control/exceptions
And here are all the system default permission tags. A single occurrence of each of these is stored on the system administrator's object.
  • fluiddb/default/namespaces/permission/create/policy
  • fluiddb/default/namespaces/permission/create/exceptions
  • fluiddb/default/namespaces/permission/update/policy
  • fluiddb/default/namespaces/permission/update/exceptions
  • fluiddb/default/namespaces/permission/delete/policy
  • fluiddb/default/namespaces/permission/delete/exceptions
  • fluiddb/default/namespaces/permission/list/policy
  • fluiddb/default/namespaces/permission/list/exceptions
  • fluiddb/default/namespaces/permission/control/policy
  • fluiddb/default/namespaces/permission/control/exceptions
  • fluiddb/default/tags/permission/update/policy
  • fluiddb/default/tags/permission/update/exceptions
  • fluiddb/default/tags/permission/delete/policy
  • fluiddb/default/tags/permission/delete/exceptions
  • fluiddb/default/tags/permission/control/policy
  • fluiddb/default/tags/permission/control/exceptions
  • fluiddb/default/tag-values/permission/write/policy
  • fluiddb/default/tag-values/permission/write/exceptions
  • fluiddb/default/tag-values/permission/read/policy
  • fluiddb/default/tag-values/permission/read/exceptions
  • fluiddb/default/tag-values/permission/delete/policy
  • fluiddb/default/tag-values/permission/delete/exceptions
  • fluiddb/default/tag-values/permission/control/policy
  • fluiddb/default/tag-values/permission/control/exceptions