# Security Guillotina provides an imperative security system. Permissions are computed for a given node in the resource tree using some concept we are going to describe in this document. ## Basics We'll be explaining the security system by showing examples. Fist, make sure to follow the steps from [Getting started](../narrative.html). Now you should have a resource tree that we can represent like the following: ``` db └── todo ├── └── ``` Where `db` is the database, `todo` a container with to content inside. More than that we need some users in order to be able to compute permssion(s) for them, to do so we are going to install [guillotina_dbusers](https://github.com/guillotinaweb/guillotina_dbusers), once installed create two users, let's say "Bob" and "Alice". You can find more information about this addon especially how to get **Bearer Authorization JWT** see [training's users section](../../training/extending/users.html). Note that at this moment the resource tree can be represented like this: ``` db └── todo ├── ├── ├── users │ ├── Bob │ └── Alice └── groups ``` Now login with "Bob" and try access `/db/todo` endpoint: ```eval_rst .. http:example:: curl wget httpie python-requests GET /db/todo/ HTTP/1.1 Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I Host: localhost:8080 HTTP/1.1 401 Unauthorized Content-Type: application/json { "auths": [ "Bob" ], "content": "< Container at /todo by 140237937521992 >", "reason": "You are not authorized to view" } ``` Like you can see in the response you are not authorized to view. Let's grant "Bob" view permission for this `db/todo/` resource tree node: ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo/@sharing HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "prinperm": [ { "permission": "guillotina.ViewContent", "principal": "bob", "setting": "Allow" } ] } HTTP/1.1 200 OK Content-Type: application/json ``` You can now access to `/db/todo` endpoint using Bob user: ```eval_rst .. http:example:: curl wget httpie python-requests GET /db/todo/ HTTP/1.1 Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MzIyNTM3NDcsImlkIjoiQm9iIn0.1-JbNe1xNoHJgPEmJ05oULi4I9OMGBsviWFHnFPvm-I Host: localhost:8080 HTTP/1.1 200 OK Content-Type: application/json { "@id": "http://localhost:8080/db/todo", "@name": "todo", "@type": "Container", "@uid": "6e63e13b4d1647d5a4ef5ef61ea040f1", "UID": "6e63e13b4d1647d5a4ef5ef61ea040f1", "__behaviors__": [], "__name__": "todo", "creation_date": "2018-07-22T07:33:19.098099+00:00", "is_folderish": true, "items": [ { "@id": "http://localhost:8080/db/todo/9eca9e3e84ce4e79883f19fdbbe694b1", "@name": "9eca9e3e84ce4e79883f19fdbbe694b1", "@type": "ToDo", "@uid": "6e6|9eca9e3e84ce4e79883f19fdbbe694b1", "UID": "6e6|9eca9e3e84ce4e79883f19fdbbe694b1" }, { "@id": "http://localhost:8080/db/todo/ae45417c8115463aa2d6437de3577d02", "@name": "ae45417c8115463aa2d6437de3577d02", "@type": "ToDo", "@uid": "6e6|ae45417c8115463aa2d6437de3577d02", "UID": "6e6|ae45417c8115463aa2d6437de3577d02" }, { "@id": "http://localhost:8080/db/todo/groups", "@name": "groups", "@type": "GroupManager", "@uid": "6e6|ff31eda7808044488dc492d2075e4e13", "UID": "6e6|ff31eda7808044488dc492d2075e4e13" }, { "@id": "http://localhost:8080/db/todo/users", "@name": "users", "@type": "UserManager", "@uid": "6e6|753405ce2dfe4455930c8fc850f38157", "UID": "6e6|753405ce2dfe4455930c8fc850f38157" } ], "length": 4, "modification_date": "2018-07-22T09:33:13.486834+00:00", "parent": {}, "title": "ToDo List", "type_name": "Container", "uuid": "6e63e13b4d1647d5a4ef5ef61ea040f1" } ``` What we've done so far looks like we've grant user "Bob" view access to this node, but that's not totally true. As you can see in the permission definition we grant permission to a **principal**. A principal is like a tag and we grant permission to that tag. The user's id is the "principal" here but more on that later. Another important thing is the `setting` attribute, which defined the permission **propagation** in the resource tree, this attribute can have only three value: - Allow: set on resource and children will inherit - Deny: set on resource and children will inherit (good way to stop propagation) - AllowSingle: set on resource and children will not inherit (also a good way to stop propagation) - Unset: you remove the setting Note that we've defined a permission with "Allow" propagation setting at this level of the resource tree: ``` db └── todo <-- permission was granted here ├── ├── ├── users │ ├── Bob │ └── Alice └── groups ``` Which means that user "Bob" can view todo container, but also all todo, users and groups, but cannot `db` database. Try it. The last parameter in our permission definition we've talk so far is the **permission** parameter itself, Guillotina provided a lot of permission by default, you can find an exhaustive like by reading [Guillotina permissions definitions from the source code](https://github.com/plone/guillotina/blob/master/guillotina/permissions.py). Most of the application permissions your application should be defined there, but you can also define your own, more on that later. ## Groups Groups can be assigned to users, each group names are also principals, this is the way we can add principals to users. Let's add a group named `todo_viewer`: ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo/groups HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "@type": "Group", "id": "todo_viewer", "title": "TODO Viewer" } HTTP/1.1 201 Created Content-Type: application/json Location: http://localhost:8080/db/todo/groups/todo_viewer { "@id": "http://localhost:8080/db/todo/groups/todo_viewer", "@name": "todo_viewer", "@type": "Group", "@uid": "6e6|ff3|14f624ef23094362961df0e083cd77e4", "groupname": null, "id": "todo_viewer", "title": null, "roles": [], "users": { "items": [], "items_total": 0 } } ``` And add "Bob" and "Alice" to that group. ```eval_rst .. http:example:: curl wget httpie python-requests PATCH /db/todo/users/Bob HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "user_groups": [ "todo_viewer" ] } HTTP/1.1 204 No Content Content-Type: application/json ``` ```eval_rst .. http:example:: curl wget httpie python-requests PATCH /db/todo/users/Alice HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "user_groups": [ "todo_viewer" ] } HTTP/1.1 204 No Content Content-Type: application/json ``` Let's grant `todo_viewer` view permission for this `db/todo/` resource tree node: ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo/@sharing HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "prinperm": [ { "permission": "guillotina.ViewContent", "principal": "todo_viewer", "setting": "Allow" } ] } HTTP/1.1 200 OK Content-Type: application/json ``` Now Alice should be able to view todo container and all it's children. At the moment Alice can view users and groups which is not convenient for a `todo_viewer` group, let's deny that. ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo/users/@sharing HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "prinperm": [ { "permission": "guillotina.ViewContent", "principal": "todo_viewer", "setting": "Deny" } ] } HTTP/1.1 200 OK Content-Type: application/json ``` ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo/groups/@sharing HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "prinperm": [ { "permission": "guillotina.ViewContent", "principal": "todo_viewer", "setting": "Deny" } ] } HTTP/1.1 200 OK Content-Type: application/json ``` Now both Alice and Bob can't access to users and groups, if you want Bob to be able to access those endpoints you should explicitly set permission for principal "Bob" on those ones. ## Roles Roles are granted permissions, which means that a principal with one role will inherit all that role permissions. Guillotina defined serval default roles, see [developer roles section](../../developer/roles.html). But remember that you can defined your own ones(more on that later). For example let's give to principal "Alice" the `guillotina.Editor` role on `/db/todo/` resource tree node, which grants the following permissions: - guillotina.AccessContent - guillotina.ViewContent - guillotina.ModifyContent - guillotina.ReindexContent To do use run (don't forget to replace `` with your first todo id): ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo//@sharing HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "prinrole": [ { "principal": "alice", "role": "guillotina.Editor", "setting": "Allow" } ] } HTTP/1.1 200 OK Content-Type: application/json ``` Now Alice can access, view, modify and reindex the first todo but Bob still only view to it. You can also add permission for a role at/from a given resource tree node for a given role, for example at the moment principal "Alice" which have "guillotina.Editor" role on `/db/todo/` cannot delete it. Let's fix that by giving "guillotina.DeleteContent" permission to "guillotina.Editor" role at this specific resource tree node: ```eval_rst .. http:example:: curl wget httpie python-requests POST /db/todo//@sharing HTTP/1.1 Accept: application/json Authorization: Basic cm9vdDpyb290 Content-Type: application/json Host: localhost:8080 { "roleperm": [ { "permission": "guillotina.DeleteContent", "role": "guillotina.Editor", "setting": "Allow" } ] } HTTP/1.1 200 OK Content-Type: application/json ``` Now anyone who have "guillotina.Editor" role on that node, including only Alice at the moment, will be able to delete it. ## Security Levels Security for every operation is managed against three definitions (in order of priority): + Local + Global + Code "Local" means for a given resource tree node. "Global" stand for application level. And finally "Code" for code level, like services, containers or whatever code exposed to the API. This means that principal or role could be mandatory to access them. Locals can defined: - Permission for principal with propagation definition - Role for principal with propagation definition - Permission for role with propagation definition Globals: - Role for principal - Permission for principal Code: - Role for principal - Permission for principal - Permission for role ### Roles There are two kind of roles: Global and Local. The ones that are defined to be local can't be used globally and vice-versa. On indexing, the global roles are the ones that are indexed for security in addition to the flat user/group information from each resource. ## Python helper functions ```python # Code to get the global roles that have access_content to an object from guillotina.security.utils import get_roles_with_access_content get_roles_with_access_content(obj) # Code to get the user list that have access content to an object from guillotina.security.utils import get_principals_with_access_content get_principals_with_access_content(obj) # Code to get all the security info from guillotina.security.utils import settings_for_object settings_for_object(obj) # Code to get the Interaction object ( security object ) from guillotina.interfaces import ISecurityPolicy policy = get_utility(ISecurityPolicy) # Get the list of global roles for a user and some groups policy.global_principal_roles(principal, groups) # Get if the authenticated user has permission on a object policy.check_permission(permission, obj) ``` ## REST APIs ### Get all the endpoints and their security `[GET] APPLICATION_URL/@apidefinition` (you need `guillotina.GetContainers` permission) ### Get the security info for a resource (with inherited info) `[GET] RESOURCE/@sharing` (you need `guillotina.SeePermissions` permission) ### Modify the local roles/permission for a resource `[POST] RESOURCE/@sharing` (you need `guillotina.ChangePermissions` permission) ```json { "prinperm": [ { "principal": "foobar", "permission": "guillotina.ModifyContent", "setting": "Allow" } ], "prinrole": [ { "principal": "foobar", "role": "guillotina.Owner", "setting": "Allow" } ], "roleperm": [ { "permission": "guillotina.ModifyContent", "role": "guillotina.Member", "setting": "Allow" } ] } ``` ### Propagation setting The different types are: - Allow: set on resource and children will inherit - Deny: set on resource and children will inherit (good way to stop propagation) - AllowSingle: set on resource and children will not inherit (also a good way to stop propagation) - Unset: you remove the setting