Code

- Add explicit "Search" permissions, see Security Fix below.
[roundup.git] / test / test_xmlrpc.py
1 #
2 # Copyright (C) 2007 Stefan Seefeld
3 # All rights reserved.
4 # For license terms see the file COPYING.txt.
5 #
7 import unittest, os, shutil, errno, sys, difflib, cgi, re
9 from roundup.cgi.exceptions import *
10 from roundup import init, instance, password, hyperdb, date
11 from roundup.xmlrpc import RoundupInstance
12 from roundup.backends import list_backends
14 import db_test_base
16 NEEDS_INSTANCE = 1
18 class TestCase(unittest.TestCase):
20     backend = None
22     def setUp(self):
23         self.dirname = '_test_xmlrpc'
24         # set up and open a tracker
25         self.instance = db_test_base.setupTracker(self.dirname, self.backend)
27         # open the database
28         self.db = self.instance.open('admin')
29         self.joeid = 'user' + self.db.user.create(username='joe',
30             password=password.Password('random'), address='random@home.org',
31             realname='Joe Random', roles='User')
33         self.db.commit()
34         self.db.close()
35         self.db = self.instance.open('joe')
36         self.server = RoundupInstance(self.db, self.instance.actions, None)
38     def tearDown(self):
39         self.db.close()
40         try:
41             shutil.rmtree(self.dirname)
42         except OSError, error:
43             if error.errno not in (errno.ENOENT, errno.ESRCH): raise
45     def testAccess(self):
46         # Retrieve all three users.
47         results = self.server.list('user', 'id')
48         self.assertEqual(len(results), 3)
50         # Obtain data for 'joe'.
51         results = self.server.display(self.joeid)
52         self.assertEqual(results['username'], 'joe')
53         self.assertEqual(results['realname'], 'Joe Random')
55     def testChange(self):
56         # Reset joe's 'realname'.
57         results = self.server.set(self.joeid, 'realname=Joe Doe')
58         results = self.server.display(self.joeid, 'realname')
59         self.assertEqual(results['realname'], 'Joe Doe')
61         # check we can't change admin's details
62         self.assertRaises(Unauthorised, self.server.set, 'user1', 'realname=Joe Doe')
64     def testCreate(self):
65         results = self.server.create('issue', 'title=foo')
66         issueid = 'issue' + results
67         results = self.server.display(issueid, 'title')
68         self.assertEqual(results['title'], 'foo')
70     def testFileCreate(self):
71         results = self.server.create('file', 'content=hello\r\nthere')
72         fileid = 'file' + results
73         results = self.server.display(fileid, 'content')
74         self.assertEqual(results['content'], 'hello\r\nthere')
76     def testAction(self):
77         # As this action requires special previledges, we temporarily switch
78         # to 'admin'
79         self.db.setCurrentUser('admin')
80         users_before = self.server.list('user')
81         try:
82             tmp = 'user' + self.db.user.create(username='tmp')
83             self.server.action('retire', tmp)
84         finally:
85             self.db.setCurrentUser('joe')
86         users_after = self.server.list('user')
87         self.assertEqual(users_before, users_after)
89     def testAuthDeniedEdit(self):
90         # Wrong permissions (caught by roundup security module).
91         self.assertRaises(Unauthorised, self.server.set,
92                           'user1', 'realname=someone')
94     def testAuthDeniedCreate(self):
95         self.assertRaises(Unauthorised, self.server.create,
96                           'user', {'username': 'blah'})
98     def testAuthAllowedEdit(self):
99         self.db.setCurrentUser('admin')
100         try:
101             try:
102                 self.server.set('user2', 'realname=someone')
103             except Unauthorised, err:
104                 self.fail('raised %s'%err)
105         finally:
106             self.db.setCurrentUser('joe')
108     def testAuthAllowedCreate(self):
109         self.db.setCurrentUser('admin')
110         try:
111             try:
112                 self.server.create('user', 'username=blah')
113             except Unauthorised, err:
114                 self.fail('raised %s'%err)
115         finally:
116             self.db.setCurrentUser('joe')
118     def testAuthFilter(self):
119         # this checks if we properly check for search permissions
120         self.db.security.permissions = {}
121         self.db.security.addRole(name='User')
122         self.db.security.addRole(name='Project')
123         self.db.security.addPermissionToRole('User', 'Web Access')
124         self.db.security.addPermissionToRole('Project', 'Web Access')
125         # Allow viewing keyword
126         p = self.db.security.addPermission(name='View', klass='keyword')
127         self.db.security.addPermissionToRole('User', p)
128         # Allow viewing interesting things (but not keyword) on issue
129         # But users might only view issues where they are on nosy
130         # (so in the real world the check method would be better)
131         p = self.db.security.addPermission(name='View', klass='issue',
132             properties=("title", "status"), check=lambda x,y,z: True)
133         self.db.security.addPermissionToRole('User', p)
134         # Allow role "Project" access to whole issue
135         p = self.db.security.addPermission(name='View', klass='issue')
136         self.db.security.addPermissionToRole('Project', p)
138         keyword = self.db.keyword
139         status = self.db.status
140         issue = self.db.issue
142         d1 = keyword.create(name='d1')
143         d2 = keyword.create(name='d2')
144         open = status.create(name='open')
145         closed = status.create(name='closed')
146         issue.create(title='i1', status=open, keyword=[d2])
147         issue.create(title='i2', status=open, keyword=[d1])
148         issue.create(title='i2', status=closed, keyword=[d1])
150         chef = self.db.user.create(username = 'chef', roles='User, Project')
151         joe  = self.db.user.lookup('joe')
153         # Conditionally allow view of whole issue (check is False here,
154         # this might check for keyword owner in the real world)
155         p = self.db.security.addPermission(name='View', klass='issue',
156             check=lambda x,y,z: False)
157         self.db.security.addPermissionToRole('User', p)
158         # Allow user to search for issue.status
159         p = self.db.security.addPermission(name='Search', klass='issue',
160             properties=("status",))
161         self.db.security.addPermissionToRole('User', p)
163         keyw = {'keyword':self.db.keyword.lookup('d1')}
164         stat = {'status':self.db.status.lookup('open')}
165         keygroup = keysort = [('+', 'keyword')]
166         self.db.commit()
168         # Filter on keyword ignored for role 'User':
169         r = self.server.filter('issue', None, keyw)
170         self.assertEqual(r, ['1', '2', '3'])
171         # Filter on status works for all:
172         r = self.server.filter('issue', None, stat)
173         self.assertEqual(r, ['1', '2'])
174         # Sorting and grouping for class User fails:
175         r = self.server.filter('issue', None, {}, sort=keysort)
176         self.assertEqual(r, ['1', '2', '3'])
177         r = self.server.filter('issue', None, {}, group=keygroup)
178         self.assertEqual(r, ['1', '2', '3'])
180         self.db.close()
181         self.db = self.instance.open('chef')
182         self.server = RoundupInstance(self.db, self.instance.actions, None)
184         # Filter on keyword works for role 'Project':
185         r = self.server.filter('issue', None, keyw)
186         self.assertEqual(r, ['2', '3'])
187         # Filter on status works for all:
188         r = self.server.filter('issue', None, stat)
189         self.assertEqual(r, ['1', '2'])
190         # Sorting and grouping for class Project works:
191         r = self.server.filter('issue', None, {}, sort=keysort)
192         self.assertEqual(r, ['2', '3', '1'])
193         r = self.server.filter('issue', None, {}, group=keygroup)
194         self.assertEqual(r, ['2', '3', '1'])
196 def test_suite():
197     suite = unittest.TestSuite()
198     for l in list_backends():
199         dct = dict(backend = l)
200         subcls = type(TestCase)('TestCase_%s'%l, (TestCase,), dct)
201         suite.addTest(unittest.makeSuite(subcls))
202     return suite
204 if __name__ == '__main__':
205     runner = unittest.TextTestRunner()
206     unittest.main(testRunner=runner)