Finish FLEX-WP-0019 layer-model v0.7 conformance
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 57s

Close the remaining PDP obligations: mechanical layer declaration check,
registry-snapshot digest in provenance, explicit allow TTL, per-input-class
freshness deadlines, and the published decision-record contract. Document
the canonical request digest as the §6.4.2 replay test.

Assistant: grok
Assistant-Session: 01a06256-fb71-7102-b3a9-27e6734257d0
This commit is contained in:
tegwick 2026-09-03 23:48:45 +02:00
parent 9689894c15
commit 56940727bf
32 changed files with 1194 additions and 111 deletions

View file

@ -10,6 +10,7 @@ import (
"slices"
"strings"
"sync"
"time"
"github.com/netkingdom/flex-auth/internal/policy"
"github.com/netkingdom/flex-auth/internal/registry"
@ -24,6 +25,7 @@ type Engine struct {
mu sync.RWMutex
history map[string]api.DecisionEnvelope
log DecisionRecorder
clock func() time.Time
}
// DecisionRecorder persists decision envelopes.
@ -82,6 +84,23 @@ func (e *Engine) SetDecisionLog(log DecisionRecorder) {
e.log = log
}
// SetClock overrides the engine clock. Tests use this to pin allow lifetimes.
func (e *Engine) SetClock(clock func() time.Time) {
e.mu.Lock()
defer e.mu.Unlock()
e.clock = clock
}
func (e *Engine) now() time.Time {
e.mu.RLock()
clock := e.clock
e.mu.RUnlock()
if clock != nil {
return clock().UTC()
}
return time.Now().UTC()
}
// Check evaluates one subject/action/resource request.
func (e *Engine) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
normalized, facts := e.normalizeRequest(request)
@ -307,13 +326,19 @@ func (e *Engine) envelope(request api.CheckRequest, expectation api.DecisionExpe
"matched_relationship": facts.matchedRelationship,
},
Provenance: api.DecisionProvenance{
Evaluator: "flex-auth/local",
Mode: "standalone",
PolicyPackage: e.policy.Metadata.ID,
PolicyVersion: e.policy.Metadata.Version,
Evaluator: "flex-auth/local",
Mode: "standalone",
PolicyPackage: e.policy.Metadata.ID,
PolicyVersion: e.policy.Metadata.Version,
PolicyPackageDigest: e.policy.Digest(),
RegistrySnapshotDigest: e.store.Digest(),
},
Caring: e.caringDecisionMetadata(facts.descriptor, expectation.ConformanceFindings),
}
api.CompleteDecision(&envelope, request, api.DecisionCompletion{
AllowTTL: e.policy.Metadata.AllowTTL,
Now: e.now(),
})
envelope.ID = decisionID(e.policy.Metadata, request, envelope)
return envelope
}