Skip to content

Commit ffc9498

Browse files
author
Richard Jones
committed
Bugger it. Here's the current shape of the new security implementation.
Still to do: . call the security funcs from cgi and mailgw . change shipped templates to include correct initialisation and remove the old config vars ... that seems like a lot. The bulk of the work has been done though. Honest :)
1 parent c37a896 commit ffc9498

File tree

15 files changed

+1016
-222
lines changed

15 files changed

+1016
-222
lines changed

COPYING.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
3+
Copyright (c) 2002 eKit.com Inc (http://www.ekit.com/)
4+
5+
This module is free software, and you may redistribute it and/or modify
6+
under the same terms as Python, so long as this copyright message and
7+
disclaimer are retained in their original form.
8+
9+
IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
10+
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
11+
OUT OF THE USE OF THIS CODE, EVEN IF BIZAR SOFTWARE PTY LTD HAS BEEN
12+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13+
14+
BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
15+
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
16+
FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
17+
BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
18+
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
19+
20+
21+
The stylesheet included with this package has been copied from the Zope
22+
management interface and presumably belongs to Digital Creations.
23+

TODO.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pending hyperdb: range searching of values (dates in particular)
1111
comparison functions: lt, le, eq, ge, gt. eq and
1212
[value, value, ...] implies "in"
1313
pending hyperdb: make creator, creation and activity available pre-commit
14+
pending hyperdb: migrate "id" property to be Number type
1415
pending instance: including much simpler upgrade path and the use of
1516
non-Python configuration files (ConfigParser)
1617
pending instance: cleanup to support config (feature request #498658)
@@ -28,7 +29,7 @@ pending mailgw: Allow multiple email addresses at one gw with different default
2829
roundup: "|roundup-mailgw /instances/dev"
2930
vmbugs: "|roundup-mailgw /instances/dev component=voicemail"
3031
pending project: switch to a Roundup instance for Roundup bug/feature tracking
31-
active security: finish doc/security.txt (RJ)
32+
active security: finish doc/security.txt
3233
pending security: implement and use the new logical control mechanisms
3334
pending security: at least an LDAP user database implementation
3435
pending security: authenticate over a secure connection
@@ -42,7 +43,6 @@ pending web: Quick help links next to the property labels giving a
4243
description of the property. Combine with help for the actual
4344
form element too, eg. how to use the nosy list edit box.
4445
pending web: feature request #507842
45-
active web: saving of named queries (GM)
4646

4747
ongoing any bugs
4848

@@ -53,4 +53,5 @@ done hyperdb: fix the journal bloat (RJ)
5353
done hyperdb: add Boolean and Number types (GM)
5454
done mailgw: better help message (feature request #558562) (RJ)
5555
done security: switch to sessions for web authentication (RJ)
56+
done web: saving of named queries (GM)
5657

doc/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ COMPILED := $(SOURCE:.txt=.html)
1010
all: ${COMPILED}
1111

1212
%.html: %.txt
13-
${PYTHON} ${STXTOHTML} -d -v $< $@
13+
${PYTHON} ${STXTOHTML} -d $< $@
1414

doc/default.css

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
22
:Author: David Goodger
33
4-
:date: $Date: 2002-06-24 00:57:23 $
5-
:version: $Revision: 1.5 $
4+
:date: $Date: 2002-07-25 07:14:05 $
5+
:version: $Revision: 1.6 $
66
:copyright: This stylesheet has been placed in the public domain.
77
88
Default cascading style sheet for the HTML output of Docutils.
@@ -160,3 +160,11 @@ table.docinfo {
160160
table.footnote {
161161
border-left: solid thin black ;
162162
padding-left: 0.5ex }
163+
164+
@media print {
165+
h1 {page-break-before: always; }
166+
h1, h2, h3, h4, h5, h6 { page-break-after: avoid; page-break-inside: avoid; }
167+
blockquote, pre { page-break-inside: avoid; }
168+
ul, ol, dl { page-break-before: avoid; }
169+
}
170+

doc/security.txt

Lines changed: 78 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Security Mechanisms
33
===================
44

5-
:Version: $Revision: 1.11 $
5+
:Version: $Revision: 1.12 $
66

77
Current situation
88
=================
@@ -41,6 +41,9 @@ Issues
4141
than the From address. Support for strong identification through digital
4242
signatures should be added.
4343
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).
4447

4548

4649
Possible approaches
@@ -138,8 +141,8 @@ with specific nodes by way of their user-linked properties.
138141

139142
A security module defines::
140143

141-
class InMemoryImmutableClass(hyperdb.Class):
142-
''' Don't allow changes to this class's nodes.
144+
class InMemoryClass(hyperdb.Class):
145+
''' Just be an in-memory class
143146
'''
144147
def __init__(self, db, classname, **properties):
145148
''' Set up an in-memory store for the nodes of this class
@@ -154,20 +157,21 @@ A security module defines::
154157
'''
155158

156159
def set(self, *args):
157-
raise ValueError, "%s are immutable"%self.__class__.__name__
160+
''' Set values on the node
161+
'''
158162

159-
class PermissionClass(InMemoryImmutableClass):
163+
class PermissionClass(InMemoryClass):
160164
''' Include the default attributes:
161165
- name (String)
162-
- classname (String)
166+
- klass (String)
163167
- description (String)
164168

165-
The classname may be unset, indicating that this permission is not
169+
The klass may be unset, indicating that this permission is not
166170
locked to a particular class. That means there may be multiple
167171
Permissions for the same name for different classes.
168172
'''
169173

170-
class RoleClass(InMemoryImmutableClass):
174+
class RoleClass(InMemoryClass):
171175
''' Include the default attributes:
172176
- name (String, key)
173177
- description (String)
@@ -179,51 +183,23 @@ A security module defines::
179183
''' Initialise the permission and role classes, and add in the
180184
base roles (for admin user).
181185
'''
182-
# use a weak ref to avoid circularity
183-
self.db = weakref.proxy(db)
184-
185-
# create the permission class instance (we only need one))
186-
self.permission = PermissionClass(db, "permission")
187-
188-
# create the role class instance (we only need one)
189-
self.role = RoleClass(db, "role")
190-
191-
# the default Roles
192-
self.addRole(name="User", description="A regular user, no privs")
193-
self.addRole(name="Admin", description="An admin user, full privs")
194-
self.addRole(name="No Rego",
195-
description="A user who can't register")
196-
197-
ee = self.addPermission(name="Edit",
198-
description="User may edit everthing")
199-
self.addPermissionToRole('Admin', ee)
200-
ae = self.addPermission(name="Assign",
201-
description="User may be assigned to anything")
202-
self.addPermissionToRole('Admin', ae)
203-
204-
# initialise the permissions and roles needed for the UIs
205-
from roundup import cgi_client, mailgw
206-
cgi_client.initialiseSecurity(self)
207-
mailgw.initialiseSecurity(self)
208186

209187
def hasClassPermission(self, db, classname, permission, userid):
210188
''' Look through all the Roles, and hence Permissions, and see if
211189
"permission" is there for the specified classname.
212190

213191
'''
214192

215-
def hasNodePermission(self, db, classname, nodeid, userid, properties):
193+
def hasNodePermission(self, db, classname, nodeid, **propspec):
216194
''' Check the named properties of the given node to see if the
217195
userid appears in them. If it does, then the user is granted
218196
this permission check.
219197

220-
'propspec' consists of a list of property names. The property
221-
names must be the name of a property of classname, or a
222-
KeyError is raised. That property must be a Link or Multilink
223-
property, or a TypeError is raised.
198+
'propspec' consists of a set of properties and values that
199+
must be present on the given node for access to be granted.
224200

225-
If the property is a Link, the userid must match the property
226-
value. If the property is a Multilink, the userid must appear
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
227203
in the Multilink list.
228204
'''
229205

@@ -251,13 +227,9 @@ permissions like so (this example is ``cgi_client.py``)::
251227
This function is directly invoked by security.Security.__init__()
252228
as a part of the Security object instantiation.
253229
'''
254-
newid = security.addPermission(name="Web Access",
255-
description="User may use the web interface")
256-
security.addToRole('User', newid)
257-
security.addToRole('No Rego', newid)
258230
newid = security.addPermission(name="Web Registration",
259-
description="User may register through the web")
260-
security.addToRole('User', newid)
231+
description="Anonymous users may register through the web")
232+
security.addToRole('Anonymous', newid)
261233

262234
The instance dbinit module then has in ``open()``::
263235

@@ -266,10 +238,10 @@ The instance dbinit module then has in ``open()``::
266238
db = Database(instance_config, name)
267239

268240
# add some extra permissions and associate them with roles
269-
ei = db.security.addPermission(name="Edit", classname="issue",
241+
ei = db.security.addPermission(name="Edit", klass="issue",
270242
description="User is allowed to edit issues")
271243
db.security.addPermissionToRole('User', ei)
272-
ai = db.security.addPermission(name="Assign", classname="issue",
244+
ai = db.security.addPermission(name="Assign", klass="issue",
273245
description="User may be assigned to issues")
274246
db.security.addPermissionToRole('User', ei)
275247

@@ -284,29 +256,65 @@ In the dbinit ``init()``::
284256
r = db.getclass('role').lookup('User')
285257
user.create(username="anonymous", roles=[r])
286258

287-
Then in the code that matters, calls to ``hasPermission`` are made to
288-
determine if the user has permission to perform some action::
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::
289262

290-
if db.security.hasClassPermission('issue', 'Edit', self.user):
263+
if db.security.hasClassPermission('issue', 'Edit', userid):
291264
# all ok
292265

293-
if db.security.hasNodePermission('issue', nodeid, self.user,
294-
['assignedto']):
266+
if db.security.hasNodePermission('issue', nodeid, assignedto=userid):
295267
# all ok
296268

297-
The htmltemplate will implement a new tag, <permission> which has the form::
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::
298272

299-
<permission require=name,name,name node=assignedto>
273+
<require permission="name,name,name" assignedto="$userid" status="open">
300274
HTML to display if the user has the permission.
301275
<else>
302276
HTML to display if the user does not have the permission.
303-
</permission>
277+
</require>
278+
279+
where:
280+
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.
286+
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.
289+
290+
Implementation as shipped
291+
-------------------------
292+
293+
A set of Permissions are built in to the security module by default:
304294

305-
where the require attribute gives a comma-separated list of permission names
306-
which are required, and the node attribute gives a comma-separated list of
307-
node properties whose value must match the current user's id. Either of these
308-
tests must pass or the permission check will fail. The section of html within
309-
the side of the ``<else>`` that fails is remove from processing.
295+
- Edit (everything)
296+
- Access (everything)
297+
- Assign (everything)
298+
299+
The default interfaces define:
300+
301+
- Web Registration
302+
- Email Registration
303+
304+
These are hooked into the default Roles:
305+
306+
- Admin (Edit everything, Access everything, Assign everything)
307+
- User ()
308+
- Anonymous (Web Registration, Email Registration)
309+
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:
313+
314+
- Edit issue, Access issue (both)
315+
- Edit support, Access support (extended only)
316+
317+
and assign those Permissions to the "User" Role.
310318

311319

312320
Authentication of Users
@@ -322,6 +330,14 @@ first message into the tracker should be at a lower level of trust to those
322330
who supply their signature to an admin for submission to their user details.
323331

324332

333+
Anonymous Users
334+
---------------
335+
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.
339+
340+
325341
Action
326342
======
327343

roundup/admin.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1717
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1818
#
19-
# $Id: admin.py,v 1.18 2002-07-18 11:17:30 gmcm Exp $
19+
# $Id: admin.py,v 1.19 2002-07-25 07:14:05 richard Exp $
2020

2121
import sys, os, getpass, getopt, re, UserDict, shlex, shutil
2222
try:
@@ -371,8 +371,8 @@ def do_get(self, args):
371371
for designator in designators:
372372
# decode the node designator
373373
try:
374-
classname, nodeid = roundupdb.splitDesignator(designator)
375-
except roundupdb.DesignatorError, message:
374+
classname, nodeid = hyperdb.splitDesignator(designator)
375+
except hyperdb.DesignatorError, message:
376376
raise UsageError, message
377377

378378
# get the class
@@ -411,8 +411,8 @@ def do_set(self, args):
411411
for designator in designators:
412412
# decode the node designator
413413
try:
414-
classname, nodeid = roundupdb.splitDesignator(designator)
415-
except roundupdb.DesignatorError, message:
414+
classname, nodeid = hyperdb.splitDesignator(designator)
415+
except hyperdb.DesignatorError, message:
416416
raise UsageError, message
417417

418418
# get the class
@@ -537,8 +537,8 @@ def do_display(self, args):
537537

538538
# decode the node designator
539539
try:
540-
classname, nodeid = roundupdb.splitDesignator(args[0])
541-
except roundupdb.DesignatorError, message:
540+
classname, nodeid = hyperdb.splitDesignator(args[0])
541+
except hyperdb.DesignatorError, message:
542542
raise UsageError, message
543543

544544
# get the class
@@ -747,8 +747,8 @@ def do_history(self, args):
747747
if len(args) < 1:
748748
raise UsageError, _('Not enough arguments supplied')
749749
try:
750-
classname, nodeid = roundupdb.splitDesignator(args[0])
751-
except roundupdb.DesignatorError, message:
750+
classname, nodeid = hyperdb.splitDesignator(args[0])
751+
except hyperdb.DesignatorError, message:
752752
raise UsageError, message
753753

754754
try:
@@ -797,8 +797,8 @@ def do_retire(self, args):
797797
designators = args[0].split(',')
798798
for designator in designators:
799799
try:
800-
classname, nodeid = roundupdb.splitDesignator(designator)
801-
except roundupdb.DesignatorError, message:
800+
classname, nodeid = hyperdb.splitDesignator(designator)
801+
except hyperdb.DesignatorError, message:
802802
raise UsageError, message
803803
try:
804804
self.db.getclass(classname).retire(nodeid)
@@ -1131,6 +1131,11 @@ def main(self):
11311131

11321132
#
11331133
# $Log: not supported by cvs2svn $
1134+
# Revision 1.18 2002/07/18 11:17:30 gmcm
1135+
# Add Number and Boolean types to hyperdb.
1136+
# Add conversion cases to web, mail & admin interfaces.
1137+
# Add storage/serialization cases to back_anydbm & back_metakit.
1138+
#
11341139
# Revision 1.17 2002/07/14 06:05:50 richard
11351140
# . fixed the date module so that Date(". - 2d") works
11361141
#

0 commit comments

Comments
 (0)