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

1 from nexios.decorators import RouteDecorator

2 import typing

3 from .exceptions import AuthenticationFailed

4 from nexios.http import Request, Response

5 from functools import wraps

6

7

8 class 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