Mercurial > p > roundup > code
comparison roundup/rest.py @ 6111:2a513a057691
Fix transitive property check in REST API
Checking Search permissin on a displayed property is wrong, we
now check *View* Permission on each component of a transitive prop.
| author | Ralf Schlatterbeck <rsc@runtux.com> |
|---|---|
| date | Fri, 28 Feb 2020 11:47:40 +0100 |
| parents | 53c4668a6600 |
| children | 1cb2375015f0 |
comparison
equal
deleted
inserted
replaced
| 6110:af81e7a4302f | 6111:2a513a057691 |
|---|---|
| 471 return prop | 471 return prop |
| 472 | 472 |
| 473 def transitive_props (self, class_name, props): | 473 def transitive_props (self, class_name, props): |
| 474 """Construct a list of transitive properties from the given | 474 """Construct a list of transitive properties from the given |
| 475 argument, and return it after permission check. Raises | 475 argument, and return it after permission check. Raises |
| 476 Unauthorised if no permission. Permission is checked by checking | 476 Unauthorised if no permission. Permission is checked by |
| 477 search-permission on the path (delimited by '.') excluding the | 477 checking View permission on each component. We do not allow to |
| 478 last component and then checking View permission on the last | 478 traverse multilinks -- the last item of an expansion *may* be a |
| 479 component. We do not allow to traverse multilinks -- the last | 479 multilink but in the middle of a transitive prop. |
| 480 item of an expansion *may* be a multilink but in the middle of a | |
| 481 transitive prop. | |
| 482 """ | 480 """ |
| 483 checked_props = [] | 481 checked_props = [] |
| 484 uid = self.db.getuid() | 482 uid = self.db.getuid() |
| 485 for p in props: | 483 for p in props: |
| 486 pn = p | 484 pn = p |
| 487 cn = class_name | 485 cn = class_name |
| 488 if '.' in p: | 486 if '.' in p: |
| 489 path, lc = p.rsplit('.', 1) | 487 prop = None |
| 490 if not self.db.security.hasSearchPermission(uid, class_name, p): | 488 for pn in p.split('.'): |
| 491 raise (Unauthorised | 489 # Tried to dereference a non-Link property |
| 492 ('User does not have permission on "%s.%s"' | 490 if cn is None: |
| 493 % (class_name, p))) | 491 raise AttributeError("Unknown: %s" % p) |
| 494 prev = prop = None | |
| 495 # This shouldn't raise any errors, otherwise the search | |
| 496 # permission check above would have failed | |
| 497 for pn in path.split('.'): | |
| 498 cls = self.db.getclass(cn) | 492 cls = self.db.getclass(cn) |
| 499 prop = cls.getprops(protected=True)[pn] | 493 # This raises a KeyError for unknown prop: |
| 494 try: | |
| 495 prop = cls.getprops(protected=True)[pn] | |
| 496 except KeyError: | |
| 497 raise AttributeError("Unknown: %s" % p) | |
| 500 if isinstance(prop, hyperdb.Multilink): | 498 if isinstance(prop, hyperdb.Multilink): |
| 501 raise UsageError( | 499 raise UsageError( |
| 502 'Multilink Traversal not allowed: %s' % p) | 500 'Multilink Traversal not allowed: %s' % p) |
| 503 cn = prop.classname | 501 # Now we have the classname in cn and the prop name in pn. |
| 504 cls = self.db.getclass(cn) | 502 if not self.db.security.hasPermission('View', uid, cn, pn): |
| 505 # Now we have the classname in cn and the prop name in pn. | 503 raise(Unauthorised |
| 506 if not self.db.security.hasPermission('View', uid, cn, pn): | 504 ('User does not have permission on "%s.%s"' |
| 507 raise(Unauthorised | 505 % (cn, pn))) |
| 508 ('User does not have permission on "%s.%s"' % (cn, pn))) | 506 try: |
| 507 cn = prop.classname | |
| 508 except AttributeError: | |
| 509 cn = None | |
| 509 checked_props.append (p) | 510 checked_props.append (p) |
| 510 return checked_props | 511 return checked_props |
| 511 | 512 |
| 512 def error_obj(self, status, msg, source=None): | 513 def error_obj(self, status, msg, source=None): |
| 513 """Return an error object""" | 514 """Return an error object""" |
