Code

Bugger it. Here's the current shape of the new security implementation.
[roundup.git] / doc / security.txt
1 ===================
2 Security Mechanisms
3 ===================
5 :Version: $Revision: 1.12 $
7 Current situation
8 =================
10 Current logical controls:
12 ANONYMOUS_ACCESS = 'deny'
13  Deny or allow anonymous access to the web interface
14 ANONYMOUS_REGISTER = 'deny'
15  Deny or allow anonymous users to register through the web interface
16 ANONYMOUS_REGISTER_MAIL = 'deny'
17  Deny or allow anonymous users to register through the mail interface
19 Current user interface authentication and controls:
21 - command-line tool access controlled with passwords, but no logical controls
22 - CGI access is by username and password and has some logical controls
23 - mailgw access is through identification using sender email address, with
24   limited functionality available
26 The web interface implements has specific logical controls,
27 preventing non-admin users from accessing:
29  - other user's details pages
30  - listing the base classes (not issues or their user page)
31  - editing base classes
33 Issues
34 ======
36 1. The current implementation is ad-hoc, and not complete for all `use cases`_.
37 2. Currently it is not possible to allow submission of issues through email
38    but restrict those users from accessing the web interface.
39 3. Only one user may perform admin functions.
40 4. There is no verification of users in the mail gateway by any means other
41    than the From address. Support for strong identification through digital
42    signatures should be added.
43 5. The command-line tool has no logical controls.
44 6. The anonymous control needs revising - there should only be one way to be
45    an anonymous user, not two (currently there is user==None and
46    user=='anonymous).
49 Possible approaches
50 ===================
52 Security controls in Roundup could be approached in three ways:
54 1) at the hyperdb level, with read/write/modify permissions on classes, nodes
55    and node properties for all or specific transitions.
56 2) at the user interface level, with access permissions on CGI interface
57    methods, mailgw methods, roundup-admin methods, and so on.
58 3) at a logical permission level, checked as needed.
60 In all cases, the security built into roundup assumes restricted access to the
61 hyperdatabase itself, through Operating System controls such as user or group
62 permissions.
65 Hyperdb-level control
66 ---------------------
68 Control is implemented at the Class.get, Class.set and Class.create level. All
69 other methods must access nodes through these methods. Since all accesses go
70 through the database, we can implement deny by default.
72 Pros:
74    - easier to implement as it only affects one module
75    - smaller number of permissions to worry about
77 Cons:
79    - harder to determine the relationship between user interaction and hyperdb
80      permission.
81    - a lot of work to define
82    - must special-case to handle by-node permissions (editing user details,
83      having private messages)
86 User-interface control
87 ----------------------
89 The user interfaces would have an extra layer between that which
90 parses the request to determine action and the action method. This layer
91 controls access. Since it is possible to require methods be registered
92 with the security mechanisms to be accessed by the user, deny by default
93 is possible.
95 Pros:
97    - much more obvious at the user level what the controls are
99 Cons:
101    - much more work to implement
102    - most user interfaces have multiple uses which can't be covered by a
103      single permission
106 Logical control
107 ---------------
109 At each point that requires an action to be performed, the security mechanisms
110 are asked if the current user has permission. Since code must call the
111 check function to raise a denial, there is no possibility to have automatic
112 default of deny in this situation.
114 Pros:
116    - quite obvious what is going on
117    - is very similar to the current system
119 Cons:
121    - large number of possible permissions that may be defined, possibly
122      mirroring actual user interface controls.
123    - access to the hyperdb must be strictly controlled through program code
124      that implements the logical controls.
127 Applying controls to users
128 ==========================
130 Individual assignment of Permission to User is unwieldy. The concept of a
131 Role, which encompasses several Permissions and may be assigned to many Users,
132 is quite well developed in many projects. Roundup will take this path, and
133 allow the multiple assignment of Roles to Users, and multiple Permissions to
134 Roles. These definitions will be stored in the hyperdb. They don't need to be
135 pushed to the actual database though.
137 There will be two levels of Permission. The Class level permissions define
138 logical permissions associated with all nodes of a particular class (or all
139 classes). The Node level permissions define logical permissions associated
140 with specific nodes by way of their user-linked properties.
142 A security module defines::
144     class InMemoryClass(hyperdb.Class):
145         ''' Just be an in-memory class
146         '''
147         def __init__(self, db, classname, **properties):
148             ''' Set up an in-memory store for the nodes of this class
149             '''
151         def create(self, **propvalues):
152             ''' Create a new node in the in-memory store
153             '''
155         def get(self, nodeid, propname, default=_marker, cache=1):
156             ''' Get the node from the in-memory store
157             '''
159         def set(self, *args):
160             ''' Set values on the node
161             '''
163     class PermissionClass(InMemoryClass):
164         ''' Include the default attributes:
165             - name (String)
166             - klass (String)
167             - description (String)
169             The klass may be unset, indicating that this permission is not
170             locked to a particular class. That means there may be multiple
171             Permissions for the same name for different classes.
172         '''
174     class RoleClass(InMemoryClass):
175         ''' Include the default attributes:
176             - name (String, key)
177             - description (String)
178             - permissions (PermissionClass Multilink)
179         '''
181     class Security:
182         def __init__(self, db):
183             ''' Initialise the permission and role classes, and add in the
184                 base roles (for admin user).
185             '''
187         def hasClassPermission(self, db, classname, permission, userid):
188             ''' Look through all the Roles, and hence Permissions, and see if
189                 "permission" is there for the specified classname.
191             '''
193         def hasNodePermission(self, db, classname, nodeid, **propspec):
194             ''' Check the named properties of the given node to see if the
195                 userid appears in them. If it does, then the user is granted
196                 this permission check.
198                 'propspec' consists of a set of properties and values that
199                 must be present on the given node for access to be granted.
201                 If a property is a Link, the value must match the property
202                 value. If a property is a Multilink, the value must appear
203                 in the Multilink list.
204             '''
206         def addPermission(self, **propspec):
207             ''' Create a new Permission with the properties defined in
208                 'propspec'
209             '''
211         def addRole(self, **propspec):
212             ''' Create a new Role with the properties defined in 'propspec'
213             '''
215         def addPermissionToRole(self, rolename, permissionid):
216             ''' Add the permission to the role's permission list.
218                 'rolename' is the name of the role to add 'permissionid'.
219             '''
221 Modules such as ``cgi_client.py`` and ``mailgw.py`` define their own
222 permissions like so (this example is ``cgi_client.py``)::
224     def initialiseSecurity(security):
225         ''' Create some Permissions and Roles on the security object
227             This function is directly invoked by security.Security.__init__()
228             as a part of the Security object instantiation.
229         '''
230         newid = security.addPermission(name="Web Registration",
231             description="Anonymous users may register through the web")
232         security.addToRole('Anonymous', newid)
234 The instance dbinit module then has in ``open()``::
236     # open the database - it must be modified to init the Security class
237     # from security.py as db.security
238     db = Database(instance_config, name)
240     # add some extra permissions and associate them with roles
241     ei = db.security.addPermission(name="Edit", klass="issue",
242                     description="User is allowed to edit issues")
243     db.security.addPermissionToRole('User', ei)
244     ai = db.security.addPermission(name="Assign", klass="issue",
245                     description="User may be assigned to issues")
246     db.security.addPermissionToRole('User', ei)
248 In the dbinit ``init()``::
250     r = db.getclass('role').lookup('Admin')
251     user.create(username="admin", password=Password(adminpw),
252                 address=instance_config.ADMIN_EMAIL, roles=[r])
254     # choose your anonymous user access permission here
255     #r = db.getclass('role').lookup('No Rego')
256     r = db.getclass('role').lookup('User')
257     user.create(username="anonymous", roles=[r])
259 Then in the code that matters, calls to ``hasClassPermission`` and
260 ``hasNodePermission`` are made to determine if the user has permission
261 to perform some action::
263     if db.security.hasClassPermission('issue', 'Edit', userid):
264         # all ok
266     if db.security.hasNodePermission('issue', nodeid, assignedto=userid):
267         # all ok
269 Code in the core will make use of these methods, as should code in auditors in
270 custom templates. The htmltemplate will implement a new tag, ``<require>``
271 which has the form::
273   <require permission="name,name,name" assignedto="$userid" status="open">
274    HTML to display if the user has the permission.
275   <else>
276    HTML to display if the user does not have the permission.
277   </require>
279 where:
281 - the permission attribute gives a comma-separated list of permission names.
282   These are checked in turn using ``hasClassPermission`` and requires one to
283   be OK.
284 - the other attributes are lookups on the node using ``hasNodePermission``. If
285   the attribute value is "$userid" then the current user's userid is tested.
287 Any of these tests must pass or the ``<require>`` check will fail. The section
288 of html within the side of the ``<else>`` that fails is remove from processing.
290 Implementation as shipped
291 -------------------------
293 A set of Permissions are built in to the security module by default:
295 - Edit (everything)
296 - Access (everything)
297 - Assign (everything)
299 The default interfaces define:
301 - Web Registration
302 - Email Registration
304 These are hooked into the default Roles:
306 - Admin (Edit everything, Access everything, Assign everything)
307 - User ()
308 - Anonymous (Web Registration, Email Registration)
310 And finally, the "admin" user gets the "Admin" Role, and the "anonymous" user
311 gets the "Anonymous" assigned when the database is initialised on installation.
312 The two default schemas then define:
314 - Edit issue, Access issue (both)
315 - Edit support, Access support (extended only)
317 and assign those Permissions to the "User" Role.
320 Authentication of Users
321 -----------------------
323 Users must be authenticated correctly for the above controls to work. This is
324 not done in the current mail gateway at all. Use of digital signing of
325 messages could alleviate this problem.
327 The exact mechanism of registering the digital signature should be flexible,
328 with perhaps a level of trust. Users who supply their signature through their
329 first message into the tracker should be at a lower level of trust to those
330 who supply their signature to an admin for submission to their user details.
333 Anonymous Users
334 ---------------
336 The "anonymous" user must always exist, and defines the access permissions for
337 anonymous users. The three ANONYMOUS_ configuration variables are subsumed by
338 this new functionality.
341 Action
342 ======
344 The CGI interface must be changed to:
346 - authenticate over a secure connection
347 - use unique tokens as a result of authentication, rather than pass the user's
348   real credentials (username/password) around for each request (this means
349   sessions and hence a session database)
350 - use the new logical control mechanisms
352   - implement the permission module
353   - implement a Role editing interface for users
354   - implement htmltemplate tests on permissions
355   - switch all code over from using config vars for permission checks to using
356     permissions
357   - include config vars for initial Roles for anonymous web, new web and new
358     email users
360 The mail gateway must be changed to:
362 - use digital signatures
363 - use the new logical control mechanisms
365   - switch all code over from using config vars for permission checks to using
366     permissions
368 The command-line tool must be changed to:
370 - use the new logical control mechanisms (only allowing write
371   access by admin users, and read-only by everyone else)
374 Use cases
375 =========
377 public
378   end users that can submit bugs, request new features, request support
379 developer
380   developers that can fix bugs, implement new features provide support
381 manager
382   approvers/managers that can approve new features and signoff bug fixes
383 admin
384   administrators that can add users and set user's roles
385 system
386   automated request handlers running various report/escalation scripts
387 privacy
388   issues that are only visible to some users