Group -> permission rollup helper.
Chirp's authorization gate (enforce_auth())
resolves against a flatuser.permissionsfrozenset and assumes something
upstream already flattened the user's group memberships into that set. This
module ships the flattener: a pure-stdlibresolve_permissions() that the
app calls inside its ownload_user so the result lands on user.permissions
and feeds the existing exact-match gate unchanged.
It is a primitive the app wires by hand — there is no DB-backed Group/User model here. Persistence stays the app's choice; this honors the BYO-user Protocol and the no-ORM stance.
Usage insideload_user::
from chirp.security import resolve_permissions
def load_user(user_id: str) -> User | None:
record = db.get_user(user_id)
if record is None:
return None
perms = resolve_permissions(
[group.permissions for group in record.groups],
base=frozenset(record.direct_permissions),
)
return User(id=record.id, permissions=perms)
Each group blob may be either:
- an
Iterable[str]of already-flat permission names (e.g.{"billing.read", "billing.write"}), passed straight through, or - a nested truthy-leaf
Mapping(e.g.{"billing": {"read": True, "write": False}}) flattened to dotted keys — only truthy leaves are emitted, so the example yields{"billing.read"}and never"billing.write".
Groups are OR-merged (most-permissive-wins = set union, never intersection):
a permission held by any group is held by the user. The result includes
base and is always a frozenset(immutable, thread-safe by construction).
security.resolve_permissions
| Name | Type | Default | Description |
|---|---|---|---|
type
|
|
— | |
qualified_name
|
|
— | |
element_type
|
|
— | |
description
|
|
— | |
source_file
|
|
— | |
line_number
|
|
— | |
is_autodoc
|
|
— | |
autodoc_element
|
|
— | |
_autodoc_template
|
|
— | |
_autodoc_url_path
|
|
— | |
_autodoc_page_type
|
|
— | |
title
|
|
— | |
doc_content_hash
|
|
— |
Symbols on this page
resolve_permissions
function
def resolve_permissions(group_blobs: Iterable[Mapping[str, Any] | Iterable[str]], *, base: frozenset[str] = frozenset()) -> frozenset[str]
OR-merge group permission blobs into a flatfrozenset[str].
The most-permissive-wins rollup an app'sload_userruns to turn a user's
group memberships into the flatuser.permissionsset that
enforce_auth() checks.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
group_blobs
|
Iterable[Mapping[str, Any] | Iterable[str]]
|
— | One blob per group the user belongs to. Each blob is either an ``Iterable[str]`` of already-flat permission names, or a nested truthy-leaf ``Mapping`` (e.g. ``{"billing": {"read": True}}``) flattened to dotted keys (``"billing.read"``). Only truthy leaves are emitted; a ``{"read": False}`` leaf grants nothing. |
base
|
frozenset[str]
|
frozenset()
|
Permissions granted regardless of group membership (e.g. a user's direct grants). Merged into the result via union. |
View source · /home/runner/work/chirp/chirp/site/../src/chirp/security/resolve_permissions.py:1