1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-27 00:55:06 +03:00
awx/docs/rbac.md

167 lines
7.5 KiB
Markdown
Raw Normal View History

2016-02-11 00:59:31 +03:00
# Role-Based Access Control (RBAC)
This document describes the RBAC implementation of the Ansible Tower Software.
The intended audience of this document is the Ansible Tower developer.
## Overview
2016-04-12 17:11:38 +03:00
### RBAC - System Basics
2019-09-11 23:27:28 +03:00
There are three main concepts to be familiar with: Roles, Resources, and Users.
2016-05-13 19:01:54 +03:00
Users can be members of a role, which gives them certain access to any
resources associated with that role, or any resources associated with "descendent"
roles.
For example, if I have an organization named "MyCompany" and I want to allow
2019-09-11 23:27:28 +03:00
two people, "Alice", and "Bob", access to manage all of the settings associated
2016-05-13 19:01:54 +03:00
with that organization, I'd make them both members of the organization's `admin_role`.
It is often the case that you have many Roles in a system, and you want some
2016-05-13 19:01:54 +03:00
roles to include all of the capabilities of other roles. For example, you may
want a System Administrator to have access to everything that an Organization
Administrator has access to, who has everything that a Project Administrator
has access to, and so on. We refer to this concept as the 'Role Hierarchy', and
2019-09-11 23:27:28 +03:00
is represented by allowing roles to have "Parent Roles". Any permission that a
role has is implicitly granted to any parent roles (or parents of those
parents, and so on). Of course roles can have more than one parent, and
2016-05-13 19:01:54 +03:00
capabilities are implicitly granted to all parents. (Technically speaking, this
forms a directional acyclic graph instead of a strict hierarchy, but the
concept should remain intuitive.)
![Example RBAC hierarchy](img/rbac_example.png?raw=true)
2016-02-12 19:02:12 +03:00
### Implementation Overview
The RBAC system allows you to create and layer roles for controlling access to resources. Any Django Model can
2019-09-11 23:27:28 +03:00
be made into a resource in the RBAC system by using the `ResourceMixin`. Once a model is accessible as a resource, you can
2016-05-13 19:01:54 +03:00
extend the model definition to have specific roles using the `ImplicitRoleField`. Within the declaration of
this role field you can also specify any parents the role may have, and the RBAC system will take care of
2019-09-11 23:27:28 +03:00
all of the appropriate ancestral binding that takes place behind the scenes to ensure that the model you've declared
2016-05-13 19:01:54 +03:00
is kept up to date as the relations in your model change.
2016-02-11 00:59:31 +03:00
### Roles
Roles are defined for a resource. If a role has any parents, these parents will be considered when determining
2016-02-11 00:59:31 +03:00
what roles are checked when accessing a resource.
ResourceA
|-- AdminRole
ResourceB
| -- AdminRole
|-- parent = ResourceA.AdminRole
2019-09-11 23:27:28 +03:00
When a user attempts to access ResourceB, we will check for their access using the set of all unique roles, including the parents.
2016-02-11 00:59:31 +03:00
2016-02-12 19:42:00 +03:00
ResourceA.AdminRole, ResourceB.AdminRole
2016-02-11 00:59:31 +03:00
2016-02-12 19:42:00 +03:00
This would provide any members of the above roles with access to ResourceB.
2016-02-11 00:59:31 +03:00
2016-02-12 17:41:30 +03:00
#### Singleton Role
2019-09-11 23:27:28 +03:00
There is a special case _Singleton Role_ that you can create. This type of role is for system-wide roles.
2016-02-12 17:41:30 +03:00
### Models
2016-02-11 00:59:31 +03:00
The RBAC system defines a few new models. These models represent the underlying RBAC implementation and generally will be abstracted away from your daily development tasks by the implicit fields and mixins.
2016-02-11 00:59:31 +03:00
2016-02-12 17:41:30 +03:00
#### `Role`
`Role` defines a single role within the RBAC implementation. It encapsulates the `ancestors`, `parents`, and `members` for a role. This model is intentionally kept dumb and it has no explicit knowledge of a `Resource`. The `Role` model (get it?), defines some methods that aid in the granting and creation of roles.
2016-02-12 17:41:30 +03:00
2016-04-12 17:11:38 +03:00
##### `visible_roles(cls, user)`
2016-02-12 17:41:30 +03:00
2019-09-11 23:27:28 +03:00
`visible_roles` is a class method that will look up all of the `Role` instances a user can "see". This includes any roles the user is a direct descendent of as well as any ancestor roles.
2016-02-12 17:41:30 +03:00
2016-04-12 17:11:38 +03:00
##### `singleton(cls, name)`
2016-02-12 17:41:30 +03:00
2016-04-12 17:11:38 +03:00
The `singleton` class method is a helper method on the `Role` model that helps in the creation of singleton roles. It will return the role by name if it already exists or create and return the new role in the case it does not.
##### `get_absolute_url(self)`
`get_absolute_url` returns the consumable URL endpoint for the `Role`.
2016-02-12 17:41:30 +03:00
##### `rebuild_role_ancestor_list(self)`
2016-02-12 17:41:30 +03:00
2016-05-13 19:01:54 +03:00
`rebuild_role_ancestor_list` will rebuild the current role ancestry that is stored in the `ancestors` field of a `Role`. This is called for you by `save` and different Django signals.
2016-02-11 00:59:31 +03:00
2016-04-12 17:11:38 +03:00
##### `is_ancestor_of(self, role)`
`is_ancestor_of` returns if the given `role` is an ancestor of the current `Role` instance.
2016-05-13 19:01:54 +03:00
##### `user in role`
2016-05-13 19:01:54 +03:00
You may use the `user in some_role` syntax to check and see if the specified
user is a member of the given role, **or** a member of any ancestor role.
2016-02-11 00:59:31 +03:00
2016-02-12 17:41:30 +03:00
### Fields
2016-02-11 00:59:31 +03:00
2016-02-12 17:41:30 +03:00
#### `ImplicitRoleField`
2016-02-11 00:59:31 +03:00
2016-05-13 19:01:54 +03:00
`ImplicitRoleField` fields are declared on your model. They provide the definition of grantable roles for accessing your resource. You may (and should) use the `parent_role` parameter to specify any parent roles that should inherit privileges implied by the role.
2016-05-13 19:01:54 +03:00
`parent_role` is the link to any parent roles you want considered when a user
is requesting access to your resource. A `parent_role` can be declared as a
single string, `"parent.read_role"`, or a list of many roles,
`['parentA.read_role', 'parentB.read_role']` which will make each listed role a parent. You can also use the syntax
`[('parentA.read_role', 'parentB.read_role'), 'parentC.read_role']` to make
`(parentA.read_role OR parentB.read_role) AND 'parentC.read_role` parents (so `parentB.read_role` will be added only if `parentA.read_role` was `None`).
If any listed role can't be evaluated (for example if there are `None` components in the path), then they are simply ignored until the value of the field changes.
2016-02-12 17:41:30 +03:00
### Mixins
2016-02-11 00:59:31 +03:00
2016-02-12 17:41:30 +03:00
#### `ResourceMixin`
By mixing in the `ResourceMixin` to your model, you are turning your model in to a resource in the eyes of the RBAC implementation. Your model will gain the helper methods that aid in the checking the access a users roles provides them to your resource.
2016-05-13 19:01:54 +03:00
##### `accessible_objects(cls, user, role_field)`
2016-05-13 19:01:54 +03:00
`accessible_objects` is a class method to use instead of `Model.objects`. This method will restrict the query of objects to only those that the user has access to - specifically those objects which the user is a member of the specified role (either directly or indirectly).
```python
2016-05-13 19:01:54 +03:00
objects = MyModel.accessible_objects(user, 'admin_role')
objects.filter(name__istartswith='december')
```
2016-02-12 17:41:30 +03:00
##### `get_permissions(self, user)`
2016-05-13 19:01:54 +03:00
`get_permissions` is an instance method that will give you the list of role names that the user has access to for a given object.
```python
>>> instance.get_permissions(admin)
2016-05-13 19:01:54 +03:00
['admin_role', 'execute_role', 'read_role']
```
## Usage
2019-09-11 23:27:28 +03:00
After exploring the _Overview_, the usage of the RBAC implementation in your code should feel unobtrusive and natural.
```python
# make your model a Resource
class Document(Model, ResourceMixin):
...
# declare your new role
readonly_role = ImplicitRoleField(
role_name="readonly",
permissions={'read':True},
)
```
2019-09-11 23:27:28 +03:00
Now that your model is a resource and has a `Role` defined, you can begin to access the helper methods provided to you by the `ResourceMixin` for checking a user's access to your resource. Here is the output of a Python REPL session:
```python
# we've created some documents and a user
>>> document = Document.objects.filter(pk=1)
>>> user = User.objects.first()
2016-05-13 19:01:54 +03:00
>>> user in document.read_role
False # not accessible by default
>>> document.readonly_role.memebers.add(user)
2016-05-13 19:01:54 +03:00
>>> user in document.read_role
True # now it is accessible
2016-05-13 19:01:54 +03:00
>>> user in document.admin_role
False # my role does not have admin permission
```