Mercurial > p > roundup > code
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')) |
