Skip to content

Commit 36e2bcf

Browse files
author
Richard Jones
committed
Stuff from the train ride this morning:
- Extend the property concept in Permissions to allow a list of properties - Fix the cgi templating code to check the correct permission when rendering edit fields - A swag of changes (just the start) fixing up the customisation doc for the new tracker layout and permissions setup
1 parent ced20d8 commit 36e2bcf

File tree

7 files changed

+179
-94
lines changed

7 files changed

+179
-94
lines changed

TODO.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
This file contains items that need doing before the next release:
22

33
Required:
4-
- implementing logging for roundup-server, including command-line switches
54
- Security review:
65
- write up security model used in classic tracker
76
- ensure classic template actually implements the model detailed

doc/admin_guide.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Administration Guide
33
====================
44

5-
:Version: $Revision: 1.14 $
5+
:Version: $Revision: 1.15 $
66

77
.. contents::
88

@@ -141,7 +141,7 @@ Roundup identifies users in a number of ways:
141141

142142
In both cases, Roundup's behaviour when dealing with unknown users is
143143
controlled by Permissions defined in the "SECURITY SETTINGS" section of the
144-
tracker's ``dbinit.py`` module:
144+
tracker's ``schema.py`` module:
145145

146146
Web Registration
147147
If granted to the Anonymous Role, then anonymous users will be able to

doc/customizing.txt

Lines changed: 120 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Customising Roundup
33
===================
44

5-
:Version: $Revision: 1.159 $
5+
:Version: $Revision: 1.160 $
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
@@ -290,13 +290,13 @@ Note: if you modify the schema, you'll most likely need to edit the
290290
your changes.
291291

292292
A tracker schema defines what data is stored in the tracker's database.
293-
Schemas are defined using Python code in the ``dbinit.py`` module of your
293+
Schemas are defined using Python code in the ``schema.py`` module of your
294294
tracker.
295295

296-
The ``dbinit.py`` module
296+
The ``schema.py`` module
297297
------------------------
298298

299-
The ``dbinit.py`` module contains two functions:
299+
The ``schema.py`` module contains two functions:
300300

301301
**open**
302302
This function defines what your tracker looks like on the inside, the
@@ -702,7 +702,7 @@ tracker is initialised. The actual method of doing so is completely
702702
different in each case though, so be careful to use the right one.
703703

704704
**Changing content before tracker initialisation**
705-
Edit the dbinit module in your tracker to alter the items created in
705+
Edit the schema module in your tracker to alter the items created in
706706
using the ``create()`` methods.
707707

708708
**Changing content after tracker initialisation**
@@ -723,11 +723,12 @@ Security / Access Controls
723723

724724
A set of Permissions is built into the security module by default:
725725

726+
- Create (everything)
726727
- Edit (everything)
727728
- View (everything)
728729

729-
Every Class you define in your tracker's schema also gets an Edit and View
730-
Permission of its own.
730+
Every Class you define in your tracker's schema also gets an Create, Edit
731+
and View Permission of its own.
731732

732733
The default interfaces define:
733734

@@ -739,47 +740,108 @@ The default interfaces define:
739740

740741
These are hooked into the default Roles:
741742

742-
- Admin (Edit everything, View everything, Web Roles)
743-
- User (Web Access, Email Access)
744-
- Anonymous (Web Registration, Email Registration)
743+
- Admin (Create, Edit, View and everything; Web Roles)
744+
- User (Web Access; Email Access)
745+
- Anonymous (Web Registration; Email Registration)
745746

746747
And finally, the "admin" user gets the "Admin" Role, and the "anonymous"
747-
user gets "Anonymous" assigned when the database is initialised on
748-
installation. The two default schemas then define:
748+
user gets "Anonymous" assigned when the tracker is installed.
749749

750-
- Edit issue, View issue (both)
751-
- Edit file, View file (both)
752-
- Edit msg, View msg (both)
753-
- Edit support, View support (extended only)
750+
For the "User" Role, the "classic" tracker defines:
754751

755-
and assign those Permissions to the "User" Role. Put together, these
756-
settings appear in the ``open()`` function of the tracker ``dbinit.py``
757-
(the following is taken from the "minimal" template's ``dbinit.py``)::
752+
- Create, Edit and View issue, file, msg, query, keyword
753+
- View priority, status
754+
- View user
755+
- Edit their own record
758756

757+
And the "Anonymous" Role is defined as:
758+
759+
- Create user (for registration)
760+
- View issue, file, msg, query, keyword, priority, status
761+
762+
Put together, these settings appear in the tracker's ``schema.py`` file::
763+
764+
#
765+
# TRACKER SECURITY SETTINGS
759766
#
760-
# SECURITY SETTINGS
767+
# See the configuration and customisation document for information
768+
# about security setup.
769+
770+
#
771+
# REGULAR USERS
761772
#
762-
# and give the regular users access to the web and email interface
773+
# Give the regular users access to the web and email interface
763774
p = db.security.getPermission('Web Access')
764775
db.security.addPermissionToRole('User', p)
765776
p = db.security.getPermission('Email Access')
766777
db.security.addPermissionToRole('User', p)
767778

779+
# Assign the access and edit Permissions for issue, file and message
780+
# to regular users now
781+
for cl in 'issue', 'file', 'msg', 'query', 'keyword':
782+
p = db.security.getPermission('View', cl)
783+
db.security.addPermissionToRole('User', p)
784+
p = db.security.getPermission('Edit', cl)
785+
db.security.addPermissionToRole('User', p)
786+
p = db.security.getPermission('Create', cl)
787+
db.security.addPermissionToRole('User', p)
788+
for cl in 'priority', 'status':
789+
p = db.security.getPermission('View', cl)
790+
db.security.addPermissionToRole('User', p)
791+
768792
# May users view other user information? Comment these lines out
769793
# if you don't want them to
770794
p = db.security.getPermission('View', 'user')
771795
db.security.addPermissionToRole('User', p)
772796

773-
# Assign the appropriate permissions to the anonymous user's
774-
# Anonymous role. Choices here are:
775-
# - Allow anonymous users to register through the web
776-
p = db.security.getPermission('Web Registration')
797+
# Users should be able to edit their own details. Note that this
798+
# permission is limited to only the situation where the Viewed or
799+
# Edited item is their own.
800+
def own_record(db, userid, itemid):
801+
'''Determine whether the userid matches the item being accessed.'''
802+
return userid == itemid
803+
p = db.security.addPermission(name='View', klass='user', check=own_record,
804+
description="User is allowed to view their own user details")
805+
p = db.security.addPermission(name='Edit', klass='user', check=own_record,
806+
description="User is allowed to edit their own user details")
807+
db.security.addPermissionToRole('User', p)
808+
809+
#
810+
# ANONYMOUS USER PERMISSIONS
811+
#
812+
# Let anonymous users access the web interface. Note that almost all
813+
# trackers will need this Permission. The only situation where it's not
814+
# required is in a tracker that uses an HTTP Basic Authenticated front-end.
815+
p = db.security.getPermission('Web Access')
816+
db.security.addPermissionToRole('Anonymous', p)
817+
818+
# Let anonymous users access the email interface (note that this implies
819+
# that they will be registered automatically, hence they will need the
820+
# "Create" user Permission below)
821+
p = db.security.getPermission('Email Access')
777822
db.security.addPermissionToRole('Anonymous', p)
778-
# - Allow anonymous (new) users to register through the email
779-
# gateway
780-
p = db.security.getPermission('Email Registration')
823+
824+
# Assign the appropriate permissions to the anonymous user's Anonymous
825+
# Role. Choices here are:
826+
# - Allow anonymous users to register
827+
p = db.security.getPermission('Create', 'user')
781828
db.security.addPermissionToRole('Anonymous', p)
782829

830+
# Allow anonymous users access to view issues (and the related, linked
831+
# information)
832+
for cl in 'issue', 'file', 'msg', 'keyword', 'priority', 'status':
833+
p = db.security.getPermission('View', cl)
834+
db.security.addPermissionToRole('Anonymous', p)
835+
836+
# [OPTIONAL]
837+
# Allow anonymous users access to create or edit "issue" items (and the
838+
# related file and message items)
839+
#for cl in 'issue', 'file', 'msg':
840+
# p = db.security.getPermission('Create', cl)
841+
# db.security.addPermissionToRole('Anonymous', p)
842+
# p = db.security.getPermission('Edit', cl)
843+
# db.security.addPermissionToRole('Anonymous', p)
844+
783845

784846
New User Roles
785847
--------------
@@ -806,7 +868,7 @@ Adding a new Permission
806868

807869
When adding a new Permission, you will need to:
808870

809-
1. add it to your tracker's dbinit so it is created, using
871+
1. add it to your tracker's ``schema.py`` so it is created, using
810872
``security.addPermission``, for example::
811873

812874
self.security.addPermission(name="View", klass='frozzle',
@@ -819,6 +881,18 @@ When adding a new Permission, you will need to:
819881
4. add it to the appropriate xxxPermission methods on in your tracker
820882
interfaces module
821883

884+
The ``addPermission`` method takes a couple of optional parameters:
885+
886+
**properties**
887+
A sequence of property names that are the only properties to apply the
888+
new Permission to (eg. ``... klass='user', properties=('name',
889+
'email') ...``)
890+
**code**
891+
A function to be execute which returns boolean determining whether the
892+
Permission is allowed. The function has the signature ``check(db, userid,
893+
itemid)`` where ``db`` is a handle on the open database, ``userid`` is
894+
the user attempting access and ``itemid`` is the specific item being
895+
accessed.
822896

823897
Example Scenarios
824898
~~~~~~~~~~~~~~~~~
@@ -1061,6 +1135,8 @@ Each action class also has a ``*permission*`` method which determines whether
10611135
the action is permissible given the current user. The base permission checks
10621136
are:
10631137

1138+
XXX REVIEW for Permissions changes
1139+
10641140
**login**
10651141
Determine whether the user has permission to log in. Base behaviour is
10661142
to check the user has "Web Access".
@@ -1070,14 +1146,12 @@ are:
10701146
Determine whether the user has permission to register. Base behaviour
10711147
is to check the user has the "Web Registration" Permission.
10721148
**edit**
1073-
Determine whether the user has permission to edit this item. Base
1074-
behaviour is to check whether the user can edit this class. If we're
1149+
Determine whether the user has permission to edit this item. If we're
10751150
editing the "user" class, users are allowed to edit their own details -
10761151
unless they try to edit the "roles" property, which requires the
10771152
special Permission "Web Roles".
10781153
**new**
1079-
Determine whether the user has permission to create (or edit) this
1080-
item. Base behaviour is to check the user can edit this class. No
1154+
Determine whether the user has permission to create this item. No
10811155
additional property checks are made. Additionally, new user items may
10821156
be created if the user has the "Web Registration" Permission.
10831157
**editCSV**
@@ -1164,11 +1238,11 @@ None of the above (ie. just a simple form value)
11641238

11651239
For a Link('klass') property, the form value is a
11661240
single key for 'klass', where the key field is
1167-
specified in dbinit.py.
1241+
specified in schema.py.
11681242

11691243
For a Multilink('klass') property, the form value is a
11701244
comma-separated list of keys for 'klass', where the
1171-
key field is specified in dbinit.py.
1245+
key field is specified in schema.py.
11721246

11731247
Note that for simple-form-variables specifiying Link
11741248
and Multilink properties, the linked-to class must
@@ -2510,7 +2584,7 @@ Adding a field to the database
25102584

25112585
This is the easiest part of the change. The category would just be a
25122586
plain string, nothing fancy. To change what is in the database you need
2513-
to add some lines to the ``open()`` function in ``dbinit.py``. Under the
2587+
to add some lines to the ``open()`` function in ``schema.py``. Under the
25142588
comment::
25152589

25162590
# add any additional database schema configuration here
@@ -2533,7 +2607,7 @@ Adding the above lines allows us to create categories, but they're not
25332607
tied to the issues that we are going to be creating. It's just a list of
25342608
categories off on its own, which isn't much use. We need to link it in
25352609
with the issues. To do that, find the lines in the ``open()`` function
2536-
in ``dbinit.py`` which set up the "issue" class, and then add a link to
2610+
in ``schema.py`` which set up the "issue" class, and then add a link to
25372611
the category::
25382612

25392613
issue = IssueClass(db, "issue", ... ,
@@ -2552,7 +2626,7 @@ Populating the new category class
25522626

25532627
If you haven't initialised the database with the roundup-admin
25542628
"initialise" command, then you can add the following to the tracker
2555-
``dbinit.py`` in the ``init()`` function under the comment::
2629+
``schema.py`` in the ``init()`` function under the comment::
25562630

25572631
# add any additional database create steps here - but only if you
25582632
# haven't initialised the database with the admin "initialise" command
@@ -2591,7 +2665,7 @@ as required, and obviously everyone needs to be able to view the
25912665
categories of issues for it to be useful.
25922666

25932667
We therefore need to change the security of the category objects. This
2594-
is also done in the ``open()`` function of ``dbinit.py``.
2668+
is also done in the ``open()`` function of ``schema.py``.
25952669

25962670
There are currently two loops which set up permissions and then assign
25972671
them to various roles. Simply add the new "category" to both lists::
@@ -2908,7 +2982,7 @@ Adding a time log to your issues
29082982
We want to log the dates and amount of time spent working on issues, and
29092983
be able to give a summary of the total time spent on a particular issue.
29102984

2911-
1. Add a new class to your tracker ``dbinit.py``::
2985+
1. Add a new class to your tracker ``schema.py``::
29122986

29132987
# storage for time logging
29142988
timelog = Class(db, "timelog", period=Interval())
@@ -2917,7 +2991,7 @@ be able to give a summary of the total time spent on a particular issue.
29172991
creation through the standard property "creation".
29182992

29192993
2. Link to the new class from your issue class (again, in
2920-
``dbinit.py``)::
2994+
``schema.py``)::
29212995

29222996
issue = IssueClass(db, "issue",
29232997
assignedto=Link("user"), topic=Multilink("keyword"),
@@ -3012,7 +3086,7 @@ a customer support issue class to a tracker.
30123086
this is obvious, but sometimes it's better to actually sit down for a
30133087
while and think about the schema you're going to implement.
30143088

3015-
2. Add the new issue class to your tracker's ``dbinit.py`` - in this
3089+
2. Add the new issue class to your tracker's ``schema.py`` - in this
30163090
example, we're adding a "system support" class. Just after the "issue"
30173091
class definition in the "open" function, add::
30183092

@@ -3460,7 +3534,7 @@ they can't be resolved until another issue (the blocker) they rely on is
34603534
resolved. To achieve this:
34613535

34623536
1. Create a new property on the issue Class,
3463-
``blockers=Multilink("issue")``. Edit your tracker's dbinit.py file.
3537+
``blockers=Multilink("issue")``. Edit your tracker's schema.py file.
34643538
Where the "issue" class is defined, something like::
34653539

34663540
issue = IssueClass(db, "issue",
@@ -3616,7 +3690,7 @@ a list of topics for which he wants to be put on the nosy list. Adding
36163690
a ``Multilink`` of ``keyword`` seem to fullfill this (note that within
36173691
the code topics are called ``keywords``.) As such, all what has to be
36183692
done is to add a new field to the definition of ``user`` within the
3619-
file ``dbinit.py``. We will call this new field ``nosy_keywords``, and
3693+
file ``schema.py``. We will call this new field ``nosy_keywords``, and
36203694
the updated definition of user will be::
36213695

36223696
user = Class(db, "user",
@@ -3764,7 +3838,7 @@ Changes to Security and Permissions
37643838
Restricting the list of users that are assignable to a task
37653839
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
37663840

3767-
1. In your tracker's "dbinit.py", create a new Role, say "Developer"::
3841+
1. In your tracker's ``schema.py``, create a new Role, say "Developer"::
37683842

37693843
db.security.addRole(name='Developer', description='A developer')
37703844

@@ -3829,7 +3903,7 @@ Own" on issues (regular users have "Edit".) We back up the permissions with
38293903
an auditor.
38303904

38313905
First up, we create the new Role and Permission structure in
3832-
``dbinit.py``::
3906+
``schema.py``::
38333907

38343908
# New users not approved by the admin
38353909
db.security.addRole(name='Provisional User',

doc/design.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,11 +1401,20 @@ The security module defines::
14011401
- name
14021402
- description
14031403
- klass (optional)
1404+
- properties (optional)
1405+
- check function (optional)
14041406

14051407
The klass may be unset, indicating that this permission is
14061408
not locked to a particular hyperdb class. There may be
14071409
multiple Permissions for the same name for different
14081410
classes.
1411+
1412+
If property names are set, permission is restricted to those
1413+
properties only.
1414+
1415+
If check function is set, permission is granted only when
1416+
the function returns value interpreted as boolean true.
1417+
The function is called with arguments db, userid, itemid.
14091418
'''
14101419

14111420
class Role:

0 commit comments

Comments
 (0)