Class S2SServiceLevelDecisionSource

java.lang.Object
io.confluent.security.auth.s2s.S2SServiceLevelDecisionSource
All Implemented Interfaces:
S2SDecisionSource

public class S2SServiceLevelDecisionSource extends Object implements S2SDecisionSource
SRJ Cedar integration point for the L3b service-level AuthZ provider.

start() builds the CedarS2SPass from the L3 category of the in-memory policy snapshot held by CedarPolicyStore and wraps it in a VERDICT-role MeteredPass (s2s-verdict), which records the per-pass S2S AuthZ metric on each applicable evaluation and returns the delegate's raw Decision unchanged. evaluate(EvaluationInput) calls Pass.appliesTo(Request)/Pass.evaluate(Request) directly per request — ce-kafka owns the gate/verdict interpretation itself.

toRequest(EvaluationInput) evaluates the system-LAT sub SPIFFE-ID (the request originator, carried on EvaluationInput.spiffeId() — the L3b provider populates this from MultiTenantPrincipal.tokenSubSpiffe(), not the cert spiffeId() that L2 uses).

Verdict mapping (tri-state, grant-capable). As the VERDICT layer, L3b is the only layer that can positively grant. A matching permit → Effect.ALLOW → Decision.ALLOW (a real grant). Effect.DENY (explicit forbid) and Effect.ABSTAIN (no matching L3 permit) both → Decision.DENY — a VERDICT default-denies when nothing positively grants, so an empty/no-match policy set denies everything. Policy authors must therefore ship at least one baseline permit for every service that should be allowed to call in. A pass that does not apply (principal is not a parseable SPIFFE) also default-denies here.

Lifecycle / failure semantics (fail-closed):

  • empty L3 policy snapshot → install null; evaluate(EvaluationInput) returns DENY for every request (VERDICT default-deny -- a valid "no permits" state, hence the bootstrap L3 permit requirement).
  • pass build throws on cold start (start()) → rethrow → broker startup FAILS. A fresh process has no last-good pass; coming up would silently deny every system-LAT request (an availability outage indistinguishable from a real default-deny), so we refuse to serve and let the rolling deploy halt on the old (still-good) pods.
  • pass build throws on reload (reload()) → log + keep the previously installed pass (last-good); the running broker is never taken down by a bad live update.
  • Constructor Details

    • S2SServiceLevelDecisionSource

      public S2SServiceLevelDecisionSource(CedarPolicyStore policyStore)
  • Method Details

    • start

      public void start()
      Description copied from interface: S2SDecisionSource
      Cold-start lifecycle hook invoked once at broker/controller startup AFTER the config snapshot has been loaded into any backing store. Implementations pre-build/validate state (e.g., hand policies to an underlying evaluator).

      Fail-closed: a present-but-unbuildable policy set MUST throw here so broker startup fails. A fresh process has no last-good state to fall back on, so coming up with enforcement silently disabled/degraded is not an option. Default is no-op.

      Specified by:
      start in interface S2SDecisionSource
    • reload

      public void reload()
      Description copied from interface: S2SDecisionSource
      Dynamic-reload lifecycle hook invoked on every config reload, AFTER the backing store has swapped in the new snapshot. Unlike S2SDecisionSource.start(), a build failure here MUST NOT throw: the broker is already serving, so it keeps the last-good state in place rather than being taken down by a bad live update (which validateReconfiguration() should have rejected upstream anyway). Default is no-op.
      Specified by:
      reload in interface S2SDecisionSource
    • evaluate

      public S2SDecisionSource.Result evaluate(EvaluationInput input)
      Specified by:
      evaluate in interface S2SDecisionSource