changeset 3973:85cbaa50eba1

xml-rpc security checks and tests across all backends [SF#1907211] also add some leap year tests
author Richard Jones <richard@users.sourceforge.net>
date Fri, 07 Mar 2008 01:11:55 +0000
parents eee76dd4a09f
children ff089ebe95d1
files CHANGES.txt roundup/xmlrpc.py test/db_test_base.py test/test_dates.py test/test_xmlrpc.py
diffstat 5 files changed, 82 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.txt	Fri Mar 07 00:35:47 2008 +0000
+++ b/CHANGES.txt	Fri Mar 07 01:11:55 2008 +0000
@@ -4,6 +4,7 @@
 2008-03-01 1.4.5
 Fixed:
 - 'Make a Copy' failed with more than one person in nosy list (sf #1906147)
+- xml-rpc security checks and tests across all backends (sf #1907211)
 
 
 2008-03-01 1.4.4
--- a/roundup/xmlrpc.py	Fri Mar 07 00:35:47 2008 +0000
+++ b/roundup/xmlrpc.py	Fri Mar 07 01:11:55 2008 +0000
@@ -63,13 +63,10 @@
     def close(self):
         """Close the database, after committing any changes, if needed."""
 
-        if getattr(self, 'db'):
-            try:
-                if self.db.transactions:
-                    self.db.commit()
-            finally:
-                self.db.close()
-
+        try:
+            self.db.commit()
+        finally:
+            self.db.close()
 
     def get_class(self, classname):
         """Return the class for the given classname."""
@@ -94,7 +91,7 @@
             if value:
                 try:
                     props[key] = hyperdb.rawToHyperdb(self.db, cl, None,
-                                                      key, value)
+                        key, value)
                 except hyperdb.HyperdbValueError, message:
                     raise UsageError, message
             else:
@@ -115,51 +112,53 @@
 
     def list(self, username, password, classname, propname=None):
         r = RoundupRequest(self.tracker, username, password)
-        cl = r.get_class(classname)
-        if not propname:
-            propname = cl.labelprop()
-        def has_perm(itemid):
-            return True
-            r.db.security.hasPermission('View', r.userid, classname,
-                itemid=itemid, property=propname)
-        result = [cl.get(id, propname) for id in cl.list()
-            if has_perm(id)]
-        r.close()
+        try:
+            cl = r.get_class(classname)
+            if not propname:
+                propname = cl.labelprop()
+            result = [cl.get(itemid, propname)
+                for itemid in cl.list()
+                     if r.db.security.hasPermission('View', r.userid,
+                         classname, propname, itemid)
+            ]
+        finally:
+            r.close()
         return result
 
     def display(self, username, password, designator, *properties):
         r = RoundupRequest(self.tracker, username, password)
-        classname, itemid = hyperdb.splitDesignator(designator)
-
-        if not r.db.security.hasPermission('View', r.userid, classname,
-                itemid=itemid):
-            raise Unauthorised('Permission to view %s denied'%designator)
-
-        cl = r.get_class(classname)
-        props = properties and list(properties) or cl.properties.keys()
-        props.sort()
-        result = [(property, cl.get(itemid, property)) for property in props]
-        r.close()
+        try:
+            classname, itemid = hyperdb.splitDesignator(designator)
+            cl = r.get_class(classname)
+            props = properties and list(properties) or cl.properties.keys()
+            props.sort()
+            for p in props:
+                if not r.db.security.hasPermission('View', r.userid,
+                        classname, p, itemid):
+                    raise Unauthorised('Permission to view %s of %s denied'%
+                            (p, designator))
+            result = [(prop, cl.get(itemid, prop)) for prop in props]
+        finally:
+            r.close()
         return dict(result)
 
     def create(self, username, password, classname, *args):
         r = RoundupRequest(self.tracker, username, password)
+        try:
+            if not r.db.security.hasPermission('Create', r.userid, classname):
+                raise Unauthorised('Permission to create %s denied'%classname)
 
-        if not r.db.security.hasPermission('Create', r.userid, classname):
-            raise Unauthorised('Permission to create %s denied'%classname)
-
-        cl = r.get_class(classname)
+            cl = r.get_class(classname)
 
-        # convert types
-        props = r.props_from_args(cl, args)
+            # convert types
+            props = r.props_from_args(cl, args)
 
-        # check for the key property
-        key = cl.getkey()
-        if key and not props.has_key(key):
-            raise UsageError, 'you must provide the "%s" property.'%key
+            # check for the key property
+            key = cl.getkey()
+            if key and not props.has_key(key):
+                raise UsageError, 'you must provide the "%s" property.'%key
 
-        # do the actual create
-        try:
+            # do the actual create
             try:
                 result = cl.create(**props)
             except (TypeError, IndexError, ValueError), message:
@@ -170,19 +169,17 @@
 
     def set(self, username, password, designator, *args):
         r = RoundupRequest(self.tracker, username, password)
-        classname, itemid = hyperdb.splitDesignator(designator)
-
-        if not r.db.security.hasPermission('Edit', r.userid, classname,
-                itemid=itemid):
-            raise Unauthorised('Permission to edit %s denied'%designator)
-
-        cl = r.get_class(classname)
-
-        # convert types
-        props = r.props_from_args(cl, args)
         try:
+            classname, itemid = hyperdb.splitDesignator(designator)
+            cl = r.get_class(classname)
+            props = r.props_from_args(cl, args) # convert types
+            for p in props.iterkeys ():
+                if not r.db.security.hasPermission('Edit', r.userid,
+                        classname, p, itemid):
+                    raise Unauthorised('Permission to edit %s of %s denied'%
+                        (p, designator))
             try:
-                cl.set(itemid, **props)
+                return cl.set(itemid, **props)
             except (TypeError, IndexError, ValueError), message:
                 raise UsageError, message
         finally:
--- a/test/db_test_base.py	Fri Mar 07 00:35:47 2008 +0000
+++ b/test/db_test_base.py	Fri Mar 07 01:11:55 2008 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 #
-# $Id: db_test_base.py,v 1.96 2008-02-07 03:28:34 richard Exp $
+# $Id: db_test_base.py,v 1.97 2008-03-07 01:11:55 richard Exp $
 
 import unittest, os, shutil, errno, imp, sys, time, pprint, sets, base64, os.path
 
@@ -62,6 +62,7 @@
     tracker = instance.open(dirname)
     if tracker.exists():
         tracker.nuke()
+        init.write_select_db(dirname, backend)
     tracker.init(password.Password('sekrit'))
     return tracker
 
@@ -293,7 +294,7 @@
             l = [u1,u2]; l.sort()
             m = self.db.issue.get(nid, "nosy"); m.sort()
             self.assertEqual(l, m)
-       
+
 
 # XXX one day, maybe...
 #    def testMultilinkOrdering(self):
@@ -329,6 +330,18 @@
                 c = self.db.issue.get(nid, "deadline")
                 self.assertEqual(c, d)
 
+    def testDateLeapYear(self):
+        nid = self.db.issue.create(title='spam', status='1',
+            deadline=date.Date('2008-02-29'))
+        self.assertEquals(str(self.db.issue.get(nid, 'deadline')),
+            '2008-02-29.00:00:00')
+        self.db.issue.set(nid, deadline=date.Date('2008-02-29'))
+        self.assertEquals(str(self.db.issue.get(nid, 'deadline')),
+            '2008-02-29.00:00:00')
+        self.assertEquals(self.db.issue.filter(None, {'deadline': '2008-02-29'}),
+            [nid])
+
+
     def testDateUnset(self):
         for commit in (0,1):
             nid = self.db.issue.create(title="spam", status='1')
--- a/test/test_dates.py	Fri Mar 07 00:35:47 2008 +0000
+++ b/test/test_dates.py	Fri Mar 07 01:11:55 2008 +0000
@@ -15,7 +15,7 @@
 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 #
-# $Id: test_dates.py,v 1.44 2007-12-23 00:23:23 richard Exp $
+# $Id: test_dates.py,v 1.45 2008-03-07 01:11:55 richard Exp $
 from __future__ import nested_scopes
 
 import unittest
@@ -68,6 +68,9 @@
         ae(str(Date('1900-02-01')), '1900-02-01.00:00:00')
         ae(str(Date('1800-07-15')), '1800-07-15.00:00:00')
 
+    def testLeapYear(self):
+        self.assertEquals(str(Date('2008-02-29')), '2008-02-29.00:00:00')
+
     def testDateError(self):
         self.assertRaises(ValueError, Date, "12")
         # Date cannot handle dates before year 1
--- a/test/test_xmlrpc.py	Fri Mar 07 00:35:47 2008 +0000
+++ b/test/test_xmlrpc.py	Fri Mar 07 01:11:55 2008 +0000
@@ -9,16 +9,20 @@
 from roundup.cgi.exceptions import *
 from roundup import init, instance, password, hyperdb, date
 from roundup.xmlrpc import RoundupServer
+from roundup.backends import list_backends
 
 import db_test_base
 
 NEEDS_INSTANCE = 1
 
 class TestCase(unittest.TestCase):
+
+    backend = None
+
     def setUp(self):
         self.dirname = '_test_xmlrpc'
         # set up and open a tracker
-        self.instance = db_test_base.setupTracker(self.dirname)
+        self.instance = db_test_base.setupTracker(self.dirname, self.backend)
 
         # open the database
         self.db = self.instance.open('admin')
@@ -55,6 +59,10 @@
             'realname')
         self.assertEqual(results['realname'], 'Joe Doe')
 
+        # check we can't change admin's details
+        self.assertRaises(Unauthorised, self.server.set, 'joe', 'random',
+            'user1', 'realname=Joe Doe')
+
     def testCreate(self):
         results = self.server.create('joe', 'random', 'issue', 'title=foo')
         issueid = 'issue' + results
@@ -89,10 +97,12 @@
 
 def test_suite():
     suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestCase))
+    for l in list_backends():
+        dct = dict(backend = l)
+        subcls = type(TestCase)('TestCase_%s'%l, (TestCase,), dct)
+        suite.addTest(unittest.makeSuite(subcls))
     return suite
 
 if __name__ == '__main__':
     runner = unittest.TextTestRunner()
     unittest.main(testRunner=runner)
-

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