Skip to content

Permission API

Core classes

general_manager.permission.base_permission.BasePermission

Bases: ABC

Abstract base class defining CRUD permission checks for managers.

instance property

instance

Return the object against which permission checks are performed.

request_user property

request_user

Return the user being evaluated for permission checks.

__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 data are denied for the resolved request_user.

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, isAuthenticated&isMatchingKeyAccount).

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

Bases: AdditiveManagerPermission

Deprecated compatibility alias for AdditiveManagerPermission.

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.

data property

data

Return wrapped mutation data used by registered permission methods.

request_user property

request_user

Return the user whose permissions are being evaluated.

__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.

permission_data property

permission_data

Return the original mapping or manager instance payload.

manager property

manager

Return the manager class associated with the permission data.

__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 GeneralManager instance whose attributes provide values.

required
manager type[GeneralManagerData] | None

Manager class associated with a dictionary payload.

None

Raises:

Type Description
InvalidPermissionDataError

If permission_data is neither a dictionary nor a GeneralManager instance.

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.

__getattr__

__getattr__(name)

Return the named value from the wrapped permission payload.

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:

  • None when 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 filter and exclude keys 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 bool; False when the evaluator raises.

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.

record

record(event)

Persist or forward a permission audit event.

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 None resets the process-global logger to the built-in no-op implementation.

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:

  • None or missing to reset to the no-op logger.
  • An AuditLogger instance.
  • A dotted import path to an AuditLogger instance, 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 options value that is not a mapping.

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 str(user.pk) when a pk attribute exists, otherwise repr(user).

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

True when every reached permission method returns True;

bool

otherwise False.

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.