feat: add per-operation auth descriptor with precedence ladder#155
feat: add per-operation auth descriptor with precedence ladder#155OmarAlJarrah wants to merge 1 commit into
Conversation
Auth requirements are per-operation, and a two-boolean "needs-auth / needs-key" model does not generalise to operations that accept several alternative schemes with different OAuth parameters. This adds a hand-constructable, scheme-agnostic descriptor model to sdk-core plus a deterministic resolver. - AuthRequirement: one accepted AuthScheme paired with its own OAuth scopes/params (immutable, Builder + newBuilder). - AuthDescriptor: a per-operation ordered list of AuthRequirements in preference order (immutable, Builder + newBuilder, of/ofSchemes factories). Records which schemes are acceptable, never how they are stamped onto the wire. - AuthDescriptorTier / AuthResolution: the precedence tier and the resolved outcome (requirement + tier + anonymous flag). - AuthDescriptorResolver: applies two precedence orders — tier precedence (per-call override > operation default > client default, no fall-through past a supplied higher tier) then requirement precedence within the chosen descriptor (first satisfiable scheme wins; NO_AUTH is always satisfiable). Throws AuthResolutionException naming the required and available schemes when nothing matches. The resolver stays scheme-agnostic: callers supply the set of schemes they can satisfy and map the resolved requirement to a concrete credential and auth step themselves; per-cloud / OAuth specifics stay in adapters. No code generation — these are the runtime primitives a generator would later target. Closes #63
|
Adds a scheme-agnostic per-operation auth descriptor model to I checked the resolution logic against the described contract: tier precedence (PER_CALL > OPERATION > CLIENT) is applied with explicit no-fall-through, and within the chosen descriptor it selects the first satisfiable requirement, with NO_AUTH always satisfiable. The model types are immutable with private constructors, defensive copies of the scopes/params collections, and the usual Builder/factory surface, so there's no shared mutable state to worry about across calls. The resolver being stateless keeps it safe to reuse concurrently. Since these are new types not yet wired into any pipeline there's no API-compat concern, and the unit tests cover the ladder ordering, the no-fall-through behavior, and the NO_AUTH path well. |
Auth requirements are per-operation, and a two-boolean "needs-auth / needs-key" model does not generalise to operations that accept several alternative schemes with different OAuth parameters. This adds a hand-constructable, scheme-agnostic auth descriptor model to
sdk-coreplus a deterministic resolver.What this adds (
org.dexpace.sdk.core.http.auth)AuthRequirement— one acceptedAuthSchemepaired with its own OAuth scopes/params. WhereAuthMetadataflattens an operations schemes into a single shared OAuth bag, a requirement pairs each scheme with its own parameters. Immutable, private-field copy-in +Builder/newBuilder,of(scheme)convenience.AuthDescriptor— the per-operation ordered list ofAuthRequirements in preference order. Immutable,Builder/newBuilder,of(...)/ofSchemes(...)factories,allowsAnonymous(). Records which schemes are acceptable and in what order, never how a scheme is stamped onto the wire.AuthDescriptorTier— the precedence tier an descriptor occupies:PER_CALL>OPERATION>CLIENT.AuthResolution— the resolved outcome: the chosen requirement, the tier it came from, and anisAnonymousflag.AuthResolutionException— tailored failure naming the required and available schemes (e.g.operation requires one of [OAUTH2, API_KEY] but no matching credential is available (have: [BASIC])).AuthDescriptorResolver— the precedence ladder. Two independent orders:NO_AUTHis always satisfiable (anonymous access); any other scheme is satisfiable iff it is in the caller-supplied available-schemes set.Scheme-agnostic by design
Core never inspects a concrete
Credentialor knows how a scheme is stamped. The caller supplies the set of schemes it can satisfy (derived from configured credentials) and maps the resolved requirement to a credential + auth step itself. Per-cloud / OAuth specifics stay in adapters. There is no code generation — these are the runtime primitives a generator would later target, fully usable by hand today.Tests
New suites cover requirement/descriptor construction + builders + defensive copies, tier precedence (including the no-fall-through rule), requirement precedence, anonymous (
NO_AUTH) handling, OAuth-parameter forwarding, and the tailored failure message.Gated build (scoped, run locally)
Closes #63