security.resolve_permissions

Page actions AI-ready formats and sharing
Open LLM text
Share with AI
Ask Claude Ask ChatGPT Ask Gemini Ask Copilot

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…

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:

  • anIterable[str]of already-flat permission names (e.g.{"billing.read", "billing.write"}), passed straight through, or
  • a nested truthy-leafMapping (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