diff --git a/src/ttfrog/schema.py b/src/ttfrog/schema.py index 727197b..2fa7598 100644 --- a/src/ttfrog/schema.py +++ b/src/ttfrog/schema.py @@ -1,10 +1,9 @@ from __future__ import annotations from datetime import datetime -from functools import cache from typing import List -from grung.types import BackReference, Collection, DateTime, Field, Password, Pointer, Record, Timestamp +from grung.types import BackReference, Collection, DateTime, Dict, Field, Password, Pointer, Record, Timestamp from tinydb import where @@ -27,7 +26,7 @@ class Page(Record): Pointer("author", value_type=User), # The last user to touch the page. DateTime("created"), # When the page was created Timestamp("last_modified"), # The last time the page was modified. - Collection("acl", Permissions), # The access control list + Dict("acl"), ] # fmt: on @@ -79,18 +78,17 @@ class Page(Record): return page return None - def set_permissions(self, entity: Entity, permissions: List) -> Record: + def set_permissions(self, entity: Entity, permissions: List) -> str: from ttfrog import app app.check_state() - perms = app.db.save(Permissions(entity=entity, grants="".join(permissions))) - self.acl = list(set(self.acl + [perms])) + perms = "".join(permissions) + self.acl[entity.reference] = perms app.db.save(self) return perms - @cache - def get_acl_for_entity(self, entity) -> list: + def get_acl_for_entity(self, entity) -> (str, str | None): """ Search upward through the page hierarchy looking for one with an ACL that either has a grant for the entity we care about, or at least one group in which the entity is a member. @@ -101,36 +99,29 @@ class Page(Record): def find_acl(obj): if hasattr(obj, "acl"): - # examine each entry in the ACL and see if one refers to the entity we care about - group_grants = [] - for entry in obj.acl: - if type(entry) == str: - entry = app.db.Permissions.get(where("uid") == entry.split("::")[1], recurse=False) + if entity.reference in obj.acl: + return {entity.reference: obj.acl[entity.reference]} - # grants specific to the entity always take precedence - if entry.entity.uid == entity.uid: - return [entry] - - # keep track of grants to groups containing the entity - elif entity.reference in getattr(entry.entity, "members", []): - group_grants.append(entry) - - # if we found group grants, return them + group_grants = {} + for ref, grant in obj.acl.items(): + if ref.startswith("Group::"): + group = app.db.Group.get(where("uid") == ref.split("::")[1], recurse=False) + if entity.reference in group.members: + group_grants[ref] = grant if group_grants: return group_grants - # no ACL on this object, so check its parent, if there is one - if not hasattr(obj, "parent"): - return [] - return find_acl(obj.parent) + if hasattr(obj, "parent"): + return find_acl(obj.parent) + return {"": ""} return find_acl(self) class Entity(Page): def has_permission(self, record: Record, requested: str) -> bool | None: - for acl in record.get_acl_for_entity(self): - if requested in acl.grants: + for entity, grants in record.get_acl_for_entity(self).items(): + if requested in grants: return True return False diff --git a/test/test_db.py b/test/test_db.py index 5c782e0..ea96011 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -58,7 +58,6 @@ def test_permissions(app): # set to rw, no delete notes.set_permissions(players, [schema.Permissions.READ, schema.Permissions.WRITE]) notes = app.db.Page.get(doc_id=notes.doc_id) - assert players.can_read(notes) assert players.can_write(notes) assert not players.can_delete(notes)