8. Permissions and Rules¶
The Cog chatbot system comes equipped with a comprehensive and flexible authorization system which allows operators fine-grained control over who is able to execute chat commands, extending even to control over particular invocations of chat commands.
In this document, we will discuss the individual pieces of the authorization system and take a look at how it is used in practice.
8.1. Authorization Rules¶
At the core of Cog’s authorization system are rules. Each time a user issues a chat command to Cog, rules governing the execution of that command are looked up and applied to the current invocation. If a match is found, the list of permissions the invoking user has is consulted to see if it includes the permission(s) required in the matching rule. If it does, the command is executed; if not, command processing immediately stops.
To make things concrete, we’ll start with a simple authorization rule. (There is actually a separate rule language that can be used to make rather complex rules, but for now we’ll start simple. Feel free to read more detailed explanations of how rules are formed. In any event, the details of the language are orthogonal to the authorization system itself.)
when command is operable:bundle must have operable:manage_commands
This is the simplest form of rule, and gives us all we need to discuss
the authorization system. This rule states, in English, that for a user
to execute the operable:bundles
chat command (which allows users to
enable or disable entire bundles of commands at once), they must have
the operable:manage_commands
permission granted to them.
With that rule in place, let’s say I type the following command invocation in my chat application:
!operable:bundle disable github
I’m telling Cog to disable all the commands in the github
bundle. In order for that command to
be executed, Cog must verify, according to the rule above, that I have
the operable:manage_commands
permission. It just so happens that I
do, so the command succeeds; now nobody can check how many pull requests
are open on their favorite repository.
Perhaps you want to restrict the ability to disable a particularly
important bundle; perhaps you’ve written one called prod
to help
manage your organization’s production environment. We can add this with
a new rule that matches the invocation
!operable:bundle disable prod
That rule might look like this:
when command is operable:bundle
with arg[0] == "disable"
and arg[1] == "prod"
must have site:manage_prod and operable:manage_commands
Here we can see a rule that applies to a very specific invocation of a
command. If you have the operable:manage_commands
permission, you
can manipulate bundles in general, but in order to disable the prod
bundle, you must have the additional site:manage_prod
permission.
As you can imagine, the ability to define rules like this offers a lot of power. We’ll talk more in depth about rules later; the remainder of this document will delve into the specifics of the authorization system itself, explaining the its components and how they all work together.
Rules can be viewed, created, and deleted using the operable:rules
command. In particular, simple rules of the form
when command is <COMMAND> must have <PERMISSION>
can be created
thusly:
``` !operable:rule create <COMMAND> <PERMISSION> ``` Note that
both <COMMAND>
and <PERMISSION>
must exist, and be typed as
fully-qualified names.
8.2. Components of the Authorization System¶
8.2.1. Permissions¶
Permissions are at the base of Cog’s authorization system. They act as a kind of token; you can carry out certain actions if you possess the correct token(s).
You will notice that permissions have a structure:
operable:manage_commands
, site:manage_prod
, etc. Permissions are
namespaced; here we have a manage_commands
permission in the
operable
namespace, and a manage_prod
permission in the site
namespace. In general, every bundle of commands defines its own
permission namespace. This allows bundle authors the flexibility to
define permissions that are used by commands in the bundle without
worrying about conflicting with permissions from any other bundles that
happen to be installed on a Cog system. We can have an
operable:manage_commands
permission as well as a
site:manage_commands
permission without any problems.
There are two ways that permissions can be created. The first is through bundle installation. All command bundles have the option to define permissions and authorization rules to help bootstrap the bundle in a Cog system. The operator is not under any obligation to use these rules or permissions, and is free to define their own, but they are always installed with the bundle.
The second way that permissions can be created is directly by the Cog
operator. This is where the special site
namespace comes into play.
site
is unique; it is the only permission namespace that is not
associated with a corresponding command bundle. All permissions created
by operators are part of the site
namespace. It is the mechanism by
which the permission scheme may be customized to the needs of the
operator’s unique environment and use cases.
8.2.2. Roles¶
Moving up from permissions, we arrive at roles, which are collections of associated permissions. While permissions can be created when you install command bundles, roles are something purely under your control as a Cog operator; you create them and you manage them.
8.2.3. Users¶
In order for permissions to be useful, we have to have a way to associate them with people invoking commands. The Cog user is the unique identity to which permissions are ultimately attached.
Each person that can interact with Cog has an associated user account. This is also the identity with which a person will interact with Cog’s REST API.
It is important to understand that this “Cog User” is not the same as
a person’s “handle” in a particular chat system. In fact, a Cog user can
be associated with multiple handles from different chat systems. For
instance, I may be @cm
in HipChat and @chris
in Slack, but
cmaier
in Cog. Cog can recognize this and map these various chat
handles back to the same Cog user, allowing authorization to be managed
centrally and independently of which chat system is in use.
Users are scoped to the entire Cog installation; that is, there is no higher-level namespace (e.g., “organization”) into which users are grouped.
8.2.4. Groups¶
Finally, Cog groups collect Cog users together. Any number of users may be in a group, but only users may be members of groups.
8.3. Bringing It All Together¶
Now that you know about permissions, roles, users, and groups, how do you use them?
We know that roles are collections of permissions, and groups are collections of users, but that ultimately, somehow, permissions become associated with users. This missing link here is that roles can be granted to groups.
Thus, a user has all the permissions in all the roles granted to all the groups of which she is a member.
To grant a permission to a user, then, the user must be placed into a group that has been granted a role that contains that permission. While this might seem a bit cumbersome from the perspective of a single user and a single permission, it makes global management easier; it frees you to think in terms of the higher-level constructs of roles and groups, without having to worry about “exceptions to the rule” like individual users being directly granted a permission, or potentially complicated group hierarchies.
Warning
For those that have used Cog before version 0.4, this document describes a departure from the previous permission scheme, where users and groups could be granted permissions directly, and groups could also contain groups.
As an example, let’s look at how we might set up a Cog system to grant permissions for the mist EC2 command bundle. For this demonstration, let’s say we have three users: Alice, Bob, and Charlie. Furthermore, let’s say that Alice is on our Operations team, while Bob and Charlie are on the Development team. Let’s also stipulate that everyone on the operations team should be able to perform any action with Mist, while developers start out with read-only permissions.
Looking at Mist’s bundle configuration, we see it declares the following permissions:
- mist:view
- mist:change-state
- mist:destroy
- mist:create
- mist:manage-tags
- mist:change-acl
It looks like we’ll want to give operations folks all of these
permissions, and developers only mist:view
. Let’s set up some roles
to express this.
First a mist_admin
role, with all the mist permissions:
cogctl role create mist_admin
cogctl role grant mist_admin mist:view
cogctl role grant mist_admin mist:change_state
cogctl role grant mist_admin mist:destroy
cogctl role grant mist_admin mist:create
cogctl role grant mist_admin mist:manage-tags
cogctl role grant mist_admin mist:change-acl
And now, a mist_read_only
role:
cogctl role create mist_read_only
cogctl role grant mist_read_only mist:view
Now we have our roles, but we have nothing to grant them to. Let’s create some groups.
cogctl group create operations
cogctl group create developers
Now let’s grant the roles to our new groups.
cogctl group grant operations mist_admin
cogctl group grant developers mist_read_only
We’re almost there. We have the groundwork laid; all that remains is to add our users.
cogctl group add operations alice
cogctl group add developers bob charlie
Any changes to the permission structure take effect immediately. If the
mist:view
permission is removed from the mist_read_only
role,
Bob and Charlie immediately lose the ability to run commands that
require that permission (unless they happen to also be members of
another group that has the permission via some other role). Similarly,
if Danielle is added to the operations
group, she immediately has
all the mist
permissions.
Note also that all authorization rules are written in terms of permissions, and not roles,