Skip to content

[CHORE]: Retire "all" APL keyword. #143

Description

@terylt

Summary

Now that policy bundles are a first-class top-level groups: section (PR #131), the
one remaining issue is the reserved all name. all is a magic key that means
"apply to every request" — it is not something a route ever opts into, yet it sits in
the same map as the opt-in bundles. This issue tracks moving the always-on policy to a
dedicated global.authorization block (beside the existing global.authentication),
so groups: holds only opt-in bundles with no reserved names.

Split out of the PR #131 review
(araujof) because it is (a) an open design decision, (b) cross-crate, and (c) orthogonal
to that PR's thesis (RFC 8693 actor tokens + subjects). PR #131 ships groups: with
all kept working as the reserved always-on group — this is the interim state below.

Current state (as of PR #131)

  • Bundles live at top-level groups: (canonical); global.policies: is a deprecated
    alias, merged into the same internal map at parse time.
  • all is still a reserved key inside that bundle map. It works as it always has:
    • its plugins are always included (cpex-core::config::resolve_plugins_for_entity,
      step 1: config.global.policies.get("all"));
    • the identity resolver's always-on layer is the separate global.authentication:
      block (GlobalConfig.identity), not the all group.
  • This is effectively option (a) below, documented as interim in the cookbook's
    scoping table.

The decision: (a) vs (b)

(a) Keep all as a reserved key inside groups:. Document that groups.all
applies to every request and every other key is opt-in via a route's groups: / tags.
Minimal change; keeps the magic name.

(b) Retire the magic name. Move the always-on
policy to a dedicated global.authorization block, symmetric with the existing
global.authentication. Then groups: holds only opt-in bundles — no reserved names,
three clean top-level concerns:

global:
  authentication: [jwt-user]        # always-on identity  (exists today)
  authorization:                    # always-on policy    (NEW — replaces groups.all)
    pre_invocation:
      - "require(role.employee)"
groups:                             # opt-in bundles only, no reserved names
  hr-tools:
    authentication: [jwt-manager]
    authorization:
      pre_invocation: [ "require(role.hr)" ]
routes:
  - tool: get_compensation
    groups: hr-tools

Scope

The always-on all layer is applied in two places, so both must move:

  1. cpex-coreresolve_plugins_for_entity step 1 reads global.policies.get("all")
    for always-on plugins. Point this at global.authorization (+ the always-on
    plugins it carries).
  2. apl-cpex — always-on authorization (APL) steps compile through the visitor's
    layers. Confirm how the all bundle's authorization: steps reach every route today
    (tag_layers["all"] vs global_layer) and route them from global.authorization
    instead.

global.authorization should accept the same shape as a group's authorization: /
plugins (reuse PolicyGroup or a focused subset).

Back-compat (required)

  • all (at groups.all and the deprecated global.policies.all) must keep working
    as a deprecated alias for global.authorization, merged at parse time. Emit a
    deprecation tracing::warn! when the all spelling is used.
  • Existing configs (the CPEX demo cpex.yaml, praxis fixtures) must load unchanged.

Acceptance criteria

  • global.authorization block accepted and applied always-on for both authorization
    steps and plugins.
  • all (both locations) still works, merged into global.authorization, with a
    deprecation warning.
  • Both application sites moved (cpex-core resolver + apl-cpex always-on layer), with
    tests proving an always-on rule from global.authorization fires on a route that
    declares no groups.
  • Cookbook scoping table updated: Global row → global.authorization; drop the
    "reserved all" interim note.
  • configuration.md documents global.authorization and the all deprecation.
  • cargo fmt --check + cargo clippy -- -D warnings clean; workspace green.
  • Linter runs cleanly (make lint)
  • CI passes with no regressions
  • Docs/tooling updated (if applicable)
  • Security scans pass

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions