aws-python-helper 1.6.0__tar.gz → 1.7.1__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.6.0 → aws_python_helper-1.7.1}/PKG-INFO +1 -1
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/base.py +28 -3
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/fetcher.py +29 -24
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/permissions/__init__.py +2 -1
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/permissions/catalog.py +51 -1
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/PKG-INFO +1 -1
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/pyproject.toml +1 -1
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/README.md +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/auth_middleware.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/auth_validators.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/dispatcher.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/exceptions.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/handler.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/context/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/context/session.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/context/state_validator.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/database/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/database/database_proxy.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/database/external_database_proxy.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/database/external_mongo_manager.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/database/mongo_manager.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/fargate/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/fargate/executor.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/fargate/fetcher.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/fargate/handler.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/fargate/task_base.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/invoker/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/invoker/api_client.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/invoker/exceptions.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/invoker/lambda_invoker.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/base.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/handler.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_index_sync/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_index_sync/base.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_query/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_query/base.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/repository/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/repository/audit.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/repository/base.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/sns/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/sns/publisher.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/sqs/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/sqs/consumer_base.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/sqs/fetcher.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/sqs/handler.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/utils/__init__.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/utils/json_encoder.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/utils/response.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/utils/serializer.py +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/SOURCES.txt +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/dependency_links.txt +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/requires.txt +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/top_level.txt +0 -0
- {aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/setup.cfg +0 -0
|
@@ -203,10 +203,14 @@ class API(ABC):
|
|
|
203
203
|
|
|
204
204
|
@property
|
|
205
205
|
def service_code(self) -> str:
|
|
206
|
-
"""Names this service inside a permission, as in `core:GET /users`.
|
|
207
|
-
import os
|
|
206
|
+
"""Names this service inside a permission, as in `core:GET /users`.
|
|
208
207
|
|
|
209
|
-
|
|
208
|
+
Resolved by the same function the catalog uses, so the name a permission
|
|
209
|
+
is stored under and the name it is demanded by cannot drift.
|
|
210
|
+
"""
|
|
211
|
+
from ..permissions import service_code
|
|
212
|
+
|
|
213
|
+
return service_code()
|
|
210
214
|
|
|
211
215
|
@property
|
|
212
216
|
def auth_data(self) -> Optional[Dict[str, Any]]:
|
|
@@ -288,6 +292,27 @@ class API(ABC):
|
|
|
288
292
|
'headers': self._response_headers
|
|
289
293
|
}
|
|
290
294
|
|
|
295
|
+
@property
|
|
296
|
+
def public(self) -> bool:
|
|
297
|
+
"""
|
|
298
|
+
Whether this endpoint answers without a token.
|
|
299
|
+
|
|
300
|
+
Declared here, next to the code, so the permission catalog can leave it
|
|
301
|
+
out: a permission for something nobody needs permission for is noise in
|
|
302
|
+
every role screen, and an administrator ticking it would be granting
|
|
303
|
+
nothing.
|
|
304
|
+
|
|
305
|
+
This does not make an endpoint public — terraform does, by leaving
|
|
306
|
+
`authorization` unset on its lambda. This says so, so the rest of the
|
|
307
|
+
framework can act on it.
|
|
308
|
+
|
|
309
|
+
class AuthLoginAPI(API):
|
|
310
|
+
@property
|
|
311
|
+
def public(self):
|
|
312
|
+
return True
|
|
313
|
+
"""
|
|
314
|
+
return False
|
|
315
|
+
|
|
291
316
|
@property
|
|
292
317
|
def schema(self):
|
|
293
318
|
"""
|
|
@@ -7,6 +7,34 @@ import importlib.util
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
from typing import List
|
|
9
9
|
|
|
10
|
+
def load_controller_class(file_path: str):
|
|
11
|
+
"""The API class a controller file defines.
|
|
12
|
+
|
|
13
|
+
Shared so the permission catalog asks the class the same questions the
|
|
14
|
+
dispatcher does — a second way of finding it would answer differently the
|
|
15
|
+
day one of them changes.
|
|
16
|
+
"""
|
|
17
|
+
spec = importlib.util.spec_from_file_location("api_module", file_path)
|
|
18
|
+
if not spec or not spec.loader:
|
|
19
|
+
raise ImportError(f"Could not load module spec from: {file_path}")
|
|
20
|
+
|
|
21
|
+
module = importlib.util.module_from_spec(spec)
|
|
22
|
+
spec.loader.exec_module(module)
|
|
23
|
+
|
|
24
|
+
for item_name in dir(module):
|
|
25
|
+
item = getattr(module, item_name)
|
|
26
|
+
if (isinstance(item, type) and
|
|
27
|
+
hasattr(item, 'process') and
|
|
28
|
+
item.__name__ not in ['API', 'ABC'] and
|
|
29
|
+
item.__module__ == module.__name__):
|
|
30
|
+
return item
|
|
31
|
+
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"No API class found in {file_path}\n"
|
|
34
|
+
f"Make sure your file exports a class that inherits from API"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
10
38
|
class Fetcher:
|
|
11
39
|
"""
|
|
12
40
|
Dynamically load API controllers
|
|
@@ -187,30 +215,7 @@ class Fetcher:
|
|
|
187
215
|
f"Expected file for endpoint '{self.endpoint}' with method '{self.method}'"
|
|
188
216
|
)
|
|
189
217
|
|
|
190
|
-
|
|
191
|
-
spec = importlib.util.spec_from_file_location("api_module", file_path)
|
|
192
|
-
if not spec or not spec.loader:
|
|
193
|
-
raise ImportError(f"Could not load module spec from: {file_path}")
|
|
194
|
-
|
|
195
|
-
module = importlib.util.module_from_spec(spec)
|
|
196
|
-
spec.loader.exec_module(module)
|
|
197
|
-
|
|
198
|
-
# Search for a class that inherits from API
|
|
199
|
-
controller_class = None
|
|
200
|
-
for item_name in dir(module):
|
|
201
|
-
item = getattr(module, item_name)
|
|
202
|
-
if (isinstance(item, type) and
|
|
203
|
-
hasattr(item, 'process') and
|
|
204
|
-
item.__name__ not in ['API', 'ABC'] and
|
|
205
|
-
item.__module__ == module.__name__):
|
|
206
|
-
controller_class = item
|
|
207
|
-
break
|
|
208
|
-
|
|
209
|
-
if not controller_class:
|
|
210
|
-
raise ValueError(
|
|
211
|
-
f"No API class found in {file_path}\n"
|
|
212
|
-
f"Make sure your file exports a class that inherits from API"
|
|
213
|
-
)
|
|
218
|
+
controller_class = load_controller_class(file_path)
|
|
214
219
|
|
|
215
220
|
# Cache the class (not the instance)
|
|
216
221
|
self._cache[file_path] = controller_class
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/permissions/__init__.py
RENAMED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from .catalog import ALL, allows, discover, required_for, service_wildcard
|
|
1
|
+
from .catalog import ALL, allows, discover, required_for, service_code, service_wildcard
|
|
2
2
|
|
|
3
3
|
__all__ = [
|
|
4
4
|
"ALL",
|
|
5
5
|
"allows",
|
|
6
6
|
"discover",
|
|
7
7
|
"required_for",
|
|
8
|
+
"service_code",
|
|
8
9
|
"service_wildcard",
|
|
9
10
|
]
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/permissions/catalog.py
RENAMED
|
@@ -12,10 +12,14 @@ otherwise silently stop matching in the other, and an access check that never
|
|
|
12
12
|
matches fails open or shut without anyone noticing.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
+
import logging
|
|
15
16
|
import os
|
|
17
|
+
import re
|
|
16
18
|
from typing import Dict, List
|
|
17
19
|
|
|
18
|
-
from ..api.fetcher import Fetcher
|
|
20
|
+
from ..api.fetcher import Fetcher, load_controller_class
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
19
23
|
|
|
20
24
|
# Cómo se llama el archivo según el método. `list` y `get` son ambos GET: uno
|
|
21
25
|
# responde la colección, el otro un elemento.
|
|
@@ -31,6 +35,19 @@ FILE_METHODS = {
|
|
|
31
35
|
ALL = "*"
|
|
32
36
|
|
|
33
37
|
|
|
38
|
+
def service_code() -> str:
|
|
39
|
+
"""The name this service takes inside a permission code, as in `core:GET /users`.
|
|
40
|
+
|
|
41
|
+
One function on purpose. Publishing the catalog and checking a permission are
|
|
42
|
+
two different places, and if each resolves the name its own way one stores
|
|
43
|
+
`core:GET /users` while the other demands `constitution-core:GET /users`.
|
|
44
|
+
Nothing reports that: every role with explicit permissions just gets 403 on
|
|
45
|
+
everything, and a role holding `*` keeps working, so it looks like a problem
|
|
46
|
+
with the role.
|
|
47
|
+
"""
|
|
48
|
+
return os.getenv("SERVICE_CODE") or os.getenv("NAMESPACE") or "service"
|
|
49
|
+
|
|
50
|
+
|
|
34
51
|
def discover(api_root: str, service: str) -> List[Dict[str, str]]:
|
|
35
52
|
"""Every endpoint this service answers, as permission records.
|
|
36
53
|
|
|
@@ -46,6 +63,10 @@ def discover(api_root: str, service: str) -> List[Dict[str, str]]:
|
|
|
46
63
|
if extension != ".py" or stem not in FILE_METHODS:
|
|
47
64
|
continue
|
|
48
65
|
|
|
66
|
+
controller = _controller(os.path.join(current, filename))
|
|
67
|
+
if controller and getattr(controller(), "public", False):
|
|
68
|
+
continue
|
|
69
|
+
|
|
49
70
|
relative = os.path.relpath(current, api_root)
|
|
50
71
|
parts = [] if relative == "." else relative.split(os.sep)
|
|
51
72
|
path = "/" + "/".join(parts)
|
|
@@ -62,11 +83,40 @@ def discover(api_root: str, service: str) -> List[Dict[str, str]]:
|
|
|
62
83
|
"method": method,
|
|
63
84
|
"path": path,
|
|
64
85
|
"scope": "item" if stem == "get" else "collection",
|
|
86
|
+
"name": _readable(controller),
|
|
65
87
|
})
|
|
66
88
|
|
|
67
89
|
return sorted(found, key=lambda item: item["code"])
|
|
68
90
|
|
|
69
91
|
|
|
92
|
+
def _controller(file_path: str):
|
|
93
|
+
"""The controller class, or None if it cannot be read.
|
|
94
|
+
|
|
95
|
+
None errs towards listing the endpoint. A permission that appears for a
|
|
96
|
+
public endpoint is noise; one that disappears for a protected endpoint
|
|
97
|
+
leaves the check with nothing to match and locks everybody out of it.
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
return load_controller_class(file_path)
|
|
101
|
+
except Exception as exc:
|
|
102
|
+
logger.warning("Could not read %s, listing it anyway: %s", file_path, exc)
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _readable(controller) -> str:
|
|
107
|
+
"""The class name as words: `UsersInviteAPI` -> `Users Invite`.
|
|
108
|
+
|
|
109
|
+
A panel showing raw paths makes people read routing to answer "what does
|
|
110
|
+
this let someone do". The class already names the thing; this only spaces it
|
|
111
|
+
out. Empty when the class could not be read, so the panel falls back to the
|
|
112
|
+
path rather than inventing a name.
|
|
113
|
+
"""
|
|
114
|
+
if controller is None:
|
|
115
|
+
return ""
|
|
116
|
+
name = re.sub(r"(API|Api)$", "", controller.__name__)
|
|
117
|
+
return re.sub(r"(?<=[a-z0-9])(?=[A-Z])", " ", name).strip()
|
|
118
|
+
|
|
119
|
+
|
|
70
120
|
def service_wildcard(service: str) -> str:
|
|
71
121
|
return f"{service}:*"
|
|
72
122
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/auth_middleware.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/api/auth_validators.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/context/state_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/database/database_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/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.6.0 → aws_python_helper-1.7.1}/aws_python_helper/invoker/lambda_invoker.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/base.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/fetcher.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/lambda_standalone/handler.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_index_sync/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_index_sync/base.py
RENAMED
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper/model_query/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/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
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.6.0 → aws_python_helper-1.7.1}/aws_python_helper.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|