Implement Galaxy filters and API key constraints
This commit is contained in:
+45
-2
@@ -1,6 +1,8 @@
|
||||
# Gateway gRPC Authorization
|
||||
|
||||
The authorization subsystem enforces per-RPC scope checks against the authenticated `ApiKeyIdentity` produced by the authentication layer, so service implementations never need to repeat permission logic.
|
||||
The authorization subsystem has two layers. The gRPC interceptor enforces the
|
||||
verb scope required by the RPC. Service-layer constraint checks then narrow
|
||||
what an authenticated API key can browse, read, or write inside the Galaxy.
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -12,6 +14,8 @@ The participating types live under `src/MxGateway.Server/Security/Authorization/
|
||||
- `GatewayGrpcScopeResolver` maps a request message (and, for `MxCommandRequest`, the inner `MxCommandKind`) to the scope string that must be present on the caller.
|
||||
- `GatewayScopes` exposes the canonical scope constants used by the resolver and any downstream consumer.
|
||||
- `GatewayRequestIdentityAccessor` and `IGatewayRequestIdentityAccessor` expose the verified identity to handlers and any service code that runs inside the call.
|
||||
- `IConstraintEnforcer` applies optional API-key constraints against the
|
||||
cached Galaxy hierarchy from service bodies.
|
||||
- `GrpcAuthorizationServiceCollectionExtensions` wires the components into the DI container and the gRPC pipeline.
|
||||
|
||||
The `ApiKeyIdentity` consumed here is produced by the authentication layer; see [Authentication](./Authentication.md) for how it is built and how scopes are persisted.
|
||||
@@ -21,7 +25,9 @@ The `ApiKeyIdentity` consumed here is produced by the authentication layer; see
|
||||
Centralizing the policy in `GatewayGrpcAuthorizationInterceptor` produces three concrete benefits:
|
||||
|
||||
1. Every RPC defined in `MxAccessGatewayService` is covered by construction. A new RPC inherits the check the moment its request type is added to `GatewayGrpcScopeResolver`, instead of relying on each service method to remember to call an authorization helper.
|
||||
2. The service class stays a thin translator between proto contracts and domain calls. RPC methods do not branch on identity or scope, which keeps the AGENTS.md guideline that gRPC handlers contain no policy.
|
||||
2. Verb-scope policy stays centralized. Request-specific constraints still run
|
||||
in service bodies because they need command payloads, item handles, and
|
||||
Galaxy metadata that the interceptor should not inspect.
|
||||
3. Authentication and authorization happen in one place, so the gRPC `Status` mapping is consistent. A failed key check always returns `Unauthenticated`, and a missing scope always returns `PermissionDenied` with the offending scope name.
|
||||
|
||||
## Interceptor Flow
|
||||
@@ -131,6 +137,43 @@ private static string ResolveCommandScope(MxCommandKind kind)
|
||||
|
||||
Reads (`Register`, `AddItem`, `Advise`, and any other unspecified kind) fall through to `InvokeRead`, which keeps the matrix small while still separating reads from writes, secured writes, metadata lookups, event drains, and worker shutdown.
|
||||
|
||||
## Constraint Enforcement
|
||||
|
||||
`ApiKeyIdentity.Constraints` is optional. Empty constraints preserve the
|
||||
previous behavior: the key is authorized only by its verb scopes. Non-empty
|
||||
constraints are stored as JSON in `api_keys.constraints` and are applied by
|
||||
`IConstraintEnforcer` after the interceptor succeeds.
|
||||
|
||||
Supported constraints are:
|
||||
|
||||
| Constraint | Meaning |
|
||||
|------------|---------|
|
||||
| `read_subtrees` | Contained-path globs allowed for read/subscription commands. |
|
||||
| `write_subtrees` | Contained-path globs allowed for write commands. |
|
||||
| `read_tag_globs` | Tag-address globs allowed for read/subscription commands. |
|
||||
| `write_tag_globs` | Tag-address globs allowed for write commands. |
|
||||
| `max_write_classification` | Maximum Galaxy attribute `security_classification` a key may write. |
|
||||
| `browse_subtrees` | Contained-path globs used to filter Galaxy browse results and deploy-event counts. |
|
||||
| `read_alarm_only` | Read/subscription commands must target objects with alarm-bearing attributes. |
|
||||
| `read_historized_only` | Read/subscription commands must target objects with historized attributes. |
|
||||
|
||||
Glob matching is anchored, case-insensitive, and supports `*` and `?`.
|
||||
Subtree and tag glob lists are alternatives: matching either list allows that
|
||||
scope dimension. Empty lists mean unconstrained for that dimension.
|
||||
|
||||
The service checks read constraints for `AddItem`, `AddItem2`, `AddItemBulk`,
|
||||
`SubscribeBulk`, and `AdviseItemBulk`. It checks write constraints for
|
||||
`Write`, `Write2`, `WriteSecured`, and `WriteSecured2`. Successful item
|
||||
registrations are tracked per session so later item-handle commands resolve
|
||||
back to the original tag address. If a constrained key presents an unknown item
|
||||
handle, the gateway fails closed.
|
||||
|
||||
Non-bulk constraint failures return gRPC `PermissionDenied`. Bulk read
|
||||
commands preserve input order and return a failed `SubscribeResult` for each
|
||||
denied item while still forwarding allowed items to the worker. Every denial
|
||||
adds an `api_key_audit` entry with the key id, command kind, target, and
|
||||
blocking constraint; secured values and raw credentials are never logged.
|
||||
|
||||
## Scope Catalog
|
||||
|
||||
`GatewayScopes` is the single source of truth for scope strings. Every entry is currently mapped by either the resolver or another security component:
|
||||
|
||||
Reference in New Issue
Block a user