@@ -77,16 +77,20 @@ async def _binding_list_context(request: Request) -> dict[str, Any]:
7777def _require_binding_operation (
7878 operation : Operation ,
7979) -> Callable [..., Awaitable [Principal ]]:
80- """Authorize a by-ID route against the binding's stored target ."""
80+ """Use stored-target authorization when the provider opts in ."""
8181
8282 async def dependency (request : Request , binding_id : int ) -> Principal :
8383 authorizer = get_authorizer (operation )
84- if isinstance (authorizer , IdentityResolver ):
85- identity = await authorizer .resolve_identity (request , operation )
86- else :
87- # Legacy providers can still resolve namespace-wide operations.
88- # Target-bound providers should implement IdentityResolver.
89- identity = await authorizer .authorize (request , operation )
84+ if (
85+ not isinstance (authorizer , IdentityResolver )
86+ or not authorizer .binding_target_authorization
87+ ):
88+ # Preserve the existing single namespace-wide authorization call
89+ # for providers without an independent identity lookup. Their
90+ # operation contract may reject a new target context altogether.
91+ return await authorizer .authorize (request , operation )
92+
93+ identity = await authorizer .resolve_identity (request , operation )
9094 # Use a short-lived session so no database connection is held while the
9195 # authorization provider performs a potentially remote request.
9296 async with AsyncSessionLocal () as db :
@@ -236,9 +240,9 @@ async def get_control_binding(
236240) -> GetControlBindingResponse :
237241 """Read a single control binding by surrogate ID.
238242
239- Authorization uses the binding's stored target identifiers. After
240- authorization succeeds, the row is loaded again using the namespace
241- resolved by the authorizer before any binding data is returned.
243+ Target-aware authorizers use the binding's stored target identifiers.
244+ Other authorizers retain namespace-wide authorization. The row is loaded
245+ using the authorized namespace before any binding data is returned.
242246 """
243247 service = ControlBindingsService (db )
244248 binding = await service .get_binding_or_404 (
@@ -295,8 +299,9 @@ async def patch_control_binding(
295299) -> PatchControlBindingResponse :
296300 """Update the ``enabled`` flag on a control binding.
297301
298- Authorization uses the binding's stored target identifiers. The mutation
299- remains scoped to the namespace resolved by the authorizer.
302+ Target-aware authorizers use the binding's stored target identifiers.
303+ Other authorizers retain namespace-wide authorization. The mutation
304+ remains scoped to the authorized namespace.
300305 """
301306 service = ControlBindingsService (db )
302307 binding = await service .set_enabled (
@@ -321,8 +326,9 @@ async def delete_control_binding(
321326) -> DeleteControlBindingResponse :
322327 """Delete a control binding by surrogate ID.
323328
324- Authorization uses the binding's stored target identifiers. The deletion
325- remains scoped to the namespace resolved by the authorizer.
329+ Target-aware authorizers use the binding's stored target identifiers.
330+ Other authorizers retain namespace-wide authorization. The deletion
331+ remains scoped to the authorized namespace.
326332 """
327333 service = ControlBindingsService (db )
328334 await service .delete_binding (namespace_key = principal .namespace_key , binding_id = binding_id )
0 commit comments