Pyramid ACL » History » Version 1

Thomas Mielke, 10/05/2019 08:10 PM

1 1 Thomas Mielke
# Pyramid ACL
2 1 Thomas Mielke
3 1 Thomas Mielke
{{toc}}
4 1 Thomas Mielke
5 1 Thomas Mielke
## Background
6 1 Thomas Mielke
7 1 Thomas Mielke
In Pyramid, every traversal resource might have an [Access Conrol List](https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/security.html#assigning-acls-to-your-resource-objects), which is a set of rules containing
8 1 Thomas Mielke
9 1 Thomas Mielke
* an action (Allow, Deny)
10 1 Thomas Mielke
* a principal (an arbitrary string usually denoting a role, group or userid)
11 1 Thomas Mielke
* a set of permissions to allow or deny
12 1 Thomas Mielke
13 1 Thomas Mielke
An example of a rule:
14 1 Thomas Mielke
15 1 Thomas Mielke
    (Allow, 'some_group', 'some_permission')
16 1 Thomas Mielke
17 1 Thomas Mielke
Every view in pyramid might be [protected](https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/security.html#protecting-views-with-permissions) by a permission.
18 1 Thomas Mielke
The permission is tested against the principals of the current web user, when the view is entered (after traversal and view lookup).
19 1 Thomas Mielke
20 1 Thomas Mielke
## Overview
21 1 Thomas Mielke
22 1 Thomas Mielke
In Portal we have two different levels of Access Roles:
23 1 Thomas Mielke
24 1 Thomas Mielke
* global web user roles
25 1 Thomas Mielke
* instance level object roles
26 1 Thomas Mielke
27 1 Thomas Mielke
The names of the permissions should reflect the action of the web user (e.g. 'edit_artist') and should be unique, if possible.
28 1 Thomas Mielke
The only exception is the permission 'authenticated', which is granted for web users after login and might be used for all views, which should be accessible for authenticated web users in general.
29 1 Thomas Mielke
30 1 Thomas Mielke
## Global Level
31 1 Thomas Mielke
32 1 Thomas Mielke
A web user has one or more roles corresponding to the portal plugins (e.g. licenser  for portal.plugin.repertoire).
33 1 Thomas Mielke
Each plugin should have it's own role to ensure that the web user is only able to use the corresponding parts of the application.
34 1 Thomas Mielke
35 1 Thomas Mielke
### Persistence
36 1 Thomas Mielke
37 1 Thomas Mielke
The web user roles are persisted in tryton:
38 1 Thomas Mielke
39 1 Thomas Mielke
* WebUserRole (web.user.role)
40 1 Thomas Mielke
41 1 Thomas Mielke
The web user roles are imported via `collection_society.xml`.
42 1 Thomas Mielke
The imported standard roles are:
43 1 Thomas Mielke
44 1 Thomas Mielke
* licenser (for portal.plugin.repertoire)
45 1 Thomas Mielke
* licensee (for portal.plugin.events)
46 1 Thomas Mielke
47 1 Thomas Mielke
### Retrieval
48 1 Thomas Mielke
49 1 Thomas Mielke
The web user roles can be retrieved via the roles method of the WebUser Model:
50 1 Thomas Mielke
51 1 Thomas Mielke
    web_user.current_roles(request)
52 1 Thomas Mielke
53 1 Thomas Mielke
For convenience, the list of roles are added as request property:
54 1 Thomas Mielke
55 1 Thomas Mielke
    request.roles
56 1 Thomas Mielke
57 1 Thomas Mielke
### Application
58 1 Thomas Mielke
59 1 Thomas Mielke
The principals are automatically assigned to a web user via [callback](https://docs.pylonsproject.org/projects/pyramid/en/latest/api/authentication.html#pyramid.authentication.AuthTktAuthenticationPolicy) of the [groupfinder](https://docs.pylonsproject.org/projects/pyramid/en/latest/tutorials/wiki/authorization.html#add-users-and-groups) method of WebUser, after the web user has been authenticated.
60 1 Thomas Mielke
61 1 Thomas Mielke
To set permissions on a resource to a principal:
62 1 Thomas Mielke
63 1 Thomas Mielke
    class SomeResource(ResourceBase):
64 1 Thomas Mielke
        
65 1 Thomas Mielke
        __acl__ = [
66 1 Thomas Mielke
            (Allow, '<PRINCIPAL>', (
67 1 Thomas Mielke
                '<PERMISSION1>',
68 1 Thomas Mielke
                '<PERMISSION2>',
69 1 Thomas Mielke
                ...
70 1 Thomas Mielke
            )),
71 1 Thomas Mielke
        ]
72 1 Thomas Mielke
73 1 Thomas Mielke
To prevent inheritance of ACL from parent resources (e.g. for the main resource of a plugin), add DENY_ALL as the last rule:
74 1 Thomas Mielke
75 1 Thomas Mielke
    class SomeResource(ResourceBase):
76 1 Thomas Mielke
        
77 1 Thomas Mielke
        __acl__ = [
78 1 Thomas Mielke
            ...,
79 1 Thomas Mielke
            DENY_ALL
80 1 Thomas Mielke
        ]
81 1 Thomas Mielke
82 1 Thomas Mielke
## Instance Level
83 1 Thomas Mielke
84 1 Thomas Mielke
Every web user might take one or more access roles for each acl capable object (e.g. Artist, Release, Creation, Content) via an access control entry.
85 1 Thomas Mielke
Every access role is associated with a set of permissions to be granted.
86 1 Thomas Mielke
87 1 Thomas Mielke
Additional to the permissions codes used in the pyramid views directly (e.g. view_artist, view_release, etc.), there are permissions to reflect transitive rights for objects down the hierarchy (e.g. view_arist_creations, view_arist_releases).
88 1 Thomas Mielke
89 1 Thomas Mielke
### Persistence
90 1 Thomas Mielke
91 1 Thomas Mielke
All access related objects are persisted in tryton and assigned to a model via the AccessControlList mixin, see also the [[Databasemodels#ACL|database model]]:
92 1 Thomas Mielke
93 1 Thomas Mielke
* AccessControlEntry (ace)
94 1 Thomas Mielke
* AccessRole (ace.role)
95 1 Thomas Mielke
* AccessPermission (ace.permission)
96 1 Thomas Mielke
97 1 Thomas Mielke
The access permissions are imported via `collection_society.xml` and readonly for the tryton client, as the codes of the permissions are hardcoded.
98 1 Thomas Mielke
The access roles are free to edit and each role might be associated with any set of existing permissions.
99 1 Thomas Mielke
The names of the access roles should be chosen according to the semantics of the role to be taken.
100 1 Thomas Mielke
101 1 Thomas Mielke
The imported standard roles are:
102 1 Thomas Mielke
103 1 Thomas Mielke
* Administrator (all permissions)
104 1 Thomas Mielke
* Stakeholder (view related permissions only)
105 1 Thomas Mielke
106 1 Thomas Mielke
### Retrieval
107 1 Thomas Mielke
108 1 Thomas Mielke
To retrieve permission related information, every acl capable model has two instance methods:
109 1 Thomas Mielke
110 1 Thomas Mielke
* `instance.permits()`: asks the instance, if a web user has a certain permission:
111 1 Thomas Mielke
112 1 Thomas Mielke
        instance.permits(web_user, permission_code)
113 1 Thomas Mielke
114 1 Thomas Mielke
* `instance.permissions()`: asks the instance, what permissions a web_user has:
115 1 Thomas Mielke
116 1 Thomas Mielke
        instance.permissions(web_user, valid_permission_codes=False)
117 1 Thomas Mielke
118 1 Thomas Mielke
    The returned permissions might be constrained to permissions in the list of valid codes to tighten security.
119 1 Thomas Mielke
120 1 Thomas Mielke
An example domain to include transitive permissions:
121 1 Thomas Mielke
122 1 Thomas Mielke
    [
123 1 Thomas Mielke
        'OR',
124 1 Thomas Mielke
        [
125 1 Thomas Mielke
            ('acl.web_user', '=', web_user_id),
126 1 Thomas Mielke
            ('acl.roles.permissions.code', '=', '<PERMISSION>')
127 1 Thomas Mielke
        ], [
128 1 Thomas Mielke
            ('<FIELD>.acl.web_user', '=', web_user_id),
129 1 Thomas Mielke
            ('<FIELD>.acl.roles.permissions.code', '=', '<PERMISSION>'),
130 1 Thomas Mielke
        ]
131 1 Thomas Mielke
    ]
132 1 Thomas Mielke
133 1 Thomas Mielke
### Application
134 1 Thomas Mielke
135 1 Thomas Mielke
The permissions are set within the `__acl__` method of a resource and bound to the [authenticated userid](https://docs.pylonsproject.org/projects/pyramid/en/latest/api/request.html#pyramid.request.Request.authenticated_userid):
136 1 Thomas Mielke
137 1 Thomas Mielke
    class SomeResource(ResourceBase):
138 1 Thomas Mielke
        _permit = ['only', 'allow', 'these', 'permissions']
139 1 Thomas Mielke
        
140 1 Thomas Mielke
        def __acl__(self):
141 1 Thomas Mielke
            return [
142 1 Thomas Mielke
                (Allow, self.request.authenticated_userid,
143 1 Thomas Mielke
                    <INSTANCE>.permissions(self.request.web_user, self._permit))
144 1 Thomas Mielke
            ]