Skip to content

Commit 7d2f714

Browse files
committed
Move permission check code to hyperdb
Now the hyperdb has a method filter_with_permissions that performs the permission checks before (for filtering on sort/group/filterspec arguments) and after a call to hyperdb.filter. This also fixes possible problems on the unfiltered sort/group/filterspec arguments in roundup/rest.py and roundup/cgi/templating.py
1 parent 9f70adb commit 7d2f714

File tree

4 files changed

+62
-46
lines changed

4 files changed

+62
-46
lines changed

roundup/cgi/templating.py

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3421,14 +3421,13 @@ def base_javascript(self):
34213421
def batch(self, permission='View'):
34223422
""" Return a batch object for results from the "current search"
34233423
"""
3424-
sec = self._client.db.security
3425-
check = sec.hasPermission
3424+
check = self._client.db.security.hasPermission
34263425
userid = self._client.userid
34273426
if not check('Web Access', userid):
34283427
return Batch(self.client, [], self.pagesize, self.startwith,
34293428
classname=self.classname)
34303429

3431-
filterspec = self.filterspec
3430+
fspec = self.filterspec
34323431
sort = self.sort
34333432
group = self.group
34343433

@@ -3454,35 +3453,9 @@ def batch(self, permission='View'):
34543453
matches = None
34553454

34563455
# filter for visibility
3457-
item_ids = klass.filter(matches, filterspec, sort, group)
3458-
cn = self.classname
3459-
if check(permission, userid, cn, only_no_check = True):
3460-
allowed = item_ids
3461-
else:
3462-
# Note that is_filterable returns True if no permissions are
3463-
# found. This makes it fail early (with an empty allowed list)
3464-
# instead of running through all ids with an empty
3465-
# permission list.
3466-
if sec.is_filterable(permission, userid, cn):
3467-
new_ids = set(item_ids)
3468-
confirmed = set()
3469-
for perm in sec.filter_iter(permission, userid, cn):
3470-
fargs = perm.filter(self._client.db, userid, klass)
3471-
for farg in fargs:
3472-
farg.update(sort=sort, group=group, retired=False)
3473-
result = klass.filter(list(new_ids), **farg)
3474-
new_ids.difference_update(result)
3475-
confirmed.update(result)
3476-
# all allowed?
3477-
if not new_ids:
3478-
break
3479-
# all allowed?
3480-
if not new_ids:
3481-
break
3482-
allowed = list(confirmed)
3483-
else:
3484-
allowed = [id for id in item_ids
3485-
if check(permission, userid, cn, itemid=id)]
3456+
allowed = klass.filter_with_permissions(
3457+
matches, fspec, sort, group, permission=permission, userid=userid
3458+
)
34863459

34873460
# return the batch object, using IDs only
34883461
return Batch(self.client, allowed, self.pagesize, self.startwith,

roundup/hyperdb.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,56 @@ def filter(self, search_matches, filterspec, sort=[], group=[],
17961796
# anyway).
17971797
filter_iter = filter
17981798

1799+
def filter_with_permissions(self, search_matches, filterspec, sort=[],
1800+
group=[], retired=False, exact_match_spec={},
1801+
limit=None, offset=None,
1802+
permission='View', userid=None):
1803+
""" Do the same as filter but return only the items the user is
1804+
entitled to see, running the results through security checks.
1805+
The userid defaults to the current database user.
1806+
"""
1807+
if userid is None:
1808+
userid = self.db.getuid()
1809+
cn = self.classname
1810+
sec = self.db.security
1811+
filterspec = sec.filterFilterspec(userid, cn, filterspec)
1812+
sort = sec.filterSortspec(userid, cn, sort)
1813+
group = sec.filterSortspec(userid, cn, group)
1814+
item_ids = self.filter(search_matches, filterspec, sort, group,
1815+
retired, exact_match_spec, limit, offset)
1816+
check = sec.hasPermission
1817+
if check(permission, userid, cn, only_no_check = True):
1818+
allowed = item_ids
1819+
else:
1820+
# Note that is_filterable returns True if no permissions are
1821+
# found. This makes it fail early (with an empty allowed list)
1822+
# instead of running through all ids with an empty
1823+
# permission list.
1824+
if sec.is_filterable(permission, userid, cn):
1825+
new_ids = set(item_ids)
1826+
confirmed = set()
1827+
for perm in sec.filter_iter(permission, userid, cn):
1828+
fargs = perm.filter(self._client.db, userid, klass)
1829+
for farg in fargs:
1830+
farg.update(sort=[], group=[], retired=None)
1831+
result = klass.filter(list(new_ids), **farg)
1832+
new_ids.difference_update(result)
1833+
confirmed.update(result)
1834+
# all allowed?
1835+
if not new_ids:
1836+
break
1837+
# all allowed?
1838+
if not new_ids:
1839+
break
1840+
# Need to sort again in database
1841+
allowed = self.filter(confirmed, {}, sort=sort, group=group,
1842+
retired=None)
1843+
else: # Last resort: filter in python
1844+
allowed = [id for id in item_ids
1845+
if check(permission, userid, cn, itemid=id)]
1846+
return allowed
1847+
1848+
17991849
def count(self):
18001850
"""Get the number of nodes in this class.
18011851

roundup/rest.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ def get_collection(self, class_name, input):
953953
kw['limit'] = self.max_response_row_size
954954
if page['index'] is not None and page['index'] > 1:
955955
kw['offset'] = (page['index'] - 1) * page['size']
956-
obj_list = class_obj.filter(None, *l, **kw)
956+
obj_list = class_obj.filter_with_permissions(None, *l, **kw)
957957

958958
# Have we hit the max number of returned rows?
959959
# If so there may be more data that the client
@@ -973,10 +973,9 @@ def get_collection(self, class_name, input):
973973
result['collection'] = []
974974
for item_id in obj_list:
975975
r = {}
976-
if self.db.security.hasPermission(
977-
'View', uid, class_name, itemid=item_id, property='id',
978-
):
979-
r = {'id': item_id, 'link': class_path + item_id}
976+
# No need to check permission on id here, as we have only
977+
# security-checked results
978+
r = {'id': item_id, 'link': class_path + item_id}
980979
if display_props:
981980
# format_item does the permission checks
982981
r.update(self.format_item(class_obj.getnode(item_id),

roundup/xmlrpc.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,9 @@ def list(self, classname, propname=None):
9494
def filter(self, classname, search_matches, filterspec,
9595
sort=[], group=[]):
9696
cl = self.db.getclass(classname)
97-
uid = self.db.getuid()
98-
security = self.db.security
99-
filterspec = security.filterFilterspec(uid, classname, filterspec)
100-
sort = security.filterSortspec(uid, classname, sort)
101-
group = security.filterSortspec(uid, classname, group)
102-
result = cl.filter(search_matches, filterspec, sort=sort, group=group)
103-
check = security.hasPermission
104-
x = [id for id in result if check('View', uid, classname, itemid=id)]
105-
return x
97+
return cl.filter_with_permissions(
98+
search_matches, filterspec, sort=sort, group=group
99+
)
106100

107101
def lookup(self, classname, key):
108102
cl = self.db.getclass(classname)

0 commit comments

Comments
 (0)