angee.resources.grants
Materialize declarative grant fixtures into REBAC relationship tuples.
A kind = "grants" resource entry (entries.GRANT_KIND) carries flat {resource, relation, subject} rows in resource <- relation <- subject direction. This module resolves each row into the REBAC-owned grant tuple shape (:class:rebac.RelationshipTuple) and commits it through :func:rebac.write_relationships, which upserts by the natural tuple key — so a re-load never duplicates a grant (idempotent, caveat-free for v1).
Reference resolution composes the two owners, never re-deriving either:
- a literal REBAC ref — a const anchor / role membership / typed wildcard — is written with a slash-form type token (
<ns>/type:<id>[`relation`], e.g.angee/role:admin,products/role:products_manager#member,auth/user:*) and is parsed verbatim by :mod:rebac.types; - a row xref — anything else (the loader's
<addon>.<xref>form, e.g.iam.alice) — resolves through the resource ledger (:func:angee.resources.widgets.resolve_xref) to the loaded row, whose own REBAC identity (:func:rebac.to_object_ref/ :func:rebac.to_subject_ref) gives the tuple side — so the row owns its resource type, never a fixture prefix. On the resource side a grant honors the row's IS-A: an MTI child materializes one tuple per REBAC identity it carries — its own type plus each REBAC-registered concrete parent it IS-A (OrganizationIS-AParty) — so the grant reaches the row through a foreign key typed to any ancestor. An ancestor with no REBAC type is skipped; the row's own type still fails fast; - the bare
*subject is the public wildcard (the anonymous subject).
materialize_grant_groups
def materialize_grant_groups(
groups: Iterable[GrantGroup], *, ledger_model: type[models.Model],
addon_aliases: Mapping[str, str]) -> tuple[int, int]Write every group's grant tuples idempotently; return (created, skipped).
Rows are deduplicated by their natural tuple key before writing, so a repeat within one load — or a re-load of an unchanged fixture — is a no-op.