aws-python-helper 1.0.3__tar.gz → 1.1.0__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.
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/PKG-INFO +3 -1
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/auth_validators.py +109 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/dispatcher.py +5 -5
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/fetcher.py +10 -1
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/PKG-INFO +3 -1
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/requires.txt +3 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/pyproject.toml +5 -1
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/README.md +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/auth_middleware.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/exceptions.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/handler.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/context/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/context/session.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/context/state_validator.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/database_proxy.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/external_database_proxy.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/external_mongo_manager.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/mongo_manager.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/fargate/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/fargate/executor.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/fargate/fetcher.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/fargate/handler.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/fargate/task_base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/invoker/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/invoker/api_client.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/invoker/exceptions.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/invoker/lambda_invoker.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/handler.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_index_sync/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_index_sync/base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_query/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_query/base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/repository/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/repository/base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/sns/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/sns/publisher.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/sqs/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/sqs/consumer_base.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/sqs/fetcher.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/sqs/handler.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/utils/__init__.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/utils/json_encoder.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/utils/response.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/utils/serializer.py +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/SOURCES.txt +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/dependency_links.txt +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/top_level.txt +0 -0
- {aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aws-python-helper
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: AWS Python Helper Framework
|
|
5
5
|
Author-email: Fabian Claros <neufabiae@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,6 +17,8 @@ Requires-Dist: motor==3.3.2
|
|
|
17
17
|
Requires-Dist: pymongo==4.6.1
|
|
18
18
|
Requires-Dist: pydantic>=2.0
|
|
19
19
|
Requires-Dist: httpx>=0.27.0
|
|
20
|
+
Provides-Extra: jwt
|
|
21
|
+
Requires-Dist: PyJWT[crypto]>=2.9.0; extra == "jwt"
|
|
20
22
|
|
|
21
23
|
# AWS Python Framework
|
|
22
24
|
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/auth_validators.py
RENAMED
|
@@ -141,3 +141,112 @@ class TokenValidator(AuthValidator):
|
|
|
141
141
|
'token_data': token_doc,
|
|
142
142
|
'is_bypass': False
|
|
143
143
|
}
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class JWTValidator(AuthValidator):
|
|
147
|
+
"""
|
|
148
|
+
Validates RS256 JWTs issued by constitution-core.
|
|
149
|
+
|
|
150
|
+
Stateless: the signature and the claims are enough, so no database is
|
|
151
|
+
touched. That is what lets any microservice authenticate a request without
|
|
152
|
+
calling the issuer.
|
|
153
|
+
|
|
154
|
+
Environment:
|
|
155
|
+
JWT_PUBLIC_KEY: RSA public key in PEM, raw or base64-encoded.
|
|
156
|
+
JWT_ISSUER: expected `iss`. Defaults to 'constitution-core'.
|
|
157
|
+
JWT_AUDIENCE: expected `aud`. Only verified when set.
|
|
158
|
+
AUTH_BYPASS_TOKEN: still honoured, same as TokenValidator.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
_public_key_cache: Optional[str] = None
|
|
162
|
+
|
|
163
|
+
@classmethod
|
|
164
|
+
def _public_key(cls) -> str:
|
|
165
|
+
"""The configured public key, decoded once per container."""
|
|
166
|
+
if cls._public_key_cache:
|
|
167
|
+
return cls._public_key_cache
|
|
168
|
+
|
|
169
|
+
raw = os.getenv('JWT_PUBLIC_KEY')
|
|
170
|
+
if not raw:
|
|
171
|
+
raise ValueError(
|
|
172
|
+
"JWT_PUBLIC_KEY environment variable not set. "
|
|
173
|
+
"Required when AUTH_STRATEGY=jwt."
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
key = raw.strip()
|
|
177
|
+
if not key.startswith('-----BEGIN'):
|
|
178
|
+
# PEMs are multi-line, so they travel base64-encoded in env vars.
|
|
179
|
+
import base64
|
|
180
|
+
key = base64.b64decode(key).decode('utf-8')
|
|
181
|
+
|
|
182
|
+
cls._public_key_cache = key
|
|
183
|
+
return key
|
|
184
|
+
|
|
185
|
+
async def validate_token(self, token: str) -> Dict[str, Any]:
|
|
186
|
+
bypass_token = os.getenv('AUTH_BYPASS_TOKEN')
|
|
187
|
+
if bypass_token and token == bypass_token:
|
|
188
|
+
logger.info("Bypass token used - skipping JWT validation")
|
|
189
|
+
return {
|
|
190
|
+
'user_id': 'bypass',
|
|
191
|
+
'user': {
|
|
192
|
+
'email': 'bypass@system',
|
|
193
|
+
'role': 'admin',
|
|
194
|
+
'name': 'Bypass User',
|
|
195
|
+
'_id': 'bypass'
|
|
196
|
+
},
|
|
197
|
+
'is_bypass': True,
|
|
198
|
+
'token_data': None
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
import jwt
|
|
203
|
+
except ImportError as exc:
|
|
204
|
+
raise RuntimeError(
|
|
205
|
+
"PyJWT is required for AUTH_STRATEGY=jwt. Install aws-python-helper[jwt]."
|
|
206
|
+
) from exc
|
|
207
|
+
|
|
208
|
+
audience = os.getenv('JWT_AUDIENCE')
|
|
209
|
+
try:
|
|
210
|
+
claims = jwt.decode(
|
|
211
|
+
token,
|
|
212
|
+
self._public_key(),
|
|
213
|
+
algorithms=['RS256'],
|
|
214
|
+
issuer=os.getenv('JWT_ISSUER', 'constitution-core'),
|
|
215
|
+
audience=audience,
|
|
216
|
+
options={'verify_aud': bool(audience)},
|
|
217
|
+
)
|
|
218
|
+
except jwt.ExpiredSignatureError:
|
|
219
|
+
logger.warning("JWT expired")
|
|
220
|
+
raise UnauthorizedError("Token has expired")
|
|
221
|
+
except jwt.InvalidTokenError as exc:
|
|
222
|
+
logger.warning("JWT rejected: %s", exc)
|
|
223
|
+
raise UnauthorizedError("Invalid token")
|
|
224
|
+
|
|
225
|
+
if not claims.get('sub'):
|
|
226
|
+
raise UnauthorizedError("Invalid token")
|
|
227
|
+
|
|
228
|
+
# Same shape TokenValidator returns, so nothing downstream changes.
|
|
229
|
+
return {
|
|
230
|
+
'user_id': str(claims['sub']),
|
|
231
|
+
'user': {
|
|
232
|
+
'_id': claims['sub'],
|
|
233
|
+
'email': claims.get('email'),
|
|
234
|
+
'name': claims.get('name', ''),
|
|
235
|
+
'role': claims.get('role', 'user'),
|
|
236
|
+
**(claims.get('extra') or {}),
|
|
237
|
+
},
|
|
238
|
+
'token_data': claims,
|
|
239
|
+
'is_bypass': False,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def get_auth_validator() -> AuthValidator:
|
|
244
|
+
"""The validator this service is configured to use.
|
|
245
|
+
|
|
246
|
+
AUTH_STRATEGY=jwt switches to stateless validation. Anything else keeps the
|
|
247
|
+
database lookup, so a service only migrates when its environment says so.
|
|
248
|
+
"""
|
|
249
|
+
strategy = (os.getenv('AUTH_STRATEGY') or 'db').strip().lower()
|
|
250
|
+
if strategy == 'jwt':
|
|
251
|
+
return JWTValidator()
|
|
252
|
+
return TokenValidator()
|
|
@@ -11,7 +11,7 @@ from .fetcher import Fetcher
|
|
|
11
11
|
from .base import API
|
|
12
12
|
from .exceptions import UnauthorizedError, ForbiddenError, AuthenticationError
|
|
13
13
|
from .auth_middleware import AuthMiddleware
|
|
14
|
-
from .auth_validators import
|
|
14
|
+
from .auth_validators import get_auth_validator
|
|
15
15
|
from ..context.session import get_session
|
|
16
16
|
from ..context.state_validator import StateValidator, InvalidStateError
|
|
17
17
|
|
|
@@ -240,8 +240,8 @@ class Dispatcher:
|
|
|
240
240
|
"""
|
|
241
241
|
Execute authentication middleware
|
|
242
242
|
|
|
243
|
-
|
|
244
|
-
|
|
243
|
+
The validator is chosen by AUTH_STRATEGY: stateless JWT, or the
|
|
244
|
+
database lookup used so far.
|
|
245
245
|
|
|
246
246
|
Args:
|
|
247
247
|
api: API instance to inject authentication data into
|
|
@@ -249,8 +249,8 @@ class Dispatcher:
|
|
|
249
249
|
Raises:
|
|
250
250
|
UnauthorizedError: If authentication fails
|
|
251
251
|
"""
|
|
252
|
-
#
|
|
253
|
-
validator =
|
|
252
|
+
# Strategy comes from configuration, per service
|
|
253
|
+
validator = get_auth_validator()
|
|
254
254
|
|
|
255
255
|
# Create middleware and authenticate
|
|
256
256
|
middleware = AuthMiddleware(validator)
|
|
@@ -91,7 +91,7 @@ class Fetcher:
|
|
|
91
91
|
|
|
92
92
|
def _looks_like_id(self, part: str) -> bool:
|
|
93
93
|
"""
|
|
94
|
-
Check if a URL part looks like an ID (numeric or UUID format)
|
|
94
|
+
Check if a URL part looks like an ID (numeric, ObjectId or UUID format)
|
|
95
95
|
|
|
96
96
|
Args:
|
|
97
97
|
part: URL part to check
|
|
@@ -105,6 +105,15 @@ class Fetcher:
|
|
|
105
105
|
# Check if it's numeric
|
|
106
106
|
if part.isdigit():
|
|
107
107
|
return True
|
|
108
|
+
|
|
109
|
+
# ObjectId de MongoDB: 24 caracteres hexadecimales. Sin esto, un
|
|
110
|
+
# PATCH /users/<oid> busca un directorio con el id como nombre.
|
|
111
|
+
if len(part) == 24:
|
|
112
|
+
try:
|
|
113
|
+
int(part, 16)
|
|
114
|
+
return True
|
|
115
|
+
except ValueError:
|
|
116
|
+
pass
|
|
108
117
|
|
|
109
118
|
# Check if it's a UUID format (8-4-4-4-12 hex digits)
|
|
110
119
|
if len(part) == 36 and part.count('-') == 4:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aws-python-helper
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: AWS Python Helper Framework
|
|
5
5
|
Author-email: Fabian Claros <neufabiae@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -17,6 +17,8 @@ Requires-Dist: motor==3.3.2
|
|
|
17
17
|
Requires-Dist: pymongo==4.6.1
|
|
18
18
|
Requires-Dist: pydantic>=2.0
|
|
19
19
|
Requires-Dist: httpx>=0.27.0
|
|
20
|
+
Provides-Extra: jwt
|
|
21
|
+
Requires-Dist: PyJWT[crypto]>=2.9.0; extra == "jwt"
|
|
20
22
|
|
|
21
23
|
# AWS Python Framework
|
|
22
24
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "aws-python-helper"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.1.0"
|
|
8
8
|
description = "AWS Python Helper Framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -31,6 +31,10 @@ keywords = [
|
|
|
31
31
|
"aws", "python", "framework", "helper", "mongodb", "sqs", "sns", "fargate", "lambda"
|
|
32
32
|
]
|
|
33
33
|
|
|
34
|
+
# Only needed by services running AUTH_STRATEGY=jwt.
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
jwt = ["PyJWT[crypto]>=2.9.0"]
|
|
37
|
+
|
|
34
38
|
[project.urls]
|
|
35
39
|
"Homepage" = "https://github.com/fabiae/aws-python-framework"
|
|
36
40
|
"Source Code" = "https://github.com/fabiae/aws-python-framework"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/api/auth_middleware.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/context/state_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/database_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/database/mongo_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/invoker/lambda_invoker.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/base.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/fetcher.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/lambda_standalone/handler.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_index_sync/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_index_sync/base.py
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/model_query/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper/repository/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{aws_python_helper-1.0.3 → aws_python_helper-1.1.0}/aws_python_helper.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|