Code

Latest thoughts.
[roundup.git] / doc / security.txt
1 ===================
2 Security Mechanisms
3 ===================
5 :Version: $Revision: 1.2 $
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 The web interface implements another level of user-interface security,
20 preventing non-admin users from accessing:
22  - other user's details pages
23  - listing the base classes (not issues or their user page)
24  - editing base classes
27 Issues
28 ======
30 1. The current implementation is ad-hoc, and not complete for all `use cases`_.
31 2. Currently it is not possible to allow submission of issues through email
32    but restrict those users from accessing the web interface.
33 3. Only one user may perform admin functions.
34 4. There is no verification of users in the mail gateway by any means other
35    than the From address. Support for strong signatures should be added.
38 Possible approaches
39 ===================
41 Security controls in Roundup could be approached in three ways:
43 1) at the hyperdb level, with read/write/modify permissions on classes, nodes
44    and node properties for all or specific transitions.
45 2) at the user interface level, with access permissions on CGI interface
46    methods, mailgw methods, roundup-admin methods, and so on.
47 3) at a logical permission level, checked as needed.
49 In all cases, the security built into roundup assumes restricted access to the
50 hyperdatabase itself, through Operating System controls such as user or group
51 permissions.
53 Hyperdb-level control
54 ---------------------
56 Control is implemented at the Class.get, Class.set and Class.create level. All
57 other methods must access nodes through these methods. Since all accesses go
58 through the database, we can implement deny by default.
60 Pros:
62    - easier to implement as it only affects one module
63    - smaller number of permissions to worry about
65 Cons:
67    - harder to determine the relationship between user interaction and hyperdb
68      permission.
69    - a lot of work to define
70    - must special-case to handle by-node permissions (editing user details,
71      having private messages)
74 User-interface control
75 ----------------------
77 The user interfaces would have an extra layer between that which
78 parses the request to determine action and the action method. This layer
79 controls access. Since it is possible to require methods be registered
80 with the security mechanisms to be accessed by the user, deny by default
81 is possible.
83 Pros:
85    - much more obvious at the user level what the controls are
87 Cons:
89    - much more work to implement
90    - most user interfaces have multiple uses which can't be covered by a
91      single permission
94 Logical control
95 ---------------
97 At each point that requires an action to be performed, the security mechanisms
98 are asked if the current user has permission. Since code must call the
99 check function to raise a denial, there is no possibility to have automatic
100 default of deny in this situation.
102 In practice, this is implemented as:
104 1. there's a mapping of user -> role          (in hyperdb)
105 2. there's a mapping of role -> permission    (in code)
106 3. there's a function that's available to all roundup code that can ask
107    whether a particular user has a particular permission.
109 Pros:
111    - quite obvious what is going on
112    - is the current system
114 Cons:
116    - large number of possible permissions that may be defined, possibly
117      mirroring actual user interface controls.
120 Applying controls to users
121 ==========================
123 Individual assignment of Permission to User is unwieldy. The concept of a
124 Role, which encompasses several Permissions and may be assigned to many Users,
125 is quite well developed in many projects. Roundup will take this path, and
126 allow the multiple assignment of Roles to Users, and multiple Permissions to
127 Roles. These definitions will be stored in the hyperdb.
130 A permission module defines::
132     class InMemoryImmutableClass(hyperdb.Class):
133         ''' Don't allow changes to this class's nodes.
134         '''
135         def __init__(self, db, classname, **properties):
136             ''' Set up an in-memory store for the nodes of this class
137             '''
139         def create(self, **propvalues):
140             ''' Create a new node in the in-memory store
141             '''
143         def get(self, nodeid, propname, default=_marker, cache=1):
144             ''' Get the node from the in-memory store
145             '''
147         def set(self, *args):
148             raise ValueError, "%s are immutable"%self.__class__.__name__
150     class PermissionClass(InMemoryImmutableClass):
151         ''' Include the default attributes:
152             - name (String, key)
153             - description (String)
154         '''
156     class RoleClass(InMemoryImmutableClass):
157         ''' Include the default attributes:
158             - name (String, key)
159             - description (String)
160             - permissions (PermissionClass Multilink)
161         '''
163     def hasPermission(db, userid, permission):
164         ''' Look through all the Roles, and hence Permissions, and see if
165             "permission" is there
166         '''
169 The instance dbinit module then has::
171   in open():
173     perm = permission.PermissionClass(db, "permission")
174     role = permission.RoleClass(db, "role")
176     wa = perm.create(name="Web Access",
177                     description="User may log in through the web")
178     wr = perm.create(name="Web Registration",
179                     description="User may register through the web")
180     ma = perm.create(name="Mail Access",
181                     description="User may log in through email")
182     mr = perm.create(name="Mail Registration",
183                     description="User may register through email")
184     aa = perm.create(name="Access Everything",
185                     description="User may access everthing")
186     role.create(name="User", description="A regular user, no privs",
187                 permissions=[wa, wr, ma, mr])
188     role.create(name="Admin", description="An admin user, full privs",
189                 permissions=[aa])
190     ro = role.create(name="No Rego", description="A user who can't register",
191                      permissions=[wa, ma])
193   in init():
195     r = db.getclass('role').find('Admin')
196     user.create(username="admin", password=Password(adminpw),
197                 address=instance_config.ADMIN_EMAIL, roles=[r])
199     # choose your anonymous user access permission here
200     #r = db.getclass('role').find('No Rego')
201     r = db.getclass('role').find('User')
202     user.create(username="anonymous", roles=[r])
204     
206 Use cases
207 =========
209 public
210   end users that can submit bugs, request new features, request support
211 developer
212   developers that can fix bugs, implement new features provide support
213 manager
214   approvers/managers that can approve new features and signoff bug fixes
215 admin
216   administrators that can add users and set user's roles
217 system
218   automated request handlers running various report/escalation scripts
219 privacy
220   issues that are only visible to some users