Bind service task HTTP input to authenticated caller before evaluation
Some checks failed
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) Failing after 1m1s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
tegwick 2026-10-16 16:25:55 +02:00
parent 919ce25a37
commit 4f6946bbb3
8 changed files with 590 additions and 9 deletions

View file

@ -20,6 +20,7 @@ import (
"github.com/netkingdom/access-engine/internal/policy"
"github.com/netkingdom/access-engine/internal/registry"
"github.com/netkingdom/access-engine/internal/sign"
"github.com/netkingdom/access-engine/internal/taskexecution"
"github.com/netkingdom/access-engine/pkg/api"
)
@ -153,9 +154,44 @@ func (e *Engine) decide(ctx context.Context, request api.CheckRequest) (api.Deci
return envelope, nil
}
// IsServicePilot selects the existing exact source package, never a request flag.
func (e *Engine) IsServicePilot() bool {
return e.policy.Metadata.ID == taskexecution.ServicePilotPackageID && e.policy.Metadata.Version == taskexecution.ServicePilotPackageVersion
}
// ErrServicePilotUnavailable withholds any result that is not this B1 slice's
// signed DENY. Provider joins and executable eligibility remain unimplemented.
var ErrServicePilotUnavailable = errors.New("service pilot source path unavailable")
// CheckServicePilot records only a signed DENY after authenticating the exact
// immutable service input. It does not validate Approval facts or grant an act.
func (e *Engine) CheckServicePilot(ctx context.Context, input taskexecution.ServiceInput) (api.DecisionEnvelope, error) {
if !e.IsServicePilot() {
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
}
request, err := input.RequestAt(e.now())
if err != nil {
return api.DecisionEnvelope{}, err
}
envelope, err := e.decide(callerauth.WithRecord(ctx, input.Caller()), request)
if err != nil {
return api.DecisionEnvelope{}, err
}
if envelope.Effect != api.DecisionEffectDeny || envelope.Signature == nil || envelope.Signature.Mode != api.EnvelopeSignatureSigned {
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
}
if err := e.recordDecisions([]api.DecisionEnvelope{envelope}); err != nil {
return api.DecisionEnvelope{}, err
}
return envelope, nil
}
// Check evaluates one subject/action/resource request. The decision is
// released only after its required record commits (GH-DEC-2026-024).
func (e *Engine) Check(ctx context.Context, request api.CheckRequest) (api.DecisionEnvelope, error) {
if e.IsServicePilot() || request.Action == "execute-ccr" {
return api.DecisionEnvelope{}, ErrServicePilotUnavailable
}
decision, err := e.decide(ctx, request)
if err != nil {
return api.DecisionEnvelope{}, err
@ -169,6 +205,9 @@ func (e *Engine) Check(ctx context.Context, request api.CheckRequest) (api.Decis
// BatchCheck evaluates one subject/action/context tuple against resources in
// request order, and commits the whole batch in one durable write.
func (e *Engine) BatchCheck(ctx context.Context, request api.BatchCheckRequest) ([]api.DecisionEnvelope, error) {
if e.IsServicePilot() || request.Action == "execute-ccr" {
return nil, ErrServicePilotUnavailable
}
decisions := make([]api.DecisionEnvelope, 0, len(request.Resources))
for _, resource := range request.Resources {
decision, err := e.decide(ctx, api.CheckRequest{
@ -193,6 +232,9 @@ func (e *Engine) BatchCheck(ctx context.Context, request api.BatchCheckRequest)
// ListAllowed evaluates candidate resources and returns only allow decisions.
func (e *Engine) ListAllowed(ctx context.Context, request ListAllowedRequest) ([]api.DecisionEnvelope, error) {
if e.IsServicePilot() || request.Action == "execute-ccr" {
return nil, ErrServicePilotUnavailable
}
candidates := e.store.ResourceRefs(request.System, request.ResourceType)
decisions := make([]api.DecisionEnvelope, 0, len(candidates))
for _, resource := range candidates {