Skip to content

Commit dced38e

Browse files
author
Richard Jones
committed
far more merging from HEAD than is good
1 parent 9643224 commit dced38e

18 files changed

+303
-160
lines changed

CHANGES.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
This file contains the changes to the Roundup system over time. The entries
22
are given with the most recent entry first.
33

4-
2005-01-?? 0.8.0
4+
2005-01-?? 0.8.0b3
55
Fixed:
66
- fix roundup-server log and PID file paths to be absolute
77
- fix initialisation of roundup-server in daemon mode so initialisation
88
errors are visible
99
- fix: 'Logout' link was enabled on issue index page only
10+
- have Permissions only test the check function if itemid is suppled
11+
- modify index templates to check for row-level Permission
12+
- more documentation of security mechanisms
13+
- better unit tests for security mechanisms
1014

1115

1216
2005-01-13 0.8.0b2

doc/customizing.txt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Customising Roundup
33
===================
44

5-
:Version: $Revision: 1.161.2.4 $
5+
:Version: $Revision: 1.161.2.5 $
66

77
.. This document borrows from the ZopeBook section on ZPT. The original is at:
88
http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -51,6 +51,7 @@ extensions/ Additional web actions and templating utilities.
5151
html/ Web interface templates, images and style sheets
5252
=================== ========================================================
5353

54+
5455
Tracker Configuration
5556
=====================
5657

@@ -855,6 +856,29 @@ Put together, these settings appear in the tracker's ``schema.py`` file::
855856
# db.security.addPermissionToRole('Anonymous', p)
856857

857858

859+
Automatic Permission Checks
860+
---------------------------
861+
862+
Permissions are automatically checked when information is rendered
863+
through the web. This includes:
864+
865+
1. View checks for properties when being rendered via the ``plain()`` or
866+
similar methods. If the check fails, the text "[hidden]" will be
867+
displayed.
868+
2. Edit checks for properties when the edit field is being rendered via
869+
the ``field()`` or similar methods. If the check fails, the property
870+
will be rendered via the ``plain()`` method (see point 1. for subsequent
871+
checking performed)
872+
3. View checks are performed in index pages for each item being displayed
873+
such that if the user does not have permission, the row is not rendered.
874+
4. View checks are performed at the top of item pages for the Item being
875+
displayed. If the user does not have permission, the text "You are not
876+
allowed to view this page." will be displayed.
877+
5. View checks are performed at the top of index pages for the Class being
878+
displayed. If the user does not have permission, the text "You are not
879+
allowed to view this page." will be displayed.
880+
881+
858882
New User Roles
859883
--------------
860884

doc/design.txt

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,11 +1384,12 @@ this path, and allow the multiple assignment of Roles to Users, and
13841384
multiple Permissions to Roles. These definitions are not persistent -
13851385
they're defined when the application initialises.
13861386

1387-
There will be two levels of Permission. The Class level permissions
1387+
There will be three levels of Permission. The Class level permissions
13881388
define logical permissions associated with all items of a particular
13891389
class (or all classes). The Item level permissions define logical
13901390
permissions associated with specific items by way of their user-linked
1391-
properties.
1391+
properties. The Property level permissions define logical permissions
1392+
associated with a specific property of an item.
13921393

13931394

13941395
Access Control Interface Specification
@@ -1430,36 +1431,41 @@ The security module defines::
14301431
the base roles (for admin user).
14311432
'''
14321433

1433-
def getPermission(self, permission, classname=None):
1434-
''' Find the Permission matching the name and for the class,
1435-
if the classname is specified.
1434+
def getPermission(self, permission, classname=None, properties=None,
1435+
check=None):
1436+
''' Find the Permission exactly matching the name, class,
1437+
properties list and check function.
14361438

14371439
Raise ValueError if there is no exact match.
14381440
'''
14391441

1440-
def hasPermission(self, permission, userid, classname=None):
1442+
def hasPermission(self, permission, userid, classname=None,
1443+
property=None, itemid=None):
14411444
''' Look through all the Roles, and hence Permissions, and
1442-
see if "permission" is there for the specified
1443-
classname.
1444-
'''
1445+
see if "permission" exists given the constraints of
1446+
classname, property and itemid.
1447+
1448+
If classname is specified (and only classname) then the
1449+
search will match if there is *any* Permission for that
1450+
classname, even if the Permission has additional
1451+
constraints.
14451452

1446-
def hasItemPermission(self, classname, itemid, **propspec):
1447-
''' Check the named properties of the given item to see if
1448-
the userid appears in them. If it does, then the user is
1449-
granted this permission check.
1453+
If property is specified, the Permission matched must have
1454+
either no properties listed or the property must appear in
1455+
the list.
14501456

1451-
'propspec' consists of a set of properties and values
1452-
that must be present on the given item for access to be
1453-
granted.
1457+
If itemid is specified, the Permission matched must have
1458+
either no check function defined or the check function,
1459+
when invoked, must return a True value.
14541460

1455-
If a property is a Link, the value must match the
1456-
property value. If a property is a Multilink, the value
1457-
must appear in the Multilink list.
1461+
Note that this functionality is actually implemented by the
1462+
Permission.test() method.
14581463
'''
14591464

14601465
def addPermission(self, **propspec):
14611466
''' Create a new Permission with the properties defined in
1462-
'propspec'
1467+
'propspec'. See the Permission class for the possible
1468+
keyword args.
14631469
'''
14641470

14651471
def addRole(self, **propspec):

doc/whatsnew-0.8.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ only specific properties on items.
6666
Permissions may also have code attached which is executed to check whether
6767
the Permission is valid for the current user and item.
6868

69+
Permissions are now automatically checked when information is rendered
70+
through the web. This includes:
71+
72+
1. View checks for properties when being rendered via the ``plain()`` or
73+
similar methods. If the check fails, the text "[hidden]" will be
74+
displayed.
75+
2. Edit checks for properties when the edit field is being rendered via
76+
the ``field()`` or similar methods. If the check fails, the property
77+
will be rendered via the ``plain()`` method (see point 1. for additional
78+
checking performed)
79+
3. View checks are performed in index pages for each item being displayed
80+
such that if the user does not have permission, the row is not rendered.
81+
6982

7083
Extending Roundup
7184
=================
@@ -134,7 +147,6 @@ Templating
134147

135148
The listing popup may be used in query forms.
136149

137-
138150
Standard templates
139151
We hide "(list)" popup links when issue is only viewable
140152

roundup/cgi/templating.py

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,8 @@ def submit(self, label=''"Submit New Entry"):
656656
self.input(type="submit", name="submit", value=self._(label))
657657

658658
def history(self):
659-
self.view_check()
659+
if not self.is_view_ok():
660+
return _('[hidden]')
660661
return self._('New node - no history')
661662

662663
def renderWith(self, name, **kwargs):
@@ -774,7 +775,8 @@ def journal(self, direction='descending'):
774775
return []
775776

776777
def history(self, direction='descending', dre=re.compile('^\d+$')):
777-
self.view_check()
778+
if not self.is_view_ok():
779+
return _('[hidden]')
778780

779781
# pre-load the history with the current state
780782
current = {}
@@ -1143,7 +1145,8 @@ def plain(self, escape=0, hyperlink=0):
11431145
- "hyperlink" turns on/off in-text hyperlinking of URLs, email
11441146
addresses and designators
11451147
'''
1146-
self.view_check()
1148+
if not self.is_view_ok():
1149+
return _('[hidden]')
11471150

11481151
if self._value is None:
11491152
return ''
@@ -1163,7 +1166,8 @@ def stext(self, escape=0):
11631166
11641167
This requires the StructureText module to be installed separately.
11651168
'''
1166-
self.view_check()
1169+
if not self.is_view_ok():
1170+
return _('[hidden]')
11671171

11681172
s = self.plain(escape=escape)
11691173
if not StructuredText:
@@ -1206,7 +1210,8 @@ def multiline(self, escape=0, rows=5, cols=40):
12061210
def email(self, escape=1):
12071211
''' Render the value of the property as an obscured email address
12081212
'''
1209-
self.view_check()
1213+
if not self.is_view_ok():
1214+
return _('[hidden]')
12101215

12111216
if self._value is None:
12121217
value = ''
@@ -1227,7 +1232,8 @@ class PasswordHTMLProperty(HTMLProperty):
12271232
def plain(self):
12281233
''' Render a "plain" representation of the property
12291234
'''
1230-
self.view_check()
1235+
if not self.is_view_ok():
1236+
return _('[hidden]')
12311237

12321238
if self._value is None:
12331239
return ''
@@ -1260,7 +1266,8 @@ class NumberHTMLProperty(HTMLProperty):
12601266
def plain(self):
12611267
''' Render a "plain" representation of the property
12621268
'''
1263-
self.view_check()
1269+
if not self.is_view_ok():
1270+
return _('[hidden]')
12641271

12651272
if self._value is None:
12661273
return ''
@@ -1298,7 +1305,8 @@ class BooleanHTMLProperty(HTMLProperty):
12981305
def plain(self):
12991306
''' Render a "plain" representation of the property
13001307
'''
1301-
self.view_check()
1308+
if not self.is_view_ok():
1309+
return _('[hidden]')
13021310

13031311
if self._value is None:
13041312
return ''
@@ -1342,7 +1350,8 @@ def __init__(self, client, classname, nodeid, prop, name, value,
13421350
def plain(self):
13431351
''' Render a "plain" representation of the property
13441352
'''
1345-
self.view_check()
1353+
if not self.is_view_ok():
1354+
return _('[hidden]')
13461355

13471356
if self._value is None:
13481357
return ''
@@ -1358,7 +1367,8 @@ def now(self, str_interval=None):
13581367
This is useful for defaulting a new value. Returns a
13591368
DateHTMLProperty.
13601369
'''
1361-
self.view_check()
1370+
if not self.is_view_ok():
1371+
return _('[hidden]')
13621372

13631373
ret = date.Date('.', translator=self._client)
13641374

@@ -1427,7 +1437,8 @@ def reldate(self, pretty=1):
14271437
14281438
If the "pretty" flag is true, then make the display pretty.
14291439
'''
1430-
self.view_check()
1440+
if not self.is_view_ok():
1441+
return _('[hidden]')
14311442

14321443
if not self._value:
14331444
return ''
@@ -1446,7 +1457,8 @@ def pretty(self, format=_marker):
14461457
string, then it'll be stripped from the output. This is handy
14471458
for the situatin when a date only specifies a month and a year.
14481459
'''
1449-
self.view_check()
1460+
if not self.is_view_ok():
1461+
return _('[hidden]')
14501462

14511463
if not self._value:
14521464
return ''
@@ -1458,7 +1470,8 @@ def pretty(self, format=_marker):
14581470
def local(self, offset):
14591471
''' Return the date/time as a local (timezone offset) date/time.
14601472
'''
1461-
self.view_check()
1473+
if not self.is_view_ok():
1474+
return _('[hidden]')
14621475

14631476
return DateHTMLProperty(self._client, self._classname, self._nodeid,
14641477
self._prop, self._formname, self._value, offset=offset)
@@ -1474,7 +1487,8 @@ def __init__(self, client, classname, nodeid, prop, name, value,
14741487
def plain(self):
14751488
''' Render a "plain" representation of the property
14761489
'''
1477-
self.view_check()
1490+
if not self.is_view_ok():
1491+
return _('[hidden]')
14781492

14791493
if self._value is None:
14801494
return ''
@@ -1483,7 +1497,8 @@ def plain(self):
14831497
def pretty(self):
14841498
''' Render the interval in a pretty format (eg. "yesterday")
14851499
'''
1486-
self.view_check()
1500+
if not self.is_view_ok():
1501+
return _('[hidden]')
14871502

14881503
return self._value.pretty()
14891504

@@ -1531,7 +1546,8 @@ def __getattr__(self, attr):
15311546
def plain(self, escape=0):
15321547
''' Render a "plain" representation of the property
15331548
'''
1534-
self.view_check()
1549+
if not self.is_view_ok():
1550+
return _('[hidden]')
15351551

15361552
if self._value is None:
15371553
return ''
@@ -1683,7 +1699,8 @@ def reverse(self):
16831699
def plain(self, escape=0):
16841700
''' Render a "plain" representation of the property
16851701
'''
1686-
self.view_check()
1702+
if not self.is_view_ok():
1703+
return _('[hidden]')
16871704

16881705
linkcl = self._db.classes[self._prop.classname]
16891706
k = linkcl.labelprop(1)

0 commit comments

Comments
 (0)