Code

just some formatting and a minor clarification.
[roundup.git] / doc / security.txt
1 ===================
2 Security Mechanisms
3 ===================
5 :Version: $Revision: 1.9 $
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.
46 Possible approaches
47 ===================
49 Security controls in Roundup could be approached in three ways:
51 1) at the hyperdb level, with read/write/modify permissions on classes, nodes
52    and node properties for all or specific transitions.
53 2) at the user interface level, with access permissions on CGI interface
54    methods, mailgw methods, roundup-admin methods, and so on.
55 3) at a logical permission level, checked as needed.
57 In all cases, the security built into roundup assumes restricted access to the
58 hyperdatabase itself, through Operating System controls such as user or group
59 permissions.
62 Hyperdb-level control
63 ---------------------
65 Control is implemented at the Class.get, Class.set and Class.create level. All
66 other methods must access nodes through these methods. Since all accesses go
67 through the database, we can implement deny by default.
69 Pros:
71    - easier to implement as it only affects one module
72    - smaller number of permissions to worry about
74 Cons:
76    - harder to determine the relationship between user interaction and hyperdb
77      permission.
78    - a lot of work to define
79    - must special-case to handle by-node permissions (editing user details,
80      having private messages)
83 User-interface control
84 ----------------------
86 The user interfaces would have an extra layer between that which
87 parses the request to determine action and the action method. This layer
88 controls access. Since it is possible to require methods be registered
89 with the security mechanisms to be accessed by the user, deny by default
90 is possible.
92 Pros:
94    - much more obvious at the user level what the controls are
96 Cons:
98    - much more work to implement
99    - most user interfaces have multiple uses which can't be covered by a
100      single permission
103 Logical control
104 ---------------
106 At each point that requires an action to be performed, the security mechanisms
107 are asked if the current user has permission. Since code must call the
108 check function to raise a denial, there is no possibility to have automatic
109 default of deny in this situation.
111 Pros:
113    - quite obvious what is going on
114    - is very similar to the current system
116 Cons:
118    - large number of possible permissions that may be defined, possibly
119      mirroring actual user interface controls.
120    - access to the hyperdb must be strictly controlled through program code
121      that implements the logical controls.
124 Applying controls to users
125 ==========================
127 Individual assignment of Permission to User is unwieldy. The concept of a
128 Role, which encompasses several Permissions and may be assigned to many Users,
129 is quite well developed in many projects. Roundup will take this path, and
130 allow the multiple assignment of Roles to Users, and multiple Permissions to
131 Roles. These definitions will be stored in the hyperdb. They don't need to be
132 pushed to the actual database though.
134 There will be two levels of Permission. The Class level permissions define
135 logical permissions associated with all nodes of a particular class (or all
136 classes). The Node level permissions define logical permissions associated
137 with specific nodes by way of their user-linked properties.
139 A permission module defines::
141     class InMemoryImmutableClass(hyperdb.Class):
142         ''' Don't allow changes to this class's nodes.
143         '''
144         def __init__(self, db, classname, **properties):
145             ''' Set up an in-memory store for the nodes of this class
146             '''
148         def create(self, **propvalues):
149             ''' Create a new node in the in-memory store
150             '''
152         def get(self, nodeid, propname, default=_marker, cache=1):
153             ''' Get the node from the in-memory store
154             '''
156         def set(self, *args):
157             raise ValueError, "%s are immutable"%self.__class__.__name__
159     class PermissionClass(InMemoryImmutableClass):
160         ''' Include the default attributes:
161             - name (String)
162             - classname (String)
163             - description (String)
165             The classname may be unset, indicating that this permission is not
166             locked to a particular class. That means there may be multiple
167             Permissions for the same name for different classes.
168         '''
170     class RoleClass(InMemoryImmutableClass):
171         ''' Include the default attributes:
172             - name (String, key)
173             - description (String)
174             - permissions (PermissionClass Multilink)
175         '''
177     def hasClassPermission(db, classname, permission, userid):
178         ''' Look through all the Roles, and hence Permissions, and see if
179             "permission" is there for the specified classname.
181         '''
183     def hasNodePermission(db, classname, nodeid, userid, properties):
184         ''' Check the named properties of the given node to see if the userid
185             appears in them. If it does, then the user is granted this
186             permission check.
188             'propspec' consists of a list of property names. The property
189             names must be the name of a property of classname, or a
190             KeyError is raised.  That property must be a Link or Multilink
191             property, or a TypeError is raised.
193             If the property is a Link, the userid must match the property
194             value. If the property is a Multilink, the userid must appear
195             in the Multilink list.
196         '''
198 The instance dbinit module then has in ``open()``::
200     perm = permission.PermissionClass(db, "permission")
201     role = permission.RoleClass(db, "role")
203     # create some Permissions
204     wa = perm.create(name="Web Access",
205                     description="User may use the web interface")
206     wr = perm.create(name="Web Registration",
207                     description="User may register through the web")
209     ma = perm.create(name="Mail Access",
210                     description="User may use the email interface")
211     mr = perm.create(name="Mail Registration",
212                     description="User may register through email")
214     ee = perm.create(name="Edit",
215                     description="User may edit everthing")
216     ei = perm.create(name="Edit", classname="issue",
217                     description="User is allowed to edit issues")
219     ae = perm.create(name="Assign",
220                     description="User may be assigned to anything")
221     ai = perm.create(name="Assign", classname="issue",
222                     description="User may be assigned to issues")
224     # create some Roles that use the Permissions
225     role.create(name="User", description="A regular user, no privs",
226                 permissions=[wa, wr, ma, mr, ei, ai])
227     role.create(name="Admin", description="An admin user, full privs",
228                 permissions=[ee, ae])
229     role.create(name="No Rego", description="A user who can't register",
230                 permissions=[wa, ma])
232 in ``init()``::
234     r = db.getclass('role').lookup('Admin')
235     user.create(username="admin", password=Password(adminpw),
236                 address=instance_config.ADMIN_EMAIL, roles=[r])
238     # choose your anonymous user access permission here
239     #r = db.getclass('role').lookup('No Rego')
240     r = db.getclass('role').lookup('User')
241     user.create(username="anonymous", roles=[r])
243 Then in the code that matters, calls to ``hasPermission`` are made to
244 determine if the user has permission to perform some action::
246     if security.hasClassPermission('issue', 'Edit', self.user):
247         # all ok
249     if security.hasNodePermission('issue', nodeid, self.user, ['assignedto']):
250         # all ok
252 The htmltemplate will implement a new tag, <permission> which has the form::
254   <permission require=name,name,name node=assignedto>
255    HTML to display if the user has the permission.
256   <else>
257    HTML to display if the user does not have the permission.
258   </permission>
260 where the require attribute gives a comma-separated list of permission names
261 which are required, and the node attribute gives a comma-separated list of
262 node properties whose value must match the current user's id. Either of these
263 tests must pass or the permission check will fail. The section of html within
264 the side of the ``<else>`` that fails is remove from processing.
267 Authentication of Users
268 -----------------------
270 Users must be authenticated correctly for the above controls to work. This is
271 not done in the current mail gateway at all. Use of digital signing of
272 messages could alleviate this problem.
274 The exact mechanism of registering the digital signature should be flexible,
275 with perhaps a level of trust. Users who supply their signature through their
276 first message into the tracker should be at a lower level of trust to those
277 who supply their signature to an admin for submission to their user details.
280 Action
281 ======
283 The CGI interface must be changed to:
285 - authenticate over a secure connection
286 - use unique tokens as a result of authentication, rather than pass the user's
287   real credentials (username/password) around for each request (this means
288   sessions and hence a session database)
289 - use the new logical control mechanisms
291   - implement the permission module
292   - implement a Role editing interface for users
293   - implement htmltemplate tests on permissions
294   - switch all code over from using config vars for permission checks to using
295     permissions
296   - include config vars for initial Roles for anonymous web, new web and new
297     email users
299 The mail gateway must be changed to:
301 - use digital signatures
302 - use the new logical control mechanisms
304   - switch all code over from using config vars for permission checks to using
305     permissions
307 The command-line tool must be changed to:
309 - use the new logical control mechanisms (only allowing write
310   access by admin users, and read-only by everyone else)
313 Use cases
314 =========
316 public
317   end users that can submit bugs, request new features, request support
318 developer
319   developers that can fix bugs, implement new features provide support
320 manager
321   approvers/managers that can approve new features and signoff bug fixes
322 admin
323   administrators that can add users and set user's roles
324 system
325   automated request handlers running various report/escalation scripts
326 privacy
327   issues that are only visible to some users