Coverage for src/lexigram/web/security/context.py: 65%
26 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 04:37 +0800
1"""Security context for storing authentication state."""
3from __future__ import annotations
5from dataclasses import dataclass, field
6from typing import TYPE_CHECKING, Any
8if TYPE_CHECKING:
9 from starlette.requests import Request
12@dataclass
13class SecurityContext:
14 """
15 Security context containing authentication and authorization info.
17 Stored in request.state.security for access in guards and handlers.
18 """
20 user: Any | None = None
21 roles: list[str] = field(default_factory=list)
22 permissions: list[str] = field(default_factory=list)
23 metadata: dict[str, Any] = field(default_factory=dict)
25 @property
26 def is_authenticated(self) -> bool:
27 """Check if user is authenticated."""
28 return self.user is not None
30 def has_role(self, role: str) -> bool:
31 """Check if user has specific role."""
32 return role in self.roles
34 def has_permission(self, permission: str) -> bool:
35 """Check if user has specific permission."""
36 return permission in self.permissions
38 def has_any_role(self, *roles: str) -> bool:
39 """Check if user has any of the specified roles."""
40 return any(role in self.roles for role in roles)
42 def has_all_roles(self, *roles: str) -> bool:
43 """Check if user has all of the specified roles."""
44 return all(role in self.roles for role in roles)
47def get_security_context(request: Request) -> SecurityContext:
48 """
49 Get or create security context from request state.
51 Args:
52 request: The HTTP request
54 Returns:
55 Security context instance
56 """
57 if not hasattr(request.state, "security"):
58 request.state.security = SecurityContext()
60 ctx: SecurityContext = request.state.security
61 return ctx
64__all__ = ["SecurityContext", "get_security_context"]