comparison test/test_cgi.py @ 4437:261c9f913ff7

- Add explicit "Search" permissions, see Security Fix below. - Security Fix: Add a check for search-permissions: now we allow searching for properties only if the property is readable without a check method or if an explicit search permission (see above unter "Features) is given for the property. This fixes cases where a user doesn't have access to a property but can deduce the content by crafting a clever search, group or sort query. see doc/upgrading.txt for how to fix your trackers!
author Ralf Schlatterbeck <schlatterbeck@users.sourceforge.net>
date Tue, 19 Oct 2010 15:29:05 +0000
parents 8e0d350ce644
children 17f796a78647
comparison
equal deleted inserted replaced
4436:528ace81fd16 4437:261c9f913ff7
12 12
13 import unittest, os, shutil, errno, sys, difflib, cgi, re, StringIO 13 import unittest, os, shutil, errno, sys, difflib, cgi, re, StringIO
14 14
15 from roundup.cgi import client, actions, exceptions 15 from roundup.cgi import client, actions, exceptions
16 from roundup.cgi.exceptions import FormError 16 from roundup.cgi.exceptions import FormError
17 from roundup.cgi.templating import HTMLItem 17 from roundup.cgi.templating import HTMLItem, HTMLRequest
18 from roundup.cgi.form_parser import FormParser 18 from roundup.cgi.form_parser import FormParser
19 from roundup import init, instance, password, hyperdb, date 19 from roundup import init, instance, password, hyperdb, date
20 20
21 from mocknull import MockNull 21 from mocknull import MockNull
22 22
614 614
615 # 615 #
616 # SECURITY 616 # SECURITY
617 # 617 #
618 # XXX test all default permissions 618 # XXX test all default permissions
619 def _make_client(self, form, classname='user', nodeid='1', userid='2'): 619 def _make_client(self, form, classname='user', nodeid='1',
620 userid='2', template='item'):
620 cl = client.Client(self.instance, None, {'PATH_INFO':'/', 621 cl = client.Client(self.instance, None, {'PATH_INFO':'/',
621 'REQUEST_METHOD':'POST'}, makeForm(form)) 622 'REQUEST_METHOD':'POST'}, makeForm(form))
622 cl.classname = 'user' 623 cl.classname = classname
623 if nodeid is not None: 624 if nodeid is not None:
624 cl.nodeid = nodeid 625 cl.nodeid = nodeid
625 cl.db = self.db 626 cl.db = self.db
626 cl.userid = userid 627 cl.userid = userid
627 cl.language = ('en',) 628 cl.language = ('en',)
628 cl.error_message = [] 629 cl.error_message = []
629 cl.template = 'item' 630 cl.template = template
630 return cl 631 return cl
631 632
632 def testClassPermission(self): 633 def testClassPermission(self):
633 cl = self._make_client(dict(username='bob')) 634 cl = self._make_client(dict(username='bob'))
634 self.failUnlessRaises(exceptions.Unauthorised, 635 self.failUnlessRaises(exceptions.Unauthorised,
722 actions.EditItemAction(cl).handle) 723 actions.EditItemAction(cl).handle)
723 cl = self._make_client(dict(roles='User,Admin')) 724 cl = self._make_client(dict(roles='User,Admin'))
724 self.assertRaises(exceptions.Unauthorised, 725 self.assertRaises(exceptions.Unauthorised,
725 actions.EditItemAction(cl).handle) 726 actions.EditItemAction(cl).handle)
726 727
728 def testSearchPermission(self):
729 # this checks if we properly check for search permissions
730 self.db.security.permissions = {}
731 self.db.security.addRole(name='User')
732 self.db.security.addRole(name='Project')
733 self.db.security.addPermissionToRole('User', 'Web Access')
734 self.db.security.addPermissionToRole('Project', 'Web Access')
735 # Allow viewing department
736 p = self.db.security.addPermission(name='View', klass='department')
737 self.db.security.addPermissionToRole('User', p)
738 # Allow viewing interesting things (but not department) on iss
739 # But users might only view issues where they are on nosy
740 # (so in the real world the check method would be better)
741 p = self.db.security.addPermission(name='View', klass='iss',
742 properties=("title", "status"), check=lambda x,y,z: True)
743 self.db.security.addPermissionToRole('User', p)
744 # Allow role "Project" access to whole iss
745 p = self.db.security.addPermission(name='View', klass='iss')
746 self.db.security.addPermissionToRole('Project', p)
747
748 department = self.instance.backend.Class(self.db, "department",
749 name=hyperdb.String())
750 status = self.instance.backend.Class(self.db, "stat",
751 name=hyperdb.String())
752 issue = self.instance.backend.Class(self.db, "iss",
753 title=hyperdb.String(), status=hyperdb.Link('stat'),
754 department=hyperdb.Link('department'))
755
756 d1 = department.create(name='d1')
757 d2 = department.create(name='d2')
758 open = status.create(name='open')
759 closed = status.create(name='closed')
760 issue.create(title='i1', status=open, department=d2)
761 issue.create(title='i2', status=open, department=d1)
762 issue.create(title='i2', status=closed, department=d1)
763
764 chef = self.db.user.lookup('Chef')
765 mary = self.db.user.lookup('mary')
766 self.db.user.set(chef, roles = 'User, Project')
767
768 perm = self.db.security.hasPermission
769 search = self.db.security.hasSearchPermission
770 self.assert_(perm('View', chef, 'iss', 'department', '1'))
771 self.assert_(perm('View', chef, 'iss', 'department', '2'))
772 self.assert_(perm('View', chef, 'iss', 'department', '3'))
773 self.assert_(search(chef, 'iss', 'department'))
774
775 self.assert_(not perm('View', mary, 'iss', 'department'))
776 self.assert_(perm('View', mary, 'iss', 'status'))
777 # Conditionally allow view of whole iss (check is False here,
778 # this might check for department owner in the real world)
779 p = self.db.security.addPermission(name='View', klass='iss',
780 check=lambda x,y,z: False)
781 self.db.security.addPermissionToRole('User', p)
782 self.assert_(perm('View', mary, 'iss', 'department'))
783 self.assert_(not perm('View', mary, 'iss', 'department', '1'))
784 self.assert_(not search(mary, 'iss', 'department'))
785
786 self.assert_(perm('View', mary, 'iss', 'status'))
787 self.assert_(not search(mary, 'iss', 'status'))
788 # Allow user to search for iss.status
789 p = self.db.security.addPermission(name='Search', klass='iss',
790 properties=("status",))
791 self.db.security.addPermissionToRole('User', p)
792 self.assert_(search(mary, 'iss', 'status'))
793
794 dep = {'@action':'search','columns':'id','@filter':'department',
795 'department':'1'}
796 stat = {'@action':'search','columns':'id','@filter':'status',
797 'status':'1'}
798 depsort = {'@action':'search','columns':'id','@sort':'department'}
799 depgrp = {'@action':'search','columns':'id','@group':'department'}
800
801 # Filter on department ignored for role 'User':
802 cl = self._make_client(dep, classname='iss', nodeid=None, userid=mary,
803 template='index')
804 h = HTMLRequest(cl)
805 self.assertEqual([x.id for x in h.batch()],['1', '2', '3'])
806 # Filter on department works for role 'Project':
807 cl = self._make_client(dep, classname='iss', nodeid=None, userid=chef,
808 template='index')
809 h = HTMLRequest(cl)
810 self.assertEqual([x.id for x in h.batch()],['2', '3'])
811 # Filter on status works for all:
812 cl = self._make_client(stat, classname='iss', nodeid=None, userid=mary,
813 template='index')
814 h = HTMLRequest(cl)
815 self.assertEqual([x.id for x in h.batch()],['1', '2'])
816 cl = self._make_client(stat, classname='iss', nodeid=None, userid=chef,
817 template='index')
818 h = HTMLRequest(cl)
819 self.assertEqual([x.id for x in h.batch()],['1', '2'])
820 # Sorting and grouping for class Project works:
821 cl = self._make_client(depsort, classname='iss', nodeid=None,
822 userid=chef, template='index')
823 h = HTMLRequest(cl)
824 self.assertEqual([x.id for x in h.batch()],['2', '3', '1'])
825 cl = self._make_client(depgrp, classname='iss', nodeid=None,
826 userid=chef, template='index')
827 h = HTMLRequest(cl)
828 self.assertEqual([x.id for x in h.batch()],['2', '3', '1'])
829 # Sorting and grouping for class User fails:
830 cl = self._make_client(depsort, classname='iss', nodeid=None,
831 userid=mary, template='index')
832 h = HTMLRequest(cl)
833 self.assertEqual([x.id for x in h.batch()],['1', '2', '3'])
834 cl = self._make_client(depgrp, classname='iss', nodeid=None,
835 userid=mary, template='index')
836 h = HTMLRequest(cl)
837 self.assertEqual([x.id for x in h.batch()],['1', '2', '3'])
838
727 def testRoles(self): 839 def testRoles(self):
728 cl = self._make_client({}) 840 cl = self._make_client({})
729 self.db.user.set('1', roles='aDmin, uSer') 841 self.db.user.set('1', roles='aDmin, uSer')
730 item = HTMLItem(cl, 'user', '1') 842 item = HTMLItem(cl, 'user', '1')
731 self.assert_(item.hasRole('Admin')) 843 self.assert_(item.hasRole('Admin'))

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