navigator-auth 0.17.2__tar.gz → 0.17.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/PKG-INFO +1 -1
  2. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/auth.py +5 -6
  3. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/api.py +4 -0
  4. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/django.py +4 -0
  5. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/token.py +4 -0
  6. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/troc.py +4 -0
  7. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/decorators.py +193 -3
  8. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/recovery.py +5 -8
  9. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/version.py +1 -1
  10. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth.egg-info/PKG-INFO +1 -1
  11. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/CHANGES.rst +0 -0
  12. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/LICENSE +0 -0
  13. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/MANIFEST.in +0 -0
  14. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/Makefile +0 -0
  15. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/README.md +0 -0
  16. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/Makefile +0 -0
  17. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/api.rst +0 -0
  18. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/authors.rst +0 -0
  19. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/changelog.rst +0 -0
  20. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/changes.rst +0 -0
  21. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/code-of-conduct.rst +0 -0
  22. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/conf.py +0 -0
  23. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/config.rst +0 -0
  24. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/index.rst +0 -0
  25. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/make.bat +0 -0
  26. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/policies.py +0 -0
  27. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/requirements-dev.txt +0 -0
  28. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/requirements.txt +0 -0
  29. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/security.rst +0 -0
  30. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/docs/settings.rst +0 -0
  31. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/__init__.py +0 -0
  32. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/__init__.py +0 -0
  33. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/audit.py +0 -0
  34. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/context.py +0 -0
  35. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/decorators.py +0 -0
  36. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/errors.py +0 -0
  37. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/guardian.py +0 -0
  38. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/middleware.py +0 -0
  39. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/pdp.py +0 -0
  40. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/__init__.py +0 -0
  41. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/abstract.py +0 -0
  42. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/environment.py +0 -0
  43. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/evaluator.py +0 -0
  44. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/file.py +0 -0
  45. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/obj.py +0 -0
  46. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/policy.py +0 -0
  47. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/resource_policy.py +0 -0
  48. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policies/resources.py +0 -0
  49. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/policyhandler.py +0 -0
  50. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/storages/__init__.py +0 -0
  51. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/storages/abstract.py +0 -0
  52. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/storages/db.py +0 -0
  53. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/abac/storages/pg.py +0 -0
  54. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/authorizations/__init__.py +0 -0
  55. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/authorizations/abstract.py +0 -0
  56. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/authorizations/allow_hosts.py +0 -0
  57. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/authorizations/hosts.py +0 -0
  58. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/authorizations/useragent.py +0 -0
  59. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/__init__.py +0 -0
  60. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/abstract.py +0 -0
  61. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/adfs.py +0 -0
  62. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/attributes/__init__.py +0 -0
  63. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/attributes/abstract.py +0 -0
  64. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/attributes/internal.py +0 -0
  65. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/azure.py +0 -0
  66. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/basic.py +0 -0
  67. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/external.py +0 -0
  68. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/github.py +0 -0
  69. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/google.py +0 -0
  70. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/idp/__init__.py +0 -0
  71. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/idp/code.py +0 -0
  72. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/jwksutils.py +0 -0
  73. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/noauth.py +0 -0
  74. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth.py +0 -0
  75. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth2/__init__.py +0 -0
  76. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth2/backend.py +0 -0
  77. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth2/client_backend.py +0 -0
  78. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth2/code_backend.py +0 -0
  79. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth2/ddl.sql +0 -0
  80. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/oauth2/models.py +0 -0
  81. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/okta.py +0 -0
  82. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/backends/saml.py +0 -0
  83. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/conf.py +0 -0
  84. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/exceptions.c +0 -0
  85. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/exceptions.pxd +0 -0
  86. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/exceptions.pyx +0 -0
  87. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/__init__.py +0 -0
  88. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/groups.py +0 -0
  89. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/handler.py +0 -0
  90. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/model.py +0 -0
  91. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/partners.py +0 -0
  92. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/permissions.py +0 -0
  93. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/userattrs.py +0 -0
  94. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/users/__init__.py +0 -0
  95. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/users/passwd.py +0 -0
  96. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/users/session.py +0 -0
  97. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/handlers/users/user.py +0 -0
  98. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/identities.py +0 -0
  99. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/__init__.py +0 -0
  100. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/cipher.cpp +0 -0
  101. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/cipher.pyx +0 -0
  102. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/json.cpp +0 -0
  103. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/json.pyx +0 -0
  104. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/parser.cpp +0 -0
  105. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/libs/parser.pyx +0 -0
  106. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/__init__.py +0 -0
  107. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/abstract.py +0 -0
  108. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/apikey.py +0 -0
  109. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/django.py +0 -0
  110. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/jwt.py +0 -0
  111. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/security.py +0 -0
  112. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/token.py +0 -0
  113. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/middlewares/troc.py +0 -0
  114. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/models.py +0 -0
  115. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/py.typed +0 -0
  116. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/responses.py +0 -0
  117. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/storages/__init__.py +0 -0
  118. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/storages/abstract.py +0 -0
  119. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/storages/postgres.py +0 -0
  120. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/storages/redis.py +0 -0
  121. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/templates.py +0 -0
  122. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth/uv.py +0 -0
  123. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth.egg-info/SOURCES.txt +0 -0
  124. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth.egg-info/dependency_links.txt +0 -0
  125. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth.egg-info/requires.txt +0 -0
  126. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/navigator_auth.egg-info/top_level.txt +0 -0
  127. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/pyproject.toml +0 -0
  128. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/setup.cfg +0 -0
  129. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/setup.py +0 -0
  130. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/__init__.py +0 -0
  131. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/test_allowed.py +0 -0
  132. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/test_login.py +0 -0
  133. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/test_policy.py +0 -0
  134. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/test_policy_conditions.py +0 -0
  135. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/test_policy_evaluation.py +0 -0
  136. {navigator_auth-0.17.2 → navigator_auth-0.17.3}/tests/test_policy_object.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: navigator-auth
3
- Version: 0.17.2
3
+ Version: 0.17.3
4
4
  Summary: Navigator Auth is an Authentication/Authorization Toolkit for aiohttp.
5
5
  Author-email: Jesus Lara Gimenez <jesuslarag@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -700,7 +700,6 @@ class AuthHandler:
700
700
  # avoid authorization backend on OPTION method:
701
701
  if request.method == hdrs.METH_OPTIONS:
702
702
  return True
703
-
704
703
  # Check for explicit exclude list matches
705
704
  for pattern in exclude_list:
706
705
  if fnmatch.fnmatch(request.path, pattern):
@@ -749,19 +748,19 @@ class AuthHandler:
749
748
  """
750
749
  if await self.verify_exceptions(request):
751
750
  return await handler(request)
752
- logging.debug(":: AUTH MIDDLEWARE ::")
751
+ self.logger.debug(":: AUTH MIDDLEWARE ::")
753
752
  try:
754
753
  token = await self._idp.get_payload(request)
755
754
  _, payload = self._idp.decode_token(code=token)
756
755
  except Forbidden as err:
757
- logging.error(
756
+ self.logger.error(
758
757
  f"Auth Middleware: Access Denied: {err}"
759
758
  )
760
759
  raise self.Unauthorized(
761
760
  reason=err.message
762
761
  ) from err
763
762
  except AuthExpired as err:
764
- logging.error(
763
+ self.logger.error(
765
764
  f"Auth Middleware: Credentials expired: {err}"
766
765
  )
767
766
  raise self.Unauthorized(
@@ -778,7 +777,7 @@ class AuthHandler:
778
777
  try:
779
778
  session = await get_session(request, payload, new=False)
780
779
  except Exception as err:
781
- logging.error(str(err))
780
+ self.logger.error(str(err))
782
781
  if not session and AUTH_CREDENTIALS_REQUIRED is True:
783
782
  raise self.Unauthorized(
784
783
  reason="There is no Session or Authentication is missing"
@@ -787,7 +786,7 @@ class AuthHandler:
787
786
  request.user = await self.get_session_user(session)
788
787
  request["authenticated"] = True
789
788
  except Exception as ex: # pylint: disable=W0703
790
- logging.error(f"Missing User Object from Session: {ex}")
789
+ self.logger.error(f"Missing User Object from Session: {ex}")
791
790
  elif self.secure_cookies is True:
792
791
  session = await get_session(request, None, new=False)
793
792
  if not session and AUTH_CREDENTIALS_REQUIRED is True:
@@ -3,6 +3,7 @@
3
3
  Navigator Authentication using an API Token.
4
4
  description: Single API Token Authentication
5
5
  """
6
+ from aiohttp import hdrs
6
7
  from collections.abc import Callable, Awaitable
7
8
  import orjson
8
9
  from aiohttp import web
@@ -223,6 +224,9 @@ class APIKeyAuth(BaseAuthBackend):
223
224
  ) -> web.StreamResponse:
224
225
  request.user = None
225
226
  # avoid check system routes
227
+ # avoid authorization backend on OPTION method:
228
+ if request.method == hdrs.METH_OPTIONS:
229
+ return await handler(request)
226
230
  if await self.verify_exceptions(request):
227
231
  return await handler(request)
228
232
  try:
@@ -4,6 +4,7 @@ Navigator Authentication using Django Session Backend
4
4
  description: read the Django session from Redis Backend
5
5
  and decrypt, after that, a session will be created.
6
6
  """
7
+ from aiohttp import hdrs
7
8
  import base64
8
9
  import logging
9
10
  from collections.abc import Callable, Awaitable
@@ -226,6 +227,9 @@ class DjangoAuth(BaseAuthBackend):
226
227
  Basic Auth Middleware.
227
228
  Description: Basic Authentication for NoAuth, Basic, Token and Django.
228
229
  """
230
+ # avoid authorization backend on OPTION method:
231
+ if request.method == hdrs.METH_OPTIONS:
232
+ return await handler(request)
229
233
  # avoid check system routes
230
234
  if await self.verify_exceptions(request):
231
235
  return await handler(request)
@@ -3,6 +3,7 @@
3
3
  Navigator Authentication using an API Token for partners.
4
4
  description: Single API Token Authentication
5
5
  """
6
+ from aiohttp import hdrs
6
7
  from collections.abc import Callable, Awaitable
7
8
  import jwt
8
9
  from aiohttp import web
@@ -177,6 +178,9 @@ class TokenAuth(BaseAuthBackend):
177
178
  Token Auth Middleware.
178
179
  Description: Token Middleware.
179
180
  """
181
+ # avoid authorization backend on OPTION method:
182
+ if request.method == hdrs.METH_OPTIONS:
183
+ return await handler(request)
180
184
  # avoid check system routes
181
185
  if await self.verify_exceptions(request):
182
186
  return await handler(request)
@@ -2,6 +2,7 @@
2
2
 
3
3
  Troc Authentication using RNC algorithm.
4
4
  """
5
+ from aiohttp import hdrs
5
6
  from typing import Optional
6
7
  from collections.abc import Awaitable, Callable
7
8
  from aiohttp import web
@@ -220,6 +221,9 @@ class TrocToken(BaseAuthBackend):
220
221
  Partner Auth Middleware.
221
222
  Description: Basic Authentication for Partner Token Auth.
222
223
  """
224
+ # avoid authorization backend on OPTION method:
225
+ if request.method == hdrs.METH_OPTIONS:
226
+ return await handler(request)
223
227
  # avoid check system routes
224
228
  if await self.verify_exceptions(request):
225
229
  return await handler(request)
@@ -1,12 +1,12 @@
1
1
  from functools import wraps
2
2
  import inspect
3
- from typing import Any, TypeVar, Union
3
+ from typing import Any, TypeVar
4
4
  from collections.abc import Callable, Awaitable
5
5
  from aiohttp import web, hdrs
6
6
  from aiohttp.abc import AbstractView
7
7
  from navigator_session import get_session
8
8
  from .exceptions import AuthException
9
- from .conf import AUTH_SESSION_OBJECT, exclude_list
9
+ from .conf import AUTH_SESSION_OBJECT
10
10
 
11
11
 
12
12
  F = TypeVar("F", bound=Callable[..., Any])
@@ -45,7 +45,7 @@ def allow_anonymous(handler: F) -> F:
45
45
  is not required for this endpoint.
46
46
 
47
47
  Args:
48
- func: The handler function or class-based view to decorate.
48
+ handler: The handler function or class-based view to decorate.
49
49
 
50
50
  Returns:
51
51
  Callable: The decorated handler that allows anonymous access.
@@ -77,6 +77,9 @@ def user_session() -> Callable[[F], F]:
77
77
  @wraps(handler)
78
78
  async def _wrap(*args, **kwargs) -> web.StreamResponse:
79
79
  request = args[0] if isinstance(args[0], web.Request) else args[-1]
80
+ # avoid check on OPTION method:
81
+ if request.method == hdrs.METH_OPTIONS:
82
+ return await handler(*args, **kwargs)
80
83
  session = await get_session(request, new=False)
81
84
  try:
82
85
  user = session.decode("user")
@@ -95,6 +98,9 @@ def user_session() -> Callable[[F], F]:
95
98
  @wraps(method)
96
99
  async def wrapped_method(self, *args, **kwargs):
97
100
  request = self.request
101
+ # avoid check on OPTION method:
102
+ if request.method == hdrs.METH_OPTIONS:
103
+ return await method(self, *args, **kwargs)
98
104
  session = await get_session(request, new=False)
99
105
  try:
100
106
  user = session.decode("user")
@@ -138,6 +144,9 @@ def is_authenticated(content_type: str = "application/json") -> Callable[[F], F]
138
144
  request = args[-1]
139
145
  if request is None or not isinstance(request, web.Request):
140
146
  raise ValueError(f"web.Request was not found in arguments. {handler!s}")
147
+ # avoid check on OPTION method:
148
+ if request.method == hdrs.METH_OPTIONS:
149
+ return await handler(*args, **kwargs)
141
150
  if request.get("authenticated", False):
142
151
  return await handler(*args, **kwargs)
143
152
  else:
@@ -167,6 +176,9 @@ def is_authenticated(content_type: str = "application/json") -> Callable[[F], F]
167
176
  @wraps(method)
168
177
  async def wrapped_method(self, *args, **kwargs):
169
178
  request = self.request
179
+ # avoid check on OPTION method:
180
+ if request.method == hdrs.METH_OPTIONS:
181
+ return await method(self, *args, **kwargs)
170
182
  if request.get("authenticated", False):
171
183
  return await method(self, *args, **kwargs)
172
184
  app = request.app
@@ -209,6 +221,9 @@ def allowed_groups(groups: list, content_type: str = "application/json") -> Call
209
221
  raise ValueError(
210
222
  f"web.Request was not found in arguments. {handler!s}"
211
223
  )
224
+ # avoid check on OPTION method:
225
+ if request.method == hdrs.METH_OPTIONS:
226
+ return await handler(*args, **kwargs)
212
227
  if request.get("authenticated", False) is False:
213
228
  # check credentials:
214
229
  raise web.HTTPUnauthorized(
@@ -261,6 +276,9 @@ def allowed_programs(
261
276
  request = args[-1]
262
277
  if request is None:
263
278
  raise ValueError(f"web.Request was not found in arguments. {handler!s}")
279
+ # avoid check on OPTION method:
280
+ if request.method == hdrs.METH_OPTIONS:
281
+ return await handler(*args, **kwargs)
264
282
  if not request.get("authenticated", False):
265
283
  raise web.HTTPUnauthorized(
266
284
  reason=f"Access Denied to Handler {handler!s}",
@@ -296,6 +314,9 @@ def allowed_programs(
296
314
  request = self.request
297
315
  if request is None:
298
316
  raise ValueError(f"web.Request was not found in arguments. {method!s}")
317
+ # avoid check on OPTION method:
318
+ if request.method == hdrs.METH_OPTIONS:
319
+ return await method(self, *args, **kwargs)
299
320
  if not request.get("authenticated", False):
300
321
  raise web.HTTPUnauthorized(
301
322
  reason=f"Access Denied to Handler {method!s}",
@@ -350,6 +371,9 @@ def apikey_required(content_type: str = "application/json") -> Callable:
350
371
  request = args[-1]
351
372
  if request is None:
352
373
  raise ValueError(f"web.Request was not found in arguments. {handler!s}")
374
+ # avoid check on OPTION method:
375
+ if request.method == hdrs.METH_OPTIONS:
376
+ return await handler(*args, **kwargs)
353
377
  app = request.app
354
378
  try:
355
379
  auth = app["auth"]
@@ -390,6 +414,9 @@ def apikey_required(content_type: str = "application/json") -> Callable:
390
414
  request = self.request
391
415
  if request is None:
392
416
  raise ValueError(f"web.Request was not found in arguments. {method!s}")
417
+ # avoid check on OPTION method:
418
+ if request.method == hdrs.METH_OPTIONS:
419
+ return await method(self, *args, **kwargs)
393
420
  app = request.app
394
421
  try:
395
422
  auth = app["auth"]
@@ -450,6 +477,9 @@ def allowed_organizations(
450
477
  request = args[-1]
451
478
  if request is None:
452
479
  raise ValueError(f"web.Request was not found in arguments. {handler!s}")
480
+ # avoid check on OPTION method:
481
+ if request.method == hdrs.METH_OPTIONS:
482
+ return await handler(*args, **kwargs)
453
483
  if not request.get("authenticated", False):
454
484
  raise web.HTTPUnauthorized(
455
485
  reason=f"Access Denied to Handler {handler!s}",
@@ -481,6 +511,9 @@ def allowed_organizations(
481
511
  request = self.request
482
512
  if request is None:
483
513
  raise ValueError(f"web.Request was not found in arguments. {method!s}")
514
+ # avoid check on OPTION method:
515
+ if request.method == hdrs.METH_OPTIONS:
516
+ return await method(self, *args, **kwargs)
484
517
  if not request.get("authenticated", False):
485
518
  raise web.HTTPUnauthorized(
486
519
  reason=f"Access Denied to Handler {method!s}",
@@ -517,3 +550,160 @@ def allowed_organizations(
517
550
  return _wrap_function(handler)
518
551
 
519
552
  return _wrapper
553
+
554
+
555
+ def is_restricted(
556
+ users: list = None, groups: list = None, content_type: str = "application/json"
557
+ ) -> Callable:
558
+ """
559
+ Restrict access to specific Users or Groups.
560
+ Logic:
561
+ - If 'users' is provided, current user MUST be in the list.
562
+ - If 'groups' is provided, current user MUST have at least one group in the list.
563
+ - If both are provided, BOTH conditions must be met (Intersection).
564
+ """
565
+ def _wrap_function(handler):
566
+ @wraps(handler)
567
+ async def _wrapped(*args, **kwargs) -> web.StreamResponse:
568
+ # For function-based handlers, assume request is the last argument.
569
+ request = args[-1]
570
+ if request is None:
571
+ raise ValueError(f"web.Request was not found in arguments. {handler!s}")
572
+ # check on OPTION method:
573
+ if request.method == hdrs.METH_OPTIONS:
574
+ return await handler(*args, **kwargs)
575
+ if not request.get("authenticated", False):
576
+ raise web.HTTPUnauthorized(
577
+ reason=f"Access Denied to Handler {handler!s}",
578
+ headers={
579
+ hdrs.CONTENT_TYPE: content_type,
580
+ hdrs.CONNECTION: "keep-alive",
581
+ },
582
+ )
583
+ # 1. Get Session
584
+ session = await get_session(request)
585
+ # 2. Extract User Info
586
+ username = None
587
+ user_groups = set()
588
+ try:
589
+ # Try getting from cached session dict
590
+ userinfo = session[AUTH_SESSION_OBJECT]
591
+ username = userinfo.get("username")
592
+ if "groups" in userinfo:
593
+ user_groups = set(userinfo["groups"])
594
+ except KeyError:
595
+ # Fallback to decoding the User object
596
+ try:
597
+ user = session.decode("user")
598
+ username = getattr(user, "username", None)
599
+ if hasattr(user, "groups"):
600
+ for g in user.groups:
601
+ if hasattr(g, "group"):
602
+ user_groups.add(g.group)
603
+ elif hasattr(g, "group_name"):
604
+ user_groups.add(g.group_name)
605
+ except (AttributeError, TypeError, RuntimeError):
606
+ pass
607
+ # 3. Check Constraints
608
+ # User Check
609
+ if users is not None:
610
+ if not username or username not in users:
611
+ raise web.HTTPUnauthorized(
612
+ reason="Access Denied",
613
+ headers={
614
+ hdrs.CONTENT_TYPE: content_type,
615
+ hdrs.CONNECTION: "keep-alive",
616
+ },
617
+ )
618
+ # Group Check
619
+ if groups is not None:
620
+ # user_groups must have intersection with allowed groups
621
+ if user_groups.isdisjoint(groups):
622
+ raise web.HTTPUnauthorized(
623
+ reason="Access Denied",
624
+ headers={
625
+ hdrs.CONTENT_TYPE: content_type,
626
+ hdrs.CONNECTION: "keep-alive",
627
+ },
628
+ )
629
+ return await handler(*args, **kwargs)
630
+ return _wrapped
631
+
632
+ def _wrap_method(method):
633
+ @wraps(method)
634
+ async def _wrapped(self, *args, **kwargs) -> web.StreamResponse:
635
+ request = self.request
636
+ if request is None:
637
+ raise ValueError(f"web.Request was not found in arguments. {method!s}")
638
+ # check on OPTION method:
639
+ if request.method == hdrs.METH_OPTIONS:
640
+ return await method(self, *args, **kwargs)
641
+ if not request.get("authenticated", False):
642
+ raise web.HTTPUnauthorized(
643
+ reason=f"Access Denied to Handler {method!s}",
644
+ headers={
645
+ hdrs.CONTENT_TYPE: content_type,
646
+ hdrs.CONNECTION: "keep-alive",
647
+ },
648
+ )
649
+ # 1. Get Session
650
+ session = await get_session(request)
651
+ # 2. Extract User Info
652
+ username = None
653
+ user_groups = set()
654
+ try:
655
+ # Try getting from cached session dict
656
+ userinfo = session[AUTH_SESSION_OBJECT]
657
+ username = userinfo.get("username")
658
+ if "groups" in userinfo:
659
+ user_groups = set(userinfo["groups"])
660
+ except KeyError:
661
+ # Fallback to decoding the User object
662
+ try:
663
+ user = session.decode("user")
664
+ username = getattr(user, "username", None)
665
+ if hasattr(user, "groups"):
666
+ for g in user.groups:
667
+ if hasattr(g, "group"):
668
+ user_groups.add(g.group)
669
+ elif hasattr(g, "group_name"):
670
+ user_groups.add(g.group_name)
671
+ except (AttributeError, TypeError, RuntimeError):
672
+ pass
673
+ # 3. Check Constraints
674
+ # User Check
675
+ if users is not None:
676
+ if not username or username not in users:
677
+ raise web.HTTPUnauthorized(
678
+ reason="Access Denied",
679
+ headers={
680
+ hdrs.CONTENT_TYPE: content_type,
681
+ hdrs.CONNECTION: "keep-alive",
682
+ },
683
+ )
684
+ # Group Check
685
+ if groups is not None:
686
+ # user_groups must have intersection with allowed groups
687
+ if user_groups.isdisjoint(groups):
688
+ raise web.HTTPUnauthorized(
689
+ reason="Access Denied",
690
+ headers={
691
+ hdrs.CONTENT_TYPE: content_type,
692
+ hdrs.CONNECTION: "keep-alive",
693
+ },
694
+ )
695
+ return await method(self, *args, **kwargs)
696
+ return _wrapped
697
+
698
+ def _wrapper(handler: F):
699
+ # If the handler is a class-based view (subclass of AbstractView), wrap each HTTP method.
700
+ if inspect.isclass(handler) and issubclass(handler, AbstractView):
701
+ for method_name in hdrs.METH_ALL:
702
+ method = getattr(handler, method_name.lower(), None)
703
+ if method is not None and callable(method):
704
+ setattr(handler, method_name.lower(), _wrap_method(method))
705
+ return handler
706
+ else:
707
+ return _wrap_function(handler)
708
+
709
+ return _wrapper
@@ -1,9 +1,11 @@
1
1
  from datetime import datetime
2
2
  import secrets
3
+ import asyncio
3
4
  import logging
4
5
  import importlib
5
6
  from typing import Optional
6
7
  from aiohttp import web
8
+ from aiohttp_cors import CorsViewMixin
7
9
  try:
8
10
  import redis.asyncio as redis
9
11
  except ImportError:
@@ -11,13 +13,9 @@ except ImportError:
11
13
  from navconfig import config
12
14
  from navigator_auth.conf import (
13
15
  REDIS_URL,
14
- AUTH_USER_MODEL,
15
- AUTH_PWD_DIGEST,
16
- AUTH_PWD_ALGORITHM,
17
- AUTH_PWD_LENGTH
16
+ AUTH_USER_MODEL
18
17
  )
19
18
  from navigator_auth.libs.json import json_decoder
20
- from navigator_auth.exceptions import UserNotFound
21
19
  from navigator_auth.responses import JSONResponse
22
20
 
23
21
  class RecoveryTokenStorage:
@@ -47,7 +45,7 @@ class RecoveryTokenStorage:
47
45
  key = f"{self.prefix}{token}"
48
46
  await self.redis.delete(key)
49
47
 
50
- class ForgotPasswordHandler(web.View):
48
+ class ForgotPasswordHandler(web.View, CorsViewMixin):
51
49
  async def post(self):
52
50
  data = await self.request.json()
53
51
  email = data.get('email')
@@ -104,9 +102,8 @@ class ForgotPasswordHandler(web.View):
104
102
  logging.exception(f"Error in ForgotPasswordHandler: {e}")
105
103
  return web.HTTPInternalServerError(reason="Internal Server Error")
106
104
 
107
- import asyncio
108
105
 
109
- class ResetPasswordHandler(web.View):
106
+ class ResetPasswordHandler(web.View, CorsViewMixin):
110
107
  async def post(self):
111
108
  data = await self.request.json()
112
109
  token = data.get('token')
@@ -7,7 +7,7 @@ __title__ = "navigator_auth"
7
7
  __description__ = (
8
8
  "Navigator Auth is an Authentication/Authorization Toolkit for aiohttp."
9
9
  )
10
- __version__ = "0.17.2" # pragma: no cover
10
+ __version__ = "0.17.3" # pragma: no cover
11
11
  __author__ = "Jesus Lara"
12
12
  __author_email__ = "jesuslarag@gmail.com"
13
13
  __copyright__ = "Copyright (c) 2019-2025 Jesus Lara"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: navigator-auth
3
- Version: 0.17.2
3
+ Version: 0.17.3
4
4
  Summary: Navigator Auth is an Authentication/Authorization Toolkit for aiohttp.
5
5
  Author-email: Jesus Lara Gimenez <jesuslarag@gmail.com>
6
6
  License-Expression: Apache-2.0
File without changes