Permission API¶
Core classes¶
general_manager.permission.base_permission.BasePermission ¶
Bases: ABC
Abstract base class defining CRUD permission checks for managers.
__init__ ¶
__init__(instance, request_user)
Initialise the permission context for a specific manager and user.
request_user may be a Django user, AnonymousUser, SimpleLazyObject, or a primary-key-like value. It is normalized through :meth:get_user_with_id.
describe_permissions ¶
describe_permissions(action, attribute)
Return permission expressions associated with an action/attribute pair.
describe_operation_permissions abstractmethod ¶
describe_operation_permissions(action)
Return permission expressions associated with an action-level check.
check_operation_permission abstractmethod ¶
check_operation_permission(action)
Return whether an action without attribute payload is allowed.
can_read_instance ¶
can_read_instance()
Return whether the current user may see that the instance exists.
check_create_permission classmethod ¶
check_create_permission(data, manager, request_user)
Validate that the requesting user is allowed to perform the create operation.
Checks create permission for every key in data using the given manager. Empty payloads still evaluate the create-level permission gate once. If any attribute is not permitted, raises a PermissionCheckError that includes the evaluated user and a list of denial messages.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data | dict[str, object] | Mapping of attribute names to the values intended for creation. | required |
manager | type[GeneralManager] | Manager class that defines the model/schema against which permissions are checked. | required |
request_user | UserLike | object | User instance or user id (will be resolved to a user or AnonymousUser). | required |
Raises:
| Type | Description |
|---|---|
PermissionCheckError | If one or more attributes in |
check_update_permission classmethod ¶
check_update_permission(
data, old_manager_instance, request_user
)
Validate whether the request_user can perform the update operation.
Checks update permission for every key in data against the existing manager instance. Empty payloads still evaluate the update-level permission gate once.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data | dict[str, object] | Mapping of attribute names to new values to be applied. | required |
old_manager_instance | GeneralManager | Existing manager instance whose current state is used to evaluate update permissions. | required |
request_user | UserLike | object | User instance or user id; non-user values will be resolved to a User or AnonymousUser via get_user_with_id. | required |
Raises:
| Type | Description |
|---|---|
PermissionCheckError | Raised with a list of error messages when one or more fields are not permitted to be updated. |
check_delete_permission classmethod ¶
check_delete_permission(manager_instance, request_user)
Validate that the request_user has delete permission for every attribute of the given manager instance.
This resolves the provided request_user to a User/AnonymousUser, evaluates delete permission for each attribute present on manager_instance, collects any denied attributes into error messages, and raises PermissionCheckError if any permissions are denied.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manager_instance | GeneralManager | The manager object whose attributes will be checked for delete permission. | required |
request_user | UserLike | object | The user (or user id) to evaluate; non-user values will be resolved to AnonymousUser. | required |
Raises:
| Type | Description |
|---|---|
PermissionCheckError | If one or more attributes are not permitted for deletion by request_user. The exception carries the user and the list of denial messages. |
get_user_with_id staticmethod ¶
get_user_with_id(user)
Resolve a user identifier or user-like object to a Django User or AnonymousUser instance.
If the input is already an AbstractBaseUser, AnonymousUser, or configured user-model instance, it is returned unchanged. If the input is a primary key (or other value used to look up a User by id), the corresponding User is returned; if no such User exists, an AnonymousUser is returned.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
user | UserLike | object | A user object or a value to look up a User by primary key. | required |
Returns:
| Name | Type | Description |
|---|---|---|
UserLike | UserLike | The resolved User instance, or an AnonymousUser when no matching User is found. |
check_permission abstractmethod ¶
check_permission(action, attribute)
Determine whether the given action is permitted on the specified attribute.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action | Literal['create', 'read', 'update', 'delete'] | Operation being checked. | required |
attribute | str | Attribute name subject to the permission check. | required |
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | True when the action is allowed. |
get_permission_filter ¶
get_permission_filter()
Return the filter/exclude constraints associated with this permission.
get_read_permission_plan ¶
get_read_permission_plan()
Return read-query prefilters plus whether instance checks must still run.
validate_permission_string ¶
validate_permission_string(permission)
Validate complex permission expressions joined by & operators.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
permission | str | Permission expression (for example, | required |
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | True when every sub-permission evaluates to True for the current user. |
BasePermission(instance, request_user) stores the permission context and resolves request_user through get_user_with_id(). The request user may be a Django user, AnonymousUser, SimpleLazyObject, or a primary-key value. Lazy users are forced once; existing user objects are returned unchanged; lookup failures, invalid primary-key values, and unsupported values resolve to AnonymousUser.
check_create_permission(data, manager, request_user) and check_update_permission(data, old_manager_instance, request_user) accept dict[str, object] payloads. Each payload key is checked as an attribute; empty payloads still evaluate the operation-level gate once. Superusers bypass checks and, when audit logging is enabled, emit bypass audit events. Denials are logged with manager/action/user context, collected, and raised as one PermissionCheckError. check_delete_permission() iterates the manager's permission attributes and follows the same superuser, audit, denial logging, and denial aggregation behavior.
can_read_instance() returns True for superusers and for read plans that do not require a row-level check. Otherwise it infers candidate readable attributes from the manager _attributes mapping or dictionary permission payload keys and returns after the first allowed read attribute. If no candidate readable attribute can be inferred, it raises NotImplementedError.
PermissionCheckError(user, errors) renders the user as anonymous when no id is available, otherwise as id=<id>, and includes the collected denial messages.
_get_permission_filter(permission) resolves a permission expression to optional filter/exclude mappings with object-valued lookup values. Superusers and registered permission filters that return None both produce empty filter and exclude mappings. _get_permission_filter_info(permission) returns the same constraint plus a boolean indicating whether the permission was representable as a prefilter; None from the registered filter means False. Unknown permission names raise PermissionNotFoundError.
general_manager.permission.manager_based_permission.AdditiveManagerPermission ¶
Bases: _ConfiguredManagerPermission
Manager-based permissions where attribute rules add an extra gate.
general_manager.permission.manager_based_permission.OverrideManagerPermission ¶
Bases: _ConfiguredManagerPermission
Manager-based permissions where attribute rules replace the CRUD base rule.
general_manager.permission.manager_based_permission.ManagerBasedPermission ¶
general_manager.permission.mutation_permission.MutationPermission ¶
Evaluate custom GraphQL mutation permissions from class attributes.
Subclasses declare global mutation expressions in __mutate__ and may declare attribute-specific list[str] class attributes whose names match mutation payload keys. Global expressions are alternatives: any expression that evaluates to True grants the global mutation gate. For a payload field with attribute-specific permissions, both the global gate and that field's gate must pass; payload fields without a field-specific list use the global gate alone. Expressions inside one permission list are alternatives. Empty permission lists grant their gate, so __mutate__ = [] allows the global gate and field = [] allows that field gate. __mutate__ is resolved through normal class inheritance; omitting it on the concrete class denies only when no base class provides it. __mutate__ and field-specific values must be list instances whose items are all strings. Invalid __mutate__ values deny the global gate; invalid field values are ignored. Field-specific lists are collected only from the concrete class dictionary.
__init__ ¶
__init__(data, request_user)
Create a mutation permission context for normalized data and user.
data is the mutation argument mapping after GraphQL argument normalization. Manager-typed arguments are already manager instances. Dictionary payloads are wrapped in :class:PermissionDataManager, so custom registry permissions can use attribute access against field names. Direct construction expects an already resolved Django user or AnonymousUser; use :meth:check when an identifier should be resolved first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data | dict[str, object] | Mutation payload mapping field names to normalized values. | required |
request_user | UserLike | Django user or anonymous user attempting the mutation. | required |
__get_attribute_permissions ¶
__get_attribute_permissions()
Collect concrete-class list[str] field permission declarations.
describe_permissions ¶
describe_permissions(attribute)
Return declared expressions for diagnostics in evaluation order.
The result is __mutate__ expressions followed by expressions declared directly on attribute. Values are not deduplicated, and omitted __mutate__ contributes no expression even though it denies the global gate.
check classmethod ¶
check(data, request_user)
Validate that request_user may execute the mutation for data.
Non-user values are resolved through :meth:BasePermission.get_user_with_id; missing or invalid identifiers become AnonymousUser. Superusers bypass expression evaluation and produce granted audit events when audit logging is enabled. For non-superusers, every payload key is checked independently and all denied fields are collected before a single :class:PermissionCheckError is raised.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data | dict[str, object] | Mutation payload mapping field names to normalized values. | required |
request_user | object | User object, anonymous user, lazy user, or user identifier to evaluate. | required |
Raises:
| Type | Description |
|---|---|
PermissionCheckError | If one or more payload fields fail permission checks. |
check_permission ¶
check_permission(attribute)
Return whether the request user may mutate attribute.
The global __mutate__ result is cached only for attributes without field-specific permissions. Attribute-specific permissions are evaluated every time their field is checked and are combined with the global gate.
__check_specific_permission ¶
__check_specific_permission(permissions)
Return True when any expression in permissions evaluates true.
MutationPermission protects custom GraphQL mutations registered with graph_ql_mutation. Subclasses normally declare __mutate__: ClassVar[list[str]] with permission expressions that apply to every payload field. Any expression in that list may grant the global mutation gate. A subclass may also declare field-specific list[str] class attributes whose names match payload keys; those expressions add a second gate for that field. For matching fields, both the global gate and the field-specific gate must pass. Expressions within one list are alternatives. Payload fields with no field-specific list are governed by the global gate alone.
MutationPermission.check(data, request_user) receives the normalized mutation argument mapping. Manager-typed GraphQL arguments have already been converted to manager instances. request_user may be a Django user, AnonymousUser, lazy user, or user identifier; non-user values are resolved through the same helper as manager CRUD permissions, falling back to anonymous access when resolution fails. Directly constructing MutationPermission(data, request_user) expects an already resolved Django user or AnonymousUser.
Omitting __mutate__ denies by default. Setting __mutate__ = [] intentionally allows the global mutation gate, while field-specific lists still apply to their matching payload keys. __mutate__ itself is resolved through normal class inheritance, so a subclass may inherit a base class's global mutation gate. __mutate__ must be a list containing only strings; tuples, mixed-type lists, non-list values, and other sequences deny the global gate.
Empty field-specific lists allow that field gate. Only non-dunder attributes declared directly on the concrete permission class and whose value is a list containing only strings are collected as field-specific permission lists; inherited field lists, mixed-type lists, tuples, other sequences, constants, and non-list attributes are ignored. describe_permissions(attribute) returns declared expressions for diagnostics in evaluation order, with global expressions first and matching field expressions second, without deduplication. Superusers bypass expression evaluation. When audit logging is enabled, check() emits one mutation audit event per payload key and marks superuser events as bypassed. On denial, all failed fields are collected and one PermissionCheckError is raised.
Data access helpers¶
general_manager.permission.permission_data_manager.InvalidPermissionDataError ¶
Bases: TypeError
Raised when the permission data manager receives unsupported input.
__init__ ¶
__init__()
Build the error for unsupported permission payload types.
The public message is stable: permission_data must be either a dict or an instance of GeneralManager.
general_manager.permission.permission_data_manager.PermissionDataManager ¶
Bases: Generic[GeneralManagerData]
Adapter that exposes permission-related data as a unified interface.
__init__ ¶
__init__(permission_data, manager=None)
Wrap a permission payload and expose its fields through attributes.
permission_data accepts dict instances and dict subclasses, not arbitrary Mapping implementations. Dictionary payloads are used for create, update, and mutation checks. Attribute access returns dict.get(name), so missing keys resolve to None instead of raising AttributeError. manager records the manager class associated with a dictionary payload so delegated permission checks can resolve related manager values. manager=None is valid for dictionary payloads that do not need delegated manager resolution.
Manager instance payloads are used for read/delete checks. Attribute access delegates to getattr(instance, name) and propagates that lookup's result or exception. For instance payloads, manager is ignored and inferred from type(permission_data).
Wrapper attributes and properties take precedence over payload keys with the same name. For example, a dictionary key named "manager" does not shadow the :attr:manager property; access the original dictionary through :attr:permission_data when such keys must be read.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
permission_data | dict[str, object] | GeneralManagerData | Dictionary of field names to permission values or a | required |
manager | type[GeneralManagerData] | None | Manager class associated with a dictionary payload. | None |
Raises:
| Type | Description |
|---|---|
InvalidPermissionDataError | If |
for_update classmethod ¶
for_update(base_data, update_data)
Create a wrapper representing base_data with updates applied.
base_data must support dict(base_data). It is converted with that operation and then shallowly overlaid with update_data. Values from update_data win on key conflicts; nested dictionaries or other mutable values are not deep-merged. The returned wrapper stores type(base_data) as its manager and exposes only the merged final state through dictionary-style missing-key semantics. The original object remains available to callers outside this wrapper if they need a separate before/after comparison.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_data | GeneralManagerData | Existing manager instance whose iterable key/value data provides the base permission state. | required |
update_data | dict[str, object] | Field values to add or override for the permission check. | required |
Returns:
| Type | Description |
|---|---|
PermissionDataManager[GeneralManagerData] | Wrapper exposing the merged permission state. |
PermissionDataManager is the public wrapper used by create, update, delete, mutation, and delegated permission checks when the permission engine needs one attribute-access surface for either a payload dictionary or a manager instance.
Dictionary payloads must be dict instances or subclasses, not arbitrary Mapping objects. Missing attributes resolve to None because lookups use dict.get(...); manager=None is valid when delegated manager resolution is not needed. Wrapper properties win over same-named payload keys, so a key such as "manager" must be read from permission_data directly. For manager instance payloads, lookups delegate to getattr(instance, name) and therefore follow the manager's normal attribute behavior.
for_update(base_data, update_data) requires base_data to support dict(base_data), shallowly overlays update_data, and records type(base_data) as the associated manager class. The wrapper exposes only the merged final state; keep the original manager instance separately when a rule needs an explicit before/after comparison. Unsupported payload types raise InvalidPermissionDataError with the stable message permission_data must be either a dict or an instance of GeneralManager.
Registry and reusable checks¶
general_manager.permission.permission_checks.register_permission ¶
register_permission(name, *, permission_filter=None)
Register a permission expression keyword in the global registry.
The decorated function receives the object being checked, the resolved request user, and the colon-separated configuration values from permission strings such as "belongsToCustomer:customer". It must return True to grant access and False to deny access. Applying the decorator stores that function in permission_functions under name and returns the original function unchanged.
name is stored exactly as provided in the global registry. Permission expression parsers split strings on & and : without escaping, so colon-free names are the practical form for rules referenced from permission strings. Empty config segments are preserved by normal string splitting: "rule:" passes [""] and "rule::x" passes ["", "x"].
When permission_filter is provided, read-query paths call it with the same user/config pair to build Django-style {"filter": {...}} and/or {"exclude": {...}} constraints. Return None when a permission cannot be represented as a queryset prefilter and must be evaluated per instance. Registry entries always store a callable permission_filter; permissions registered without one receive a default callable returning None. Django queryset authorization applies filter kwargs before exclude kwargs. Search backends receive only the filter side as a prefilter and the final instance gate checks exclude constraints.
permission_functions is an ordinary process-local mutable dictionary. Direct mutation affects all later permission checks in the process; tests may snapshot and restore it, while application code should prefer this decorator. Permission methods and filters are called without wrapping their exceptions, so errors raised by custom callables propagate to the caller.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name | str | Identifier used before the first colon in permission expressions. | required |
permission_filter | permission_filter | None | Optional callable that returns queryset constraints corresponding to the permission. | None |
Returns:
| Type | Description |
|---|---|
Callable[[permission_method], permission_method] | Callable[[permission_method], permission_method]: Decorator that |
Callable[[permission_method], permission_method] | registers the decorated function and returns it unchanged. |
Raises:
| Type | Description |
|---|---|
ValueError | If applying the decorator would register a name already present in the global registry. |
general_manager.permission.permission_checks.permission_functions module-attribute ¶
permission_functions = {}
register_permission(name, *, permission_filter=None) returns a decorator for custom permission checks. Applying the decorator registers the function in the global permission_functions registry and returns the original function unchanged. Duplicate names raise ValueError when the decorator is applied.
Permission methods receive (instance, user, config) and return True to allow access or False to deny access. config is the list of colon-separated values after the permission name, so "belongsToCustomer:customer" passes ["customer"]. The low-level evaluator normalizes permission method results with bool(...), but custom methods should return real bool values.
name is stored exactly as the registry key. Permission expression parsing splits on & and : without escaping, so use colon-free names for permissions that must be referenced from permission strings. Empty config segments are preserved: "rule:" passes [""], and "rule::x" passes ["", "x"].
Permission filters receive (user, config) and return one of these shapes:
Nonewhen the rule cannot be represented as a queryset prefilter.{"filter": {"field": value}}for Django-style filter kwargs.{"exclude": {"field": value}}for Django-style exclude kwargs.- Both
filterandexcludekeys when a rule needs both constraints.
Registry entries always contain a callable permission_filter. When a permission is registered without one, GeneralManager stores a default callable that returns None.
For Django queryset authorization, GeneralManager applies returned constraints as queryset.filter(**filter_kwargs).exclude(**exclude_kwargs). Search backends use the filter side as a backend prefilter and rely on the final instance gate for exclude checks. Custom permission methods and filters are called without exception wrapping; exceptions from those callables propagate to the caller.
permission_functions is a normal process-local mutable dictionary. Direct mutation changes later permission checks in the current process. Tests may snapshot and restore it, but application code should prefer register_permission() so duplicate-name protection stays active.
Built-in registry names:
| Name | Config | Instance check | Query filter |
|---|---|---|---|
public | none | Allows every user, including anonymous and inactive users. | None |
matches | <field>:<value> | Allows when str(getattr(instance, field)) == value. | {"filter": {field: value}} |
isAdmin | none | Allows Django staff users, including superusers. | None |
isSelf | none | Allows when instance.creator == user. | {"filter": {"creator_id": user.id}} |
isAuthenticated | none | Allows authenticated users. | None |
isActive | none | Allows active users. | None |
hasPermission | <app_label.codename> | Delegates to user.has_perm(...). | None |
inGroup | <group name> | Allows users in the named Django group. | None |
relatedUserField | <field> | Allows when getattr(instance, field) == user. | {"filter": {f"{field}_id": user.id}} |
manyToManyContainsUser | <field> | Allows when the related manager contains the user. | {"filter": {f"{field}__id": user.id}} |
GraphQL permission capabilities¶
general_manager.permission.graphql_capabilities.object_capability ¶
object_capability(
name,
evaluator,
*,
batch_evaluator=None,
description=None
)
Declare a domain-specific GraphQL capability for one resolved object.
Use this helper when the capability is a business rule that does not map directly to a generated CRUD mutation or custom mutation permission. The evaluator is called with (instance, user) and should return a boolean. Provide batch_evaluator for list pages to avoid repeated per-row policy work, and description to document the generated GraphQL field.
general_manager.permission.graphql_capabilities.permission_capability ¶
permission_capability(
target,
action,
*,
name=None,
payload=None,
description=None
)
Declare a GraphQL capability backed by a manager Permission CRUD check.
The generated capability previews the same permission method used by the corresponding generated mutation: create delegates to check_create_permission, update to check_update_permission, and delete to check_delete_permission. Pass payload when create or update checks need proposed field values from the current object context. payload may be a mapping or a callable receiving (instance, user): instance is the object whose GraphQL capabilities field is being resolved, and user is the authenticated request user after the standard permission user lookup. The resolved mapping is passed unchanged to create and update permission checks; delete checks ignore it.
general_manager.permission.graphql_capabilities.mutation_capability ¶
mutation_capability(
mutation, *, name=None, payload=None, description=None
)
Declare a GraphQL capability backed by a custom mutation permission.
The capability calls the mutation's configured MutationPermission with a resolved payload and returns whether the current user would pass that check. Use it when a boolean field should preview a custom GraphQL action rather than generated manager create, update, or delete behavior. payload may be a mapping or a callable receiving (instance, user): instance is the object whose GraphQL capabilities field is being resolved, and the returned mapping is passed to the mutation permission's check method.
general_manager.permission.graphql_capabilities.CapabilityEvaluationContext ¶
Operation-scoped cache for GraphQL permission capability evaluation.
evaluate ¶
evaluate(declaration, instance)
Evaluate a capability and cache fail-closed results for the operation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
declaration | GraphQLPermissionCapability | Capability declaration to evaluate. | required |
instance | object | Resolved manager object or current-user provider object whose capability field is being resolved. | required |
Returns:
| Name | Type | Description |
|---|---|---|
bool | bool | The evaluator result coerced to |
warm ¶
warm(declarations, instances)
Warm cached capability values for a page of instances when possible.
Batch evaluators may return a sequence aligned with instances or a mapping keyed by instance object or instance identity. Exceptions are logged and cached as False for every missing instance.
general_manager.permission.graphql_capabilities.GraphQLPermissionCapability dataclass ¶
Boolean authorization hint exposed under a GraphQL capabilities object.
Capability fields are advisory frontend hints, not authorization gates. The evaluator receives the resolved object and request user, and GraphQL returns false if evaluation fails. Use description to explain the business action the field previews so schema introspection remains useful to client developers.
Audit logging¶
general_manager.permission.audit.AuditLogger ¶
Bases: Protocol
Protocol describing the expected behaviour of an audit logger implementation.
general_manager.permission.audit.FileAuditLogger ¶
Bases: _BufferedAuditLogger
Persist audit events as newline-delimited JSON records in a file.
The parent directory is created during initialization. Events are appended in the built-in serialized shape. A background worker is used by default; call flush() or close() during teardown to process queued events. After closing, later record() calls are ignored.
general_manager.permission.audit.DatabaseAuditLogger ¶
Bases: _BufferedAuditLogger
Store audit events inside a dedicated database table using Django connections.
The table is created on demand if it is missing. Non-SQLite connections use the background worker; SQLite writes synchronously to support in-memory test databases. Call flush() or close() during teardown when the worker is active. After closing, later record() calls are ignored.
general_manager.permission.audit.configure_audit_logger ¶
configure_audit_logger(logger)
Configure the audit logger used by permission checks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
logger | AuditLogger | None | Concrete logger implementation. Passing | required |
general_manager.permission.audit.configure_audit_logger_from_settings ¶
configure_audit_logger_from_settings(django_settings)
Configure the audit logger based on Django settings.
GENERAL_MANAGER["AUDIT_LOGGER"] takes precedence over a top-level AUDIT_LOGGER setting. Values may be:
Noneor missing to reset to the no-op logger.- An
AuditLoggerinstance. - A dotted import path to an
AuditLoggerinstance, class, or factory. - A zero-argument callable returning an
AuditLogger. - A mapping with
{"class": <path-or-callable>, "options": {...}}; options are passed as keyword arguments when constructing/calling the reference.
Import and constructor errors propagate. Resolved objects that do not satisfy AuditLogger disable logging by resetting to the no-op logger.
Raises:
| Type | Description |
|---|---|
InvalidAuditLoggerOptionsError | If a mapping configuration provides an |
general_manager.permission.audit.get_audit_logger ¶
get_audit_logger()
Return the currently configured audit logger.
The default and reset state is an internal no-op logger that satisfies the AuditLogger protocol.
general_manager.permission.audit.audit_logging_enabled ¶
audit_logging_enabled()
Return True when a non-no-op audit logger is currently configured.
general_manager.permission.audit.emit_permission_audit_event ¶
emit_permission_audit_event(event)
Forward an audit event to the configured logger when logging is enabled.
The disabled state is a no-op. When a logger is configured, this function calls logger.record(event) directly and lets logger exceptions propagate to the caller. Delivery ordering and threading are logger-specific; the built-in buffered loggers enqueue events in call order.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event | PermissionAuditEvent | Event payload to record. | required |
general_manager.permission.audit.PermissionAuditEvent dataclass ¶
Payload describing a permission evaluation outcome.
Attributes:
| Name | Type | Description |
|---|---|---|
action | AuditAction | CRUD or mutation action that was evaluated. |
attributes | tuple[str, ...] | Attribute names covered by this evaluation, in the order the permission check reported them. |
granted | bool | True when the action was permitted. |
user | object | User object involved in the evaluation. Built-in loggers store |
manager | str | None | Name of the manager class when applicable. |
permissions | tuple[str, ...] | Permission expressions that were considered. |
bypassed | bool | True when the decision relied on a superuser bypass. |
metadata | AuditMetadata | None | Optional JSON-compatible context. Built-in file and database loggers persist this mapping as-is after copying it to a plain dict. |
Utility functions¶
general_manager.permission.utils.validate_permission_string ¶
validate_permission_string(permission, data, request_user)
Evaluate a permission expression against the global registry.
Permission strings use simple splitting: & joins required fragments and : separates the registered permission name from configuration values. For example, "isAuthenticated&belongsToCustomer:customer" first calls the isAuthenticated method with an empty config list, then calls belongsToCustomer with ["customer"] if the first check passed. Empty fragments and empty config segments are preserved by normal string splitting, so "" tries to resolve an empty permission name, "rule:" passes [""], and "rule&&other" tries to resolve an empty permission name between the two ampersands.
Fragments are evaluated left-to-right and short-circuit on the first False result. A later unknown permission is therefore reported only when every earlier fragment grants access. Custom permission methods are expected to return bool and their result is normalized through bool(...). They are called without exception wrapping.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
permission | str | Permission expression to evaluate. | required |
data | PermissionSubject | Manager instance, manager class, or permission data wrapper passed unchanged to each permission method. | required |
request_user | AbstractBaseUser | AnonymousUser | Django user or anonymous user being checked. | required |
Returns:
| Type | Description |
|---|---|
bool |
|
bool | otherwise |
Raises:
| Type | Description |
|---|---|
PermissionNotFoundError | If a reached fragment references an unregistered permission name. |
validate_permission_string(permission, data, request_user) is the low-level AND evaluator used by permission classes and mutation checks. It splits permission on &, evaluates fragments left-to-right, and stops at the first fragment whose registered permission method returns False. A later unknown permission name is therefore raised only when every earlier fragment grants access.
Each reached fragment is split on :. The first part selects a key from permission_functions; the remaining parts are passed unchanged as the permission method's config list. There is no escaping: rule: passes [""], rule::x passes ["", "x"], rule&&other attempts to resolve an empty permission name between the two ampersands, and an entirely empty string also attempts to resolve the empty permission name. Unknown reached names raise PermissionNotFoundError, and exceptions from custom permission methods propagate unchanged. Permission method results are normalized through bool(...) before the AND expression continues.
general_manager.permission.utils.PermissionNotFoundError ¶
Bases: ValueError
Raised when a permission expression references an unregistered name.
__init__ ¶
__init__(permission)
Build the error for an unresolved permission expression.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
permission | str | Full permission fragment that failed lookup, including any colon-separated configuration values. | required |
PermissionNotFoundError.permission stores the full unresolved fragment, including any colon-separated config, and the exception message remains Permission <fragment> not found.