tigrbl_kms 0.3.2.dev7__tar.gz → 0.3.2.dev8__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.
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/PKG-INFO +1 -1
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/pyproject.toml +1 -1
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/app.py +5 -0
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/orm/key.py +39 -13
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/orm/key_version.py +31 -4
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/utils.py +1 -1
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/LICENSE +0 -0
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/README.md +0 -0
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/__init__.py +0 -0
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/__main__.py +0 -0
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/cli.py +0 -0
- {tigrbl_kms-0.3.2.dev7 → tigrbl_kms-0.3.2.dev8}/tigrbl_kms/orm/__init__.py +0 -0
|
@@ -41,6 +41,11 @@ app = TigrblApp(
|
|
|
41
41
|
# Custom ops return raw dicts so no finalize hook needed
|
|
42
42
|
app.include_tables([Key, KeyVersion], base_prefix="/kms")
|
|
43
43
|
app.attach_diagnostics(prefix="/system")
|
|
44
|
+
|
|
45
|
+
# Keep package-level contract focused on domain models only.
|
|
46
|
+
for _system_model in ("__tigrbl_system_docs__", "__tigrbl_system_routes__"):
|
|
47
|
+
app.tables.pop(_system_model, None)
|
|
48
|
+
|
|
44
49
|
# Backward-compatible alias used by tests and older integrations.
|
|
45
50
|
app.routes = app.router.routes
|
|
46
51
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# tigrbl_kms/orm/key.py
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import base64
|
|
4
|
+
import inspect
|
|
4
5
|
from enum import Enum
|
|
5
6
|
from uuid import UUID, uuid4
|
|
6
7
|
from typing import List, Optional, TYPE_CHECKING
|
|
@@ -11,10 +12,10 @@ from tigrbl.orm.mixins import BulkCapable, Replaceable
|
|
|
11
12
|
|
|
12
13
|
from tigrbl.orm.tables import Base
|
|
13
14
|
from tigrbl.specs import acol, vcol, S, F, IO
|
|
14
|
-
from tigrbl.hook import hook_ctx
|
|
15
|
-
from tigrbl.
|
|
15
|
+
from tigrbl.decorators.hook import hook_ctx
|
|
16
|
+
from tigrbl.decorators import op_ctx
|
|
16
17
|
from tigrbl.runtime.status.exceptions import HTTPException
|
|
17
|
-
from tigrbl
|
|
18
|
+
from tigrbl import Response
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
20
21
|
from .key_version import KeyVersion
|
|
@@ -253,7 +254,22 @@ class Key(Base, BulkCapable, Replaceable):
|
|
|
253
254
|
)
|
|
254
255
|
async def _ensure_key_enabled(cls, ctx):
|
|
255
256
|
pp = ctx.get("path_params") or {}
|
|
256
|
-
|
|
257
|
+
temp = ctx.get("temp") or {}
|
|
258
|
+
route = temp.get("route") if isinstance(temp, dict) else {}
|
|
259
|
+
route_pp = route.get("path_params") if isinstance(route, dict) else {}
|
|
260
|
+
if not isinstance(route_pp, dict):
|
|
261
|
+
route_pp = {}
|
|
262
|
+
|
|
263
|
+
# Runtime route metadata may stage member identifiers in temp.route.
|
|
264
|
+
# Support both current and legacy parameter names.
|
|
265
|
+
ident = (
|
|
266
|
+
pp.get("id")
|
|
267
|
+
or pp.get("item_id")
|
|
268
|
+
or pp.get("key_id")
|
|
269
|
+
or route_pp.get("id")
|
|
270
|
+
or route_pp.get("item_id")
|
|
271
|
+
or route_pp.get("key_id")
|
|
272
|
+
)
|
|
257
273
|
if not ident:
|
|
258
274
|
raise HTTPException(status_code=400, detail="Missing key identifier")
|
|
259
275
|
try:
|
|
@@ -263,16 +279,26 @@ class Key(Base, BulkCapable, Replaceable):
|
|
|
263
279
|
|
|
264
280
|
db = ctx.get("db")
|
|
265
281
|
if db is None:
|
|
266
|
-
|
|
267
|
-
|
|
282
|
+
try:
|
|
283
|
+
db, _release_db = cls.acquire(op_alias="read")
|
|
284
|
+
# Keep acquired session available for downstream ops in this request.
|
|
285
|
+
ctx["db"] = db
|
|
286
|
+
temp = ctx.get("temp")
|
|
287
|
+
if not isinstance(temp, dict):
|
|
288
|
+
temp = {}
|
|
289
|
+
ctx["temp"] = temp
|
|
290
|
+
temp.setdefault("__sys_db_release__", _release_db)
|
|
291
|
+
except Exception as exc:
|
|
292
|
+
raise HTTPException(
|
|
293
|
+
status_code=500, detail="DB session missing"
|
|
294
|
+
) from exc
|
|
295
|
+
|
|
268
296
|
getter = getattr(db, "get", None)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
else db.get(cls, ident)
|
|
275
|
-
)
|
|
297
|
+
if not callable(getter):
|
|
298
|
+
raise HTTPException(status_code=500, detail="DB session missing get()")
|
|
299
|
+
obj = getter(cls, ident)
|
|
300
|
+
if inspect.isawaitable(obj):
|
|
301
|
+
obj = await obj
|
|
276
302
|
if obj is None:
|
|
277
303
|
raise HTTPException(status_code=404, detail="Key not found")
|
|
278
304
|
if obj.status == KeyStatus.disabled:
|
|
@@ -4,11 +4,9 @@ from uuid import UUID, uuid4
|
|
|
4
4
|
|
|
5
5
|
from tigrbl.runtime.status.exceptions import HTTPException
|
|
6
6
|
|
|
7
|
-
from tigrbl.hook import hook_ctx
|
|
8
|
-
from tigrbl.specs import IO, F, S, acol
|
|
7
|
+
from tigrbl.decorators.hook import hook_ctx
|
|
8
|
+
from tigrbl.specs import IO, F, S, Pair, ForeignKeySpec, acol
|
|
9
9
|
from tigrbl.orm.mixins import BulkCapable, Replaceable
|
|
10
|
-
from tigrbl.column.io_spec import Pair
|
|
11
|
-
from tigrbl.column.storage_spec import ForeignKeySpec
|
|
12
10
|
from tigrbl.orm.tables import Base
|
|
13
11
|
from tigrbl.types import (
|
|
14
12
|
Integer,
|
|
@@ -98,6 +96,35 @@ class KeyVersion(Base, BulkCapable, Replaceable):
|
|
|
98
96
|
|
|
99
97
|
key = relationship("Key", back_populates="versions", lazy="joined")
|
|
100
98
|
|
|
99
|
+
@hook_ctx(ops=("create", "bulk_create"), phase="PRE_HANDLER")
|
|
100
|
+
async def _coerce_key_id_uuid(cls, ctx):
|
|
101
|
+
payload = ctx.get("payload")
|
|
102
|
+
if payload is None:
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
def _coerce_item(item):
|
|
106
|
+
if not isinstance(item, dict):
|
|
107
|
+
return
|
|
108
|
+
key_id = item.get("key_id")
|
|
109
|
+
if key_id is None or isinstance(key_id, UUID):
|
|
110
|
+
return
|
|
111
|
+
try:
|
|
112
|
+
item["key_id"] = UUID(str(key_id))
|
|
113
|
+
except Exception as exc:
|
|
114
|
+
raise HTTPException(status_code=400, detail="Invalid key_id") from exc
|
|
115
|
+
|
|
116
|
+
if isinstance(payload, list):
|
|
117
|
+
for item in payload:
|
|
118
|
+
_coerce_item(item)
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
if isinstance(payload, dict):
|
|
122
|
+
_coerce_item(payload)
|
|
123
|
+
items = payload.get("items")
|
|
124
|
+
if isinstance(items, list):
|
|
125
|
+
for item in items:
|
|
126
|
+
_coerce_item(item)
|
|
127
|
+
|
|
101
128
|
@hook_ctx(ops="create", phase="POST_HANDLER")
|
|
102
129
|
async def _generate_material(cls, ctx):
|
|
103
130
|
obj = ctx.get("result")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|