Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .changeset/policy_directive_via_coprocessor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
hive-router-query-planner: minor
hive-router-internal: minor
hive-router-plan-executor: minor
hive-router: minor
---

# Custom authorization rules with the `@policy` directive

Adds support for the federation `@policy` directive, letting a coprocessor decide custom
authorization rules that the router cannot evaluate on its own.

`@policy(policies: [[...]])` takes an OR of AND groups, the same shape as `@requiresScopes`.
Access is granted when every policy of at least one group is granted for the request.

The decision is made in the `graphql.analysis` coprocessor stage, through two request context keys:

- `hive::authorization::required_policies` — written by the router, listing every policy the
incoming operation depends on. It is read-only, a coprocessor that writes to it fails the request.
- `hive::authorization::granted_policies` — written by the coprocessor with the subset it grants.
Policies left out are denied, so an absent or empty answer grants nothing.

Unauthorized fields are then handled by the existing
`authorization.directives.unauthorized.mode` setting: `filter` (default) nulls them and reports an
`UNAUTHORIZED_FIELD_OR_TYPE` error, `reject` fails the whole operation. As with the other
authorization directives, subgraph requests that would only resolve unauthorized fields are never
sent.

`@policy` is independent of `@authenticated` and `@requiresScopes`: it is enforced even when JWT
authentication is not configured, and when several directives sit on the same field all of them
must be satisfied.

Example coprocessor answer for the `graphql.analysis` stage:

```json
{
"version": 1,
"control": "continue",
"context": {
"hive::authorization::granted_policies": ["read_profile"]
}
}
```
18 changes: 18 additions & 0 deletions bin/router/benches/router_benches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ fn authorization_benchmark(c: &mut Criterion) {
bubble_up.schema_metadata,
bubble_up.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -139,6 +141,8 @@ fn authorization_benchmark(c: &mut Criterion) {
complex.schema_metadata,
complex.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -177,6 +181,8 @@ fn authorization_benchmark(c: &mut Criterion) {
complex_partially.schema_metadata,
complex_partially.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -261,6 +267,8 @@ fn authorization_benchmark(c: &mut Criterion) {
large_mostly_auth.schema_metadata,
large_mostly_auth.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -332,6 +340,8 @@ fn authorization_benchmark(c: &mut Criterion) {
large_partially_denied.schema_metadata,
large_partially_denied.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -414,6 +424,8 @@ fn authorization_benchmark(c: &mut Criterion) {
deep_nested.schema_metadata,
deep_nested.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -472,6 +484,8 @@ fn authorization_benchmark(c: &mut Criterion) {
large_unauth.schema_metadata,
large_unauth.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -519,6 +533,8 @@ fn authorization_benchmark(c: &mut Criterion) {
interface_auth_inline_unauth.schema_metadata,
interface_auth_inline_unauth.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down Expand Up @@ -572,6 +588,8 @@ fn authorization_benchmark(c: &mut Criterion) {
interface_auth_inline_auth.schema_metadata,
interface_auth_inline_auth.variable_payload,
&jwt_req_details,
&Default::default(),
true,
false,
)
.unwrap(),
Expand Down
Loading