Record authenticated caller in the decision envelope.
FLEX-WP-0023-T04: provenance.caller is additive (mode required; principal/audience/not_after when a token was reviewed). TokenReview keeps the JWT exp. request_digest is unchanged because the caller is not binding material. Assistant: grok Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
This commit is contained in:
parent
e62c0cfc36
commit
ca070df32d
15 changed files with 344 additions and 35 deletions
|
|
@ -14,6 +14,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/netkingdom/flex-auth/internal/callerauth"
|
||||
"github.com/netkingdom/flex-auth/internal/policy"
|
||||
"github.com/netkingdom/flex-auth/internal/registry"
|
||||
"github.com/netkingdom/flex-auth/pkg/api"
|
||||
|
|
@ -112,7 +113,7 @@ func (e *Engine) Check(ctx context.Context, request api.CheckRequest) (api.Decis
|
|||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
|
||||
decision := e.envelope(normalized, request, expectation, facts)
|
||||
decision := e.envelope(ctx, normalized, request, expectation, facts)
|
||||
if err := e.recordDecision(decision); err != nil {
|
||||
return api.DecisionEnvelope{}, err
|
||||
}
|
||||
|
|
@ -345,7 +346,7 @@ func enrichResourceRef(ref api.ResourceRef, resource api.Resource, overridden *[
|
|||
return out
|
||||
}
|
||||
|
||||
func (e *Engine) envelope(request, submitted api.CheckRequest, expectation api.DecisionExpectation, facts registryFacts) api.DecisionEnvelope {
|
||||
func (e *Engine) envelope(ctx context.Context, request, submitted api.CheckRequest, expectation api.DecisionExpectation, facts registryFacts) api.DecisionEnvelope {
|
||||
envelope := api.DecisionEnvelope{
|
||||
RequestID: request.ID,
|
||||
Effect: expectation.Effect,
|
||||
|
|
@ -372,6 +373,7 @@ func (e *Engine) envelope(request, submitted api.CheckRequest, expectation api.D
|
|||
PolicyVersion: e.policy.Metadata.Version,
|
||||
PolicyPackageDigest: e.policy.Digest(),
|
||||
RegistrySnapshotDigest: e.store.Digest(),
|
||||
Caller: callerProvenance(ctx),
|
||||
},
|
||||
Caring: e.caringDecisionMetadata(facts.descriptor, expectation.ConformanceFindings),
|
||||
}
|
||||
|
|
@ -383,6 +385,22 @@ func (e *Engine) envelope(request, submitted api.CheckRequest, expectation api.D
|
|||
return envelope
|
||||
}
|
||||
|
||||
func callerProvenance(ctx context.Context) *api.CallerProvenance {
|
||||
record, ok := callerauth.RecordFromContext(ctx)
|
||||
if !ok {
|
||||
record = callerauth.Record{Mode: callerauth.ModeDisabled}
|
||||
}
|
||||
out := &api.CallerProvenance{Mode: string(record.Mode)}
|
||||
if record.Principal != "" {
|
||||
out.Principal = record.Principal
|
||||
out.Audience = record.Audience
|
||||
if !record.NotAfter.IsZero() {
|
||||
out.NotAfter = record.NotAfter.UTC().Format(time.RFC3339)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (e *Engine) recordDecision(decision api.DecisionEnvelope) error {
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/netkingdom/flex-auth/internal/audit"
|
||||
"github.com/netkingdom/flex-auth/internal/callerauth"
|
||||
"github.com/netkingdom/flex-auth/internal/decision"
|
||||
"github.com/netkingdom/flex-auth/internal/policy"
|
||||
"github.com/netkingdom/flex-auth/internal/registry"
|
||||
|
|
@ -68,6 +69,43 @@ func TestCheckUsesExplicitCaringContext(t *testing.T) {
|
|||
if len(got.Caring.RestrictionsEvaluated) != 1 || got.Caring.RestrictionsEvaluated[0] != api.RestrictionExportBlocked {
|
||||
t.Errorf("got.Caring.RestrictionsEvaluated = %v; want [ExportBlocked]", got.Caring.RestrictionsEvaluated)
|
||||
}
|
||||
if got.Provenance.Caller == nil || got.Provenance.Caller.Mode != "disabled" || got.Provenance.Caller.Principal != "" {
|
||||
t.Errorf("got.Provenance.Caller = %+v; want disabled with no principal", got.Provenance.Caller)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallerProvenanceDoesNotChangeRequestDigest(t *testing.T) {
|
||||
engine := newTestEngine(t)
|
||||
|
||||
var request api.CheckRequest
|
||||
loadYAML(t, filepath.Join("..", "..", "examples", "caring", "check_request.yaml"), &request)
|
||||
|
||||
without, err := engine.Check(context.Background(), request)
|
||||
if err != nil {
|
||||
t.Fatalf("Check: %v", err)
|
||||
}
|
||||
ctx := callerauth.WithRecord(context.Background(), callerauth.Record{
|
||||
Mode: callerauth.ModeEnforce,
|
||||
Principal: "system:serviceaccount:secrets-engine:secrets-engine",
|
||||
Audience: "flex-auth",
|
||||
NotAfter: time.Unix(1788730498, 0).UTC(),
|
||||
})
|
||||
with, err := engine.Check(ctx, request)
|
||||
if err != nil {
|
||||
t.Fatalf("Check with caller: %v", err)
|
||||
}
|
||||
if with.Binding.RequestDigest != without.Binding.RequestDigest {
|
||||
t.Fatalf("request_digest moved from %s to %s", without.Binding.RequestDigest, with.Binding.RequestDigest)
|
||||
}
|
||||
if with.Provenance.Caller == nil || with.Provenance.Caller.Mode != "enforce" {
|
||||
t.Fatalf("caller = %+v; want enforce", with.Provenance.Caller)
|
||||
}
|
||||
if with.Provenance.Caller.Principal != "system:serviceaccount:secrets-engine:secrets-engine" {
|
||||
t.Fatalf("principal = %q", with.Provenance.Caller.Principal)
|
||||
}
|
||||
if with.Provenance.Caller.Audience != "flex-auth" || with.Provenance.Caller.NotAfter != "2026-09-06T21:34:58Z" {
|
||||
t.Fatalf("audience/not_after = %+v", with.Provenance.Caller)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMatchesRegistryRelationshipDescriptor(t *testing.T) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue