changeset 3126:a2889d22db4a

the cgi templating code now checks item-level permissions (per alex's suggestion). The templates themselves do not have row-level checks now. Cleaned up the msg and file index templates to use batching.
author Richard Jones <richard@users.sourceforge.net>
date Fri, 04 Feb 2005 05:25:50 +0000
parents 5894c7bea8ce
children 021b131bd816
files CHANGES.txt doc/customizing.txt roundup/cgi/templating.py templates/classic/html/file.index.html templates/classic/html/issue.index.html templates/classic/html/msg.index.html
diffstat 6 files changed, 103 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Fri Jan 28 05:09:44 2005 +0000
+++ b/CHANGES.txt	Fri Feb 04 05:25:50 2005 +0000
@@ -7,7 +7,8 @@
 - fix initialisation of roundup-server in daemon mode so initialisation
   errors are visible
 - have Permissions only test the check function if itemid is suppled
-- modify index templates to check for row-level Permission
+- modify cgi templating system to check item-level permissions in listings
+- enable batching in message and file listings
 - more documentation of security mechanisms
 - better unit tests for security mechanisms
 
--- a/doc/customizing.txt	Fri Jan 28 05:09:44 2005 +0000
+++ b/doc/customizing.txt	Fri Feb 04 05:25:50 2005 +0000
@@ -2,7 +2,7 @@
 Customising Roundup
 ===================
 
-:Version: $Revision: 1.169 $
+:Version: $Revision: 1.170 $
 
 .. This document borrows from the ZopeBook section on ZPT. The original is at:
    http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx
@@ -933,6 +933,8 @@
 Example Scenarios
 ~~~~~~~~~~~~~~~~~
 
+See the `examples`_ section for longer examples of customisation.
+
 **automatic registration of users in the e-mail gateway**
  By giving the "anonymous" user the "Email Registration" Role, any
  unidentified user will automatically be registered with the tracker
@@ -4037,6 +4039,26 @@
     new_email_user_roles = 'Provisional User'
 
 
+All users may only view and edit issues, files and messages they create
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Replace the standard "classic" tracker View and Edit Permission assignments
+for the "issue", "file" and "msg" classes with the following::
+
+    def checker(klass):
+        def check(db, userid, itemid, klass=klass):
+            return db.getclass(klass).get(itemid, 'creator') == userid
+    for cl in 'issue', 'file', 'msg':
+        p = db.security.addPermission(name='View', klass=cl,
+            check=checker(cl))
+        db.security.addPermissionToRole('User', p)
+        p = db.security.addPermission(name='Edit', klass=cl,
+            check=checker(cl))
+        db.security.addPermissionToRole('User', p)
+        db.security.addPermissionToRole('User', 'Create', cl)
+
+
+
 Changes to the Web User Interface
 ---------------------------------
 
--- a/roundup/cgi/templating.py	Fri Jan 28 05:09:44 2005 +0000
+++ b/roundup/cgi/templating.py	Fri Feb 04 05:25:50 2005 +0000
@@ -558,10 +558,16 @@
         '''
         # get the list and sort it nicely
         l = self._klass.list()
-        sortfunc = make_sort_function(self._db, self.classname, sort_on)
+        sortfunc = make_sort_function(self._db, self._classname, sort_on)
         l.sort(sortfunc)
 
-        l = [HTMLItem(self._client, self.classname, x) for x in l]
+        # check perms
+        check = self._client.db.security.hasPermission
+        userid = self._client.userid
+
+        l = [HTMLItem(self._client, self._classname, id) for id in l
+            if check('View', userid, self._classname, itemid=id)]
+
         return l
 
     def csv(self):
@@ -605,8 +611,13 @@
             filterspec = request.filterspec
             sort = request.sort
             group = request.group
+
+        check = self._db.security.hasPermission
+        userid = self._client.userid
+
         l = [HTMLItem(self._client, self.classname, x)
-             for x in self._klass.filter(None, filterspec, sort, group)]
+             for id in self._klass.filter(None, filterspec, sort, group)
+             if check('View', userid, self.classname, itemid=id)]
         return l
 
     def classhelp(self, properties=None, label=''"(list)", width='500',
@@ -1643,6 +1654,28 @@
         return '\n'.join(l)
 #    def checklist(self, ...)
 
+class MultilinkIterator:
+    def __init__(self, classname, client, values):
+        self.classname = classname
+        self.client = client
+        self.values = values
+        self.id = -1
+        self.cl = self.client.db.getclass(self.classname)
+    def next(self):
+        '''Return the next item, but skip inaccessible items.'''
+        check = self.client.db.security.hasPermission
+        userid = self.client.userid
+        while 1:
+            self.id += 1
+            if self.id >= len(self.values):
+                raise StopIteration
+            value = self.values[self.id]
+            if check('View', userid, self.classname, itemid=value):
+                return HTMLItem(self.client, self.classname, value)
+    def __iter__(self):
+        return self
+
+
 class MultilinkHTMLProperty(HTMLProperty):
     ''' Multilink HTMLProperty
 
@@ -1665,16 +1698,22 @@
         ''' no extended attribute accesses make sense here '''
         raise AttributeError, attr
 
-    def __getitem__(self, num):
+    def __iter__(self):
         ''' iterate and return a new HTMLItem
         '''
-       #print 'Multi.getitem', (self, num)
-        value = self._value[num]
-        return HTMLItem(self._client, self._prop.classname, value)
+        return MultilinkIterator(self._prop.classname, self._client,
+            self._value)
+
+    def reverse(self):
+        ''' return the list in reverse order
+        '''
+        l = self._value[:]
+        l.reverse()
+        return MultilinkIterator(self._prop.classname, self._client, l)
 
     def sorted(self, property):
         ''' Return this multilink sorted by the given property '''
-        value = list(self._value[num])
+        value = list(self.__iter__())
         value.sort(lambda a,b:cmp(a[property], b[property]))
         return value
 
@@ -1688,14 +1727,6 @@
         '''Is my _value not []?'''
         return self._value != []
 
-    def reverse(self):
-        ''' return the list in reverse order
-        '''
-        l = self._value[:]
-        l.reverse()
-        return [HTMLItem(self._client, self._prop.classname, value)
-            for value in l]
-
     def plain(self, escape=0):
         ''' Render a "plain" representation of the property
         '''
@@ -2138,7 +2169,12 @@
                 re.findall(r'\b\w{2,25}\b', self.search_text), klass)
         else:
             matches = None
-        l = klass.filter(matches, filterspec, sort, group)
+
+        # filter for visibility
+        check = self._client.db.security.hasPermission
+        userid = self._client.userid
+        l = [id for id in klass.filter(matches, filterspec, sort, group)
+            if check('View', userid, self.classname, itemid=id)]
 
         # return the batch object, using IDs only
         return Batch(self.client, l, self.pagesize, self.startwith,
--- a/templates/classic/html/file.index.html	Fri Jan 28 05:09:44 2005 +0000
+++ b/templates/classic/html/file.index.html	Fri Feb 04 05:25:50 2005 +0000
@@ -6,24 +6,24 @@
   i18n:translate="">List of files</span>
 <td class="content" metal:fill-slot="content">
 
-<table class="otherinfo">
-<tr><th style="padding-right: 10" i18n:translate="">Download</th>
-    <th style="padding-right: 10" i18n:translate="">Content Type</th>
-    <th style="padding-right: 10" i18n:translate="">Uploaded By</th>
-    <th style="padding-right: 10" i18n:translate="">Date</th>
-</tr>
- <tal:block repeat="file context/list">
- <tr tal:condition="file/is_view_ok"
-     tal:attributes="class python:['normal', 'alt'][repeat['file'].index%6/3]">
- <td>
-  <a tal:attributes="href string:file${file/id}/${file/name}"
-     tal:content="file/name">dld link</a>
- </td>
- <td tal:content="file/type">content type</td>
- <td tal:content="file/creator">creator's name</td>
- <td tal:content="file/creation">creation date</td>
+<table class="otherinfo" tal:define="batch request/batch">
+ <tr><th style="padding-right: 10" i18n:translate="">Download</th>
+     <th style="padding-right: 10" i18n:translate="">Content Type</th>
+     <th style="padding-right: 10" i18n:translate="">Uploaded By</th>
+     <th style="padding-right: 10" i18n:translate="">Date</th>
  </tr>
- </tal:block>
+ <tr tal:repeat="file batch" tal:attributes="class python:['normal', 'alt'][repeat['file'].index%6/3]">
+  <td>
+   <a tal:attributes="href string:file${file/id}/${file/name}"
+      tal:content="file/name">dld link</a>
+  </td>
+  <td tal:content="file/type">content type</td>
+  <td tal:content="file/creator">creator's name</td>
+  <td tal:content="file/creation">creation date</td>
+ </tr>
+
+ <metal:block use-macro="templates/issue.index/macros/batch-footer" />
+
 </table>
 
 </td>
--- a/templates/classic/html/issue.index.html	Fri Jan 28 05:09:44 2005 +0000
+++ b/templates/classic/html/issue.index.html	Fri Feb 04 05:25:50 2005 +0000
@@ -32,7 +32,7 @@
    </th>
   </tr>
 
-  <tr tal:condition="i/is_view_ok">
+  <tr>
    <td tal:condition="request/show/priority"
        tal:content="python:i.priority.plain() or default">&nbsp;</td>
    <td tal:condition="request/show/id" tal:content="i/id">&nbsp;</td>
@@ -58,6 +58,7 @@
 
  </tal:block>
 
+ <metal:index define-macro="batch-footer">
  <tr tal:condition="batch">
   <th tal:attributes="colspan python:len(request.columns)">
    <table width="100%">
@@ -84,6 +85,7 @@
    </table>
   </th>
  </tr>
+ </metal:index>
 </table>
 
 <a tal:attributes="href python:request.indexargs_url('issue',
--- a/templates/classic/html/msg.index.html	Fri Jan 28 05:09:44 2005 +0000
+++ b/templates/classic/html/msg.index.html	Fri Feb 04 05:25:50 2005 +0000
@@ -5,10 +5,9 @@
 <span metal:fill-slot="body_title" tal:omit-tag="python:1"
  i18n:translate="">Message listing</span>
 <td class="content" metal:fill-slot="content">
-<table class="messages" tal:condition="request/filter">
+<table tal:define="batch request/batch" class="messages">
  <tr><th colspan=2 class="header" i18n:translate="">Messages</th></tr>
- <tal:block tal:repeat="msg context/list">
- <tal:block tal:condition="msg/is_view_ok">
+ <tal:block tal:repeat="msg batch">
   <tr>
    <th tal:content="string:Author: ${msg/author}">author</th>
    <th tal:content="string:Date: ${msg/date}">date</th>
@@ -17,7 +16,9 @@
    <td colspan="2"><pre tal:content="msg/content">content</pre></td>
   </tr>
  </tal:block>
- </tal:block>
+
+ <metal:block use-macro="templates/issue.index/macros/batch-footer" />
+
 </table>
 </td>
 

Roundup Issue Tracker: http://roundup-tracker.org/