enlace_auth.stores.middleware#
Store injection middleware and per-app store router.
StoreInjectionMiddleware runs after the auth middleware. It reads
scope["state"]["user_id"] and scope["state"]["app_id"] (the latter set
by the per-mount wrapper in enlace.compose) and attaches a
PrefixedStore(base, f"{user_id}/{app_id}/") to scope["state"]["store"].
If either id is missing, store is None — apps are expected to handle
that case gracefully (they do so naturally by providing a dict fallback in
standalone mode).
Module Attributes
no (more) share for |
|
How many items the list route returns before it says |
Functions
|
A strong ETag for a stored JSON value: a hash of its canonical serialisation. |
|
Return a router exposing the per-user store of each app that has one. |
Classes
|
Pure-ASGI middleware that injects |
- enlace_auth.stores.middleware.DEFAULT_MAX_ITEMS = 5000#
How many items the list route returns before it says
"truncated": true.
- enlace_auth.stores.middleware.NO_ACCESS = 'no_access'#
no (more) share for
?owner=, or no such key.- Type:
The 404 details of the store routes
- class enlace_auth.stores.middleware.StoreInjectionMiddleware(app, *, base_store=None)[source]#
Bases:
objectPure-ASGI middleware that injects
request.state.store.
- enlace_auth.stores.middleware.etag_of(value)[source]#
A strong ETag for a stored JSON value: a hash of its canonical serialisation.
- Return type:
- enlace_auth.stores.middleware.make_store_router(*, base_store_getter, protected_apps, share_access=None, max_items=5000)[source]#
Return a router exposing the per-user store of each app that has one.
Routes:
GET /api/{app_id}/store?prefix=<p>→{"items": {key: {"value", "etag"}}, "truncated"}GET /api/{app_id}/store/{key}→{"value": v}with anETagheaderPUT /api/{app_id}/store/{key}— conditional withIf-Match: <etag>orIf-None-Match: *DELETE /api/{app_id}/store/{key}—If-Matchlikewise
A failed precondition is 412 with the current
{"value", "etag"}(valuenull when the key is absent), so the client can resolve and retry.protected_appsnames the apps with a per-user store:protected:userapps and those whoseapp.tomlsetsuser_store = true(a callable is re-read per request).?owner=<email>on any route acts on that user’s data instead of the caller’s, whenshare_access(app_id, owner, caller)says so ("rw", or"ro"for GET only); otherwise 404, as if the owner had nothing. Withoutshare_accessevery?owner=naming someone else is 404. Seemisc/docs/decisions/0001-owner-granted-data-shares.md.The router assumes
PlatformAuthMiddlewarehas setrequest.state.user_id(public apps get it too, when the visitor is signed in). CSRF on writes is theCSRFMiddleware’s, through itsenforce_prefixes.- Return type:
APIRouter