aws-python-helper 1.1.0__tar.gz → 1.3.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.1.0 → aws_python_helper-1.3.0}/PKG-INFO +1 -1
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/base.py +22 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/dispatcher.py +45 -1
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_query/base.py +63 -4
- aws_python_helper-1.3.0/aws_python_helper/permissions/__init__.py +9 -0
- aws_python_helper-1.3.0/aws_python_helper/permissions/catalog.py +105 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/PKG-INFO +1 -1
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/SOURCES.txt +2 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/pyproject.toml +1 -1
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/README.md +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/auth_middleware.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/auth_validators.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/exceptions.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/fetcher.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/handler.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/context/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/context/session.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/context/state_validator.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/database/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/database/database_proxy.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/database/external_database_proxy.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/database/external_mongo_manager.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/database/mongo_manager.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/fargate/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/fargate/executor.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/fargate/fetcher.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/fargate/handler.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/fargate/task_base.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/invoker/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/invoker/api_client.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/invoker/exceptions.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/invoker/lambda_invoker.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/base.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/handler.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_index_sync/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_index_sync/base.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_query/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/repository/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/repository/base.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/sns/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/sns/publisher.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/sqs/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/sqs/consumer_base.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/sqs/fetcher.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/sqs/handler.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/utils/__init__.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/utils/json_encoder.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/utils/response.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/utils/serializer.py +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/dependency_links.txt +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/requires.txt +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/top_level.txt +0 -0
- {aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/setup.cfg +0 -0
|
@@ -186,6 +186,28 @@ class API(ABC):
|
|
|
186
186
|
"""
|
|
187
187
|
return self._is_authenticated
|
|
188
188
|
|
|
189
|
+
async def granted_permissions(self):
|
|
190
|
+
"""Which permissions the caller holds, or None when this API does not know.
|
|
191
|
+
|
|
192
|
+
Override it to turn on permission checking for a service. Where the list
|
|
193
|
+
comes from is the service's business: its own database, a map cached from
|
|
194
|
+
core, whatever fits.
|
|
195
|
+
|
|
196
|
+
Returning None with AUTHORIZATION=permission is refused, not waved
|
|
197
|
+
through. An endpoint that cannot say what its caller may do has to be a
|
|
198
|
+
closed door: the alternative is that forgetting to wire this up leaves it
|
|
199
|
+
open to anyone holding a token, which is the failure this whole mechanism
|
|
200
|
+
exists to prevent.
|
|
201
|
+
"""
|
|
202
|
+
return None
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def service_code(self) -> str:
|
|
206
|
+
"""Names this service inside a permission, as in `core:GET /users`."""
|
|
207
|
+
import os
|
|
208
|
+
|
|
209
|
+
return os.getenv('SERVICE_CODE', os.getenv('NAMESPACE', 'service'))
|
|
210
|
+
|
|
189
211
|
@property
|
|
190
212
|
def auth_data(self) -> Optional[Dict[str, Any]]:
|
|
191
213
|
"""
|
|
@@ -78,13 +78,20 @@ class Dispatcher:
|
|
|
78
78
|
|
|
79
79
|
# 2. Authorization based on mode
|
|
80
80
|
authorization = os.getenv('AUTHORIZATION', '').lower()
|
|
81
|
-
requires_user = authorization in ('user', 'full')
|
|
81
|
+
requires_user = authorization in ('user', 'full', 'permission')
|
|
82
82
|
requires_state = authorization in ('state', 'full')
|
|
83
|
+
requires_permission = authorization == 'permission'
|
|
83
84
|
|
|
84
85
|
# 2a. Authenticate (if mode requires user)
|
|
85
86
|
if requires_user:
|
|
86
87
|
await self._authenticate(api)
|
|
87
88
|
|
|
89
|
+
# 2a-bis. Check what the caller is allowed to do
|
|
90
|
+
if requires_permission:
|
|
91
|
+
denial = await self._authorize(api)
|
|
92
|
+
if denial:
|
|
93
|
+
return denial
|
|
94
|
+
|
|
88
95
|
# 2b. Validate state header (if mode requires state)
|
|
89
96
|
if requires_state:
|
|
90
97
|
state = self.headers.get('constitution-state')
|
|
@@ -236,6 +243,43 @@ class Dispatcher:
|
|
|
236
243
|
|
|
237
244
|
return api
|
|
238
245
|
|
|
246
|
+
async def _authorize(self, api: API):
|
|
247
|
+
"""Refuse the request when the caller lacks the permission it needs.
|
|
248
|
+
|
|
249
|
+
Returns the response to send back, or None to carry on.
|
|
250
|
+
"""
|
|
251
|
+
from ..permissions import allows, required_for
|
|
252
|
+
|
|
253
|
+
granted = await api.granted_permissions()
|
|
254
|
+
if granted is None:
|
|
255
|
+
logger.error(
|
|
256
|
+
"AUTHORIZATION=permission but %s does not implement "
|
|
257
|
+
"granted_permissions(); refusing the request",
|
|
258
|
+
type(api).__name__,
|
|
259
|
+
)
|
|
260
|
+
return {
|
|
261
|
+
'code': 403,
|
|
262
|
+
'body': {
|
|
263
|
+
'error': 'Forbidden',
|
|
264
|
+
'message': 'Permission checking is not configured for this endpoint',
|
|
265
|
+
},
|
|
266
|
+
'headers': {},
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
required = required_for(api.service_code, self.method, self.endpoint)
|
|
270
|
+
if allows(granted, required):
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
logger.warning("Denied: %s lacks %r", self.headers.get('x-caller', 'caller'), required)
|
|
274
|
+
return {
|
|
275
|
+
'code': 403,
|
|
276
|
+
'body': {
|
|
277
|
+
'error': 'Forbidden',
|
|
278
|
+
'message': f"Te falta el permiso '{required}'",
|
|
279
|
+
},
|
|
280
|
+
'headers': {},
|
|
281
|
+
}
|
|
282
|
+
|
|
239
283
|
async def _authenticate(self, api: API):
|
|
240
284
|
"""
|
|
241
285
|
Execute authentication middleware
|
|
@@ -40,7 +40,7 @@ Response (via Lambda base run()):
|
|
|
40
40
|
{"success": True, "data": [...]}
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
|
-
from typing import Any, List, Optional
|
|
43
|
+
from typing import Any, Dict, List, Optional, Union
|
|
44
44
|
from bson import ObjectId
|
|
45
45
|
from pydantic import BaseModel, ConfigDict, model_validator
|
|
46
46
|
|
|
@@ -111,6 +111,36 @@ class _ModelQuerySchema(BaseModel):
|
|
|
111
111
|
return self
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
def _enforce_exclusions(
|
|
115
|
+
fields: Optional[dict], excluded: List[str]
|
|
116
|
+
) -> dict:
|
|
117
|
+
"""The projection the owner allows, whatever the caller asked for.
|
|
118
|
+
|
|
119
|
+
Mongo reads a projection as inclusive or exclusive, never both, so the
|
|
120
|
+
forbidden fields are dropped from an inclusive one and added to an exclusive
|
|
121
|
+
one. `_id` is ignored when deciding which it is: it may appear in either.
|
|
122
|
+
"""
|
|
123
|
+
projection = dict(fields or {})
|
|
124
|
+
if not excluded:
|
|
125
|
+
return projection
|
|
126
|
+
|
|
127
|
+
inclusive = any(value for key, value in projection.items() if key != "_id")
|
|
128
|
+
if not inclusive:
|
|
129
|
+
for name in excluded:
|
|
130
|
+
projection[name] = 0
|
|
131
|
+
return projection
|
|
132
|
+
|
|
133
|
+
for name in excluded:
|
|
134
|
+
projection.pop(name, None)
|
|
135
|
+
|
|
136
|
+
# Pedir sólo el campo prohibido dejaría la proyección vacía, y una
|
|
137
|
+
# proyección vacía en Mongo devuelve el documento entero: exactamente lo
|
|
138
|
+
# contrario de lo buscado. Sin nada legítimo que pedir, sólo el id.
|
|
139
|
+
if not any(value for key, value in projection.items() if key != "_id"):
|
|
140
|
+
return {"_id": 1}
|
|
141
|
+
return projection
|
|
142
|
+
|
|
143
|
+
|
|
114
144
|
class ModelQueryLambda(Lambda):
|
|
115
145
|
"""
|
|
116
146
|
Base class for cross-service MongoDB query Lambdas.
|
|
@@ -120,19 +150,35 @@ class ModelQueryLambda(Lambda):
|
|
|
120
150
|
"""
|
|
121
151
|
|
|
122
152
|
@property
|
|
123
|
-
def allowed_collections(self) -> List[str]:
|
|
153
|
+
def allowed_collections(self) -> Union[List[str], Dict[str, dict]]:
|
|
124
154
|
"""
|
|
125
155
|
Whitelist of collection names this Lambda is allowed to query.
|
|
126
156
|
|
|
127
157
|
Must be overridden — an empty list rejects all requests.
|
|
128
158
|
|
|
129
|
-
|
|
159
|
+
A list exposes each collection whole:
|
|
160
|
+
|
|
130
161
|
@property
|
|
131
162
|
def allowed_collections(self):
|
|
132
163
|
return ["dockets", "tax_sales"]
|
|
164
|
+
|
|
165
|
+
A dict lets the owner keep fields in, whatever the caller projects. This
|
|
166
|
+
is not `fields`: that one is the caller saying what it wants, this one is
|
|
167
|
+
the owner saying what never leaves.
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def allowed_collections(self):
|
|
171
|
+
return {"dockets": {}, "users": {"exclude": ["password"]}}
|
|
133
172
|
"""
|
|
134
173
|
return []
|
|
135
174
|
|
|
175
|
+
def excluded_fields(self, collection: str) -> List[str]:
|
|
176
|
+
"""Fields this collection never returns. Empty for list-style whitelists."""
|
|
177
|
+
allowed = self.allowed_collections
|
|
178
|
+
if not isinstance(allowed, dict):
|
|
179
|
+
return []
|
|
180
|
+
return list((allowed.get(collection) or {}).get("exclude") or [])
|
|
181
|
+
|
|
136
182
|
@property
|
|
137
183
|
def schema(self):
|
|
138
184
|
return _ModelQuerySchema
|
|
@@ -151,6 +197,16 @@ class ModelQueryLambda(Lambda):
|
|
|
151
197
|
f"Collection '{collection}' is not allowed. Allowed: {allowed}"
|
|
152
198
|
)
|
|
153
199
|
|
|
200
|
+
# Una pipeline puede renombrar un campo antes de que lo quitemos
|
|
201
|
+
# ($addFields, $replaceRoot, $lookup), así que no hay forma honesta de
|
|
202
|
+
# garantizar la exclusión sobre ella. Un $unset final daría una falsa
|
|
203
|
+
# sensación de seguridad, que es peor que no tener la función.
|
|
204
|
+
if self.excluded_fields(collection) and self.data.get("pipeline") is not None:
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"Collection '{collection}' hides fields, so it cannot be queried "
|
|
207
|
+
f"with a pipeline. Use 'filter' instead."
|
|
208
|
+
)
|
|
209
|
+
|
|
154
210
|
async def process(self) -> Any:
|
|
155
211
|
collection_name: str = self.data["collection"]
|
|
156
212
|
database: str = self.data.get("database") or self.session.state
|
|
@@ -167,7 +223,10 @@ class ModelQueryLambda(Lambda):
|
|
|
167
223
|
limit: int = self.data.get("limit") or 0
|
|
168
224
|
skip: int = self.data.get("skip") or 0
|
|
169
225
|
|
|
170
|
-
|
|
226
|
+
projection = _enforce_exclusions(
|
|
227
|
+
fields, self.excluded_fields(collection_name)
|
|
228
|
+
)
|
|
229
|
+
cursor = collection.find(mongo_filter, projection)
|
|
171
230
|
|
|
172
231
|
if skip:
|
|
173
232
|
cursor = cursor.skip(skip)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Derives a service's permissions from the API files it actually has.
|
|
2
|
+
|
|
3
|
+
The endpoints are already declared twice — in the terraform routes and in the
|
|
4
|
+
file convention the dispatcher resolves on. A hand-written list would be a third
|
|
5
|
+
copy, and the first endpoint added without touching it would be an unguarded
|
|
6
|
+
hole nobody notices.
|
|
7
|
+
|
|
8
|
+
Mirrors `aws_python_helper.api.fetcher` in reverse: that one turns a request into
|
|
9
|
+
a file, this one turns the files into the requests they answer. It lives beside
|
|
10
|
+
the fetcher so the two cannot drift: a routing rule that changes in one would
|
|
11
|
+
otherwise silently stop matching in the other, and an access check that never
|
|
12
|
+
matches fails open or shut without anyone noticing.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
from typing import Dict, List
|
|
17
|
+
|
|
18
|
+
from ..api.fetcher import Fetcher
|
|
19
|
+
|
|
20
|
+
# Cómo se llama el archivo según el método. `list` y `get` son ambos GET: uno
|
|
21
|
+
# responde la colección, el otro un elemento.
|
|
22
|
+
FILE_METHODS = {
|
|
23
|
+
"list": "GET",
|
|
24
|
+
"get": "GET",
|
|
25
|
+
"post": "POST",
|
|
26
|
+
"put": "PUT",
|
|
27
|
+
"patch": "PATCH",
|
|
28
|
+
"delete": "DELETE",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ALL = "*"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def discover(api_root: str, service: str) -> List[Dict[str, str]]:
|
|
35
|
+
"""Every endpoint this service answers, as permission records.
|
|
36
|
+
|
|
37
|
+
The `{id}` of a route is left out on purpose: holding an id does not change
|
|
38
|
+
who may call the endpoint, and leaving it out makes the mapping between a
|
|
39
|
+
file and a permission exact.
|
|
40
|
+
"""
|
|
41
|
+
found: List[Dict[str, str]] = []
|
|
42
|
+
|
|
43
|
+
for current, _, files in os.walk(api_root):
|
|
44
|
+
for filename in sorted(files):
|
|
45
|
+
stem, extension = os.path.splitext(filename)
|
|
46
|
+
if extension != ".py" or stem not in FILE_METHODS:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
relative = os.path.relpath(current, api_root)
|
|
50
|
+
parts = [] if relative == "." else relative.split(os.sep)
|
|
51
|
+
path = "/" + "/".join(parts)
|
|
52
|
+
method = FILE_METHODS[stem]
|
|
53
|
+
|
|
54
|
+
# `list` responde la colección y `get` un elemento: mismo método
|
|
55
|
+
# sobre rutas distintas, así que son dos permisos, no uno.
|
|
56
|
+
if stem == "get":
|
|
57
|
+
path = f"{path.rstrip('/')}/{{id}}"
|
|
58
|
+
|
|
59
|
+
found.append({
|
|
60
|
+
"code": f"{service}:{method} {path}",
|
|
61
|
+
"service": service,
|
|
62
|
+
"method": method,
|
|
63
|
+
"path": path,
|
|
64
|
+
"scope": "item" if stem == "get" else "collection",
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return sorted(found, key=lambda item: item["code"])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def service_wildcard(service: str) -> str:
|
|
71
|
+
return f"{service}:*"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def required_for(service: str, method: str, endpoint: str, api_root: str = "api") -> str:
|
|
75
|
+
"""The permission a request needs.
|
|
76
|
+
|
|
77
|
+
Asks the framework which file will answer, and names the permission after it.
|
|
78
|
+
Reimplementing the routing rules here would be a second copy that drifts, and
|
|
79
|
+
a permission that never matches fails open or shut without anyone noticing —
|
|
80
|
+
which is the worst way for an access check to be wrong.
|
|
81
|
+
"""
|
|
82
|
+
resolved = Fetcher(endpoint, method.lower()).file_path
|
|
83
|
+
marker = os.sep + api_root + os.sep
|
|
84
|
+
if marker not in resolved:
|
|
85
|
+
return ""
|
|
86
|
+
|
|
87
|
+
relative = resolved.split(marker, 1)[1]
|
|
88
|
+
directory, filename = os.path.split(relative)
|
|
89
|
+
stem = os.path.splitext(filename)[0]
|
|
90
|
+
if stem not in FILE_METHODS:
|
|
91
|
+
return ""
|
|
92
|
+
|
|
93
|
+
path = "/" + directory.replace(os.sep, "/") if directory else "/"
|
|
94
|
+
if stem == "get":
|
|
95
|
+
path = f"{path.rstrip('/')}/{{id}}"
|
|
96
|
+
return f"{service}:{FILE_METHODS[stem]} {path}"
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def allows(granted: List[str], required: str) -> bool:
|
|
100
|
+
"""Whether a set of granted permissions covers the one an endpoint needs."""
|
|
101
|
+
if not required:
|
|
102
|
+
return False
|
|
103
|
+
if ALL in granted or required in granted:
|
|
104
|
+
return True
|
|
105
|
+
return service_wildcard(required.split(":", 1)[0]) in granted
|
|
@@ -39,6 +39,8 @@ aws_python_helper/model_index_sync/__init__.py
|
|
|
39
39
|
aws_python_helper/model_index_sync/base.py
|
|
40
40
|
aws_python_helper/model_query/__init__.py
|
|
41
41
|
aws_python_helper/model_query/base.py
|
|
42
|
+
aws_python_helper/permissions/__init__.py
|
|
43
|
+
aws_python_helper/permissions/catalog.py
|
|
42
44
|
aws_python_helper/repository/__init__.py
|
|
43
45
|
aws_python_helper/repository/base.py
|
|
44
46
|
aws_python_helper/sns/__init__.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/api/auth_middleware.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/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.1.0 → aws_python_helper-1.3.0}/aws_python_helper/context/state_validator.py
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/database/database_proxy.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.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.1.0 → aws_python_helper-1.3.0}/aws_python_helper/invoker/lambda_invoker.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/base.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/fetcher.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/lambda_standalone/handler.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_index_sync/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_index_sync/base.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper/model_query/__init__.py
RENAMED
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.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
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{aws_python_helper-1.1.0 → aws_python_helper-1.3.0}/aws_python_helper.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|