Coverage for src/lexigram/web/security/shortcuts.py: 0%

10 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-25 04:37 +0800

1"""Shorthand guard decorators for common authorization patterns. 

2 

3Provides concise aliases over :func:`~lexigram.web.security.guards.use_guards` 

4for the most frequent use cases. 

5 

6Example:: 

7 

8 from lexigram.web import guard, roles 

9 

10 @guard(AuthGuard) 

11 async def protected_route(self): ... 

12 

13 @roles("admin", "moderator") 

14 async def admin_route(self): ... 

15""" 

16 

17from __future__ import annotations 

18 

19from typing import Any 

20 

21from lexigram.web.security.guards import RoleGuard, use_guards 

22 

23 

24def guard(*guard_classes: type[Any] | Any) -> Any: 

25 """Concise alias for :func:`use_guards`. 

26 

27 Attaches one or more guards to a route handler or controller class. 

28 Guards are executed in order; the first failure returns a 403 response. 

29 

30 Args: 

31 *guard_classes: GuardProtocol classes or instances to apply. 

32 

33 Returns: 

34 Decorator that attaches guards to the target. 

35 

36 Example:: 

37 

38 @guard(AuthGuard) 

39 async def profile(self): ... 

40 

41 @guard(AuthGuard, PermissionGuard("users:write")) 

42 async def create_user(self): ... 

43 """ 

44 return use_guards(*guard_classes) 

45 

46 

47def roles(*role_names: str, authorizer: Any | None = None) -> Any: 

48 """Restrict a handler to users that have at least one of the given roles. 

49 

50 The ``authorizer`` dependency must be injected. 

51 

52 Args: 

53 *role_names: Required role names; user must hold at least one. 

54 authorizer: **Required** AuthorizerProtocol instance, typically resolved 

55 from the container during application startup. 

56 

57 Returns: 

58 Decorator that attaches a :class:`RoleGuard` to the target. 

59 

60 Example:: 

61 

62 authorizer = await container.resolve(AuthorizerProtocol) 

63 

64 @roles("admin", authorizer=authorizer) 

65 async def admin_only(self): ... 

66 

67 @roles("admin", "moderator", authorizer=authorizer) 

68 async def manage_content(self): ... 

69 """ 

70 if authorizer is None: 

71 raise ValueError( 

72 "authorizer parameter is required. Inject it from the container at startup." 

73 ) 

74 return use_guards(RoleGuard(*role_names, authorizer=authorizer)) 

75 

76 

77__all__ = ["guard", "roles"]