Skip to content

Boolean access rules for django braces #320

@dhruvjain1512

Description

@dhruvjain1512

Add an access-control mixin that evaluates boolean expressions over a user's groups and permissions, so you don't have to stack multiple mixins. Example: group("editors") & (perm("blog.change_post") | perm("blog.add_post")). It must support object-level permissions and preserve AccessMixin behaviors.

Implement [AccessExpressionMixin] as a new class that integrates with the existing AccessMixin and a small expression evaluator. (Do not modify existing mixins)

Expression language requirements->
Token functions: group("name"), perm("app.codename"), any_group("g1", ...), all_groups("g1", ...)
Operators: ~ (NOT, highest precedence), & (AND), | (OR, lowest), and parentheses
Errors: raise ValueError for malformed input or unknown functions
Literals: String literals use double quotes (for example group("editors")). Accepting single quotes is optional and not required by tests.

Semantics->
group(name): true if and only if user.is_authenticated and the user is in the given group
perm(app.codename): true if and only if user.is_authenticated and user.has_perm("app.codename", obj)
any_group(...): true if user is in at least one listed group
all_groups(...): true if user is in every listed group
~expr, expr1 & expr2 (short-circuit on false), expr1 | expr2 (short-circuit on true)

Mixin behavior (public interface)
Class: AccessExpressionMixin(AccessMixin)
Required attribute: access_expression: str
Optional attributes:
allow_superuser: bool = True
allow_staff: bool = False
dispatch(self, request, *args, **kwargs):
1-> If allow_superuser and request.user.is_superuser, allow without evaluating the expression.
2-> If allow_staff and request.user.is_staff, allow without evaluating the expression.
3-> Otherwise, evaluate access_expression using the current request.user and, if defined, get_permission_object() as the permission object (obj) when calling user.has_perm.
4-> On allow: dispatch must return None so the view continues normal processing.
5-> On deny: return handle_no_permission(request), preserving AccessMixin behavior (honor raise_exception, login_url, redirect_field_name).

If the view defines get_permission_object(), pass its return value as the obj argument to user.has_perm(...); otherwise use None.

Test Assumptions:
braces/views/_access.py -> Defines AccessExpressionMixin
braces/views/init.py -> Re-exports AccessExpressionMixin
Public surface expected by tests
Class AccessExpressionMixin(AccessMixin) with attributes:
access_expression: str
allow_superuser: bool
allow_staff: bool
Method dispatch(self, request, *args, **kwargs) with continuation semantics:
Returns None on allow
Returns handle_no_permission(request) on deny
Note - The behavioral semantics (operators, precedence, short-circuits, anonymous behavior, error types, object-level permission usage) are verified only via the public interface above.
The parser and evaluator implementation details and module layout are flexible and not directly referenced by tests.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions