Coverage for nexios\auth\decorator.py: 83%

29 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-21 20:31 +0100

1from nexios.decorators import RouteDecorator 

2import typing 

3from .exceptions import AuthenticationFailed 

4from nexios.http import Request, Response 

5from functools import wraps 

6 

7 

8class auth(RouteDecorator): 

9 

10 def __init__(self, scopes: typing.Union[str, typing.List[str], None] = None): 

11 super().__init__() 

12 if isinstance(scopes, str): 

13 self.scopes = [scopes] 

14 elif scopes is None: 

15 self.scopes = [] # Allow authentication with any scope 

16 else: 

17 self.scopes = scopes 

18 

19 def __call__( 

20 self, handler: typing.Callable[..., typing.Awaitable[typing.Any]] 

21 ) -> typing.Any: 

22 

23 if getattr(handler, "_is_wrapped", False): 

24 return handler 

25 

26 @wraps(handler) # type: ignore 

27 async def wrapper( 

28 *args: typing.List[typing.Any], **kwargs: typing.Dict[str, typing.Any] 

29 ) -> typing.Any: 

30 request, response, *_ = kwargs.values() 

31 

32 if not isinstance(request, Request) or not isinstance(response, Response): 

33 raise TypeError("Expected request and response as the fist arguments") 

34 

35 if not request.scope.get("user"): 

36 raise AuthenticationFailed 

37 

38 scope = request.scope.get("auth") # type: ignore 

39 

40 if self.scopes and scope not in self.scopes: 

41 raise AuthenticationFailed 

42 

43 return await handler(*args, **kwargs) 

44 

45 wrapper._is_wrapped = True # type: ignore 

46 return wrapper