package taskexecution import ( "context" "encoding/json" "errors" "github.com/netkingdom/access-engine/internal/callerauth" "github.com/netkingdom/access-engine/pkg/api" "time" ) var ErrServiceInput = errors.New("service task execution input refused") const ServicePilotMappingVersion = "proposed-service-task-caller/v1" const ServicePilotPrincipal = "system:serviceaccount:secrets-engine:secrets-engine" // ServicePilotInputs is source preparation only. It installs the explicit // proposed fixed subject-to-transport mapping, never a request-selected mapping. // No constructor parameter can select warn/disabled mode or another subject. type ServicePilotInputs struct{ auth *callerauth.Authenticator } func NewServicePilotInputs(reviewer callerauth.TokenReviewer) (*ServicePilotInputs, error) { a, e := callerauth.New(callerauth.ModeEnforce, reviewer, "flex-auth", map[string]string{"secrets-engine": ServicePilotPrincipal}, nil) if e != nil { return nil, ErrServiceInput } return &ServicePilotInputs{auth: a}, nil } // ServiceInput cannot be fabricated through exported fields. It proves only // source input preparation through TokenReview, not approval provenance/consume // or execution authority. Default zero value is not an authenticated input. type ServiceInput struct { serialized []byte caller callerauth.Record } func (s ServiceInput) Request() (api.CheckRequest, error) { if s.caller.Mode != callerauth.ModeEnforce || s.caller.Principal != ServicePilotPrincipal || len(s.serialized) == 0 { return api.CheckRequest{}, ErrServiceInput } var r api.CheckRequest if json.Unmarshal(s.serialized, &r) != nil { return r, ErrServiceInput } return r, nil } func (s ServiceInput) Caller() callerauth.Record { return s.caller } // DeclaredServiceBinding is public immutable metadata to bind at approval issue. // It is not authentication evidence; Prepare independently verifies its principal. func DeclaredServiceBinding() map[string]any { return map[string]any{"version": ServicePilotMappingVersion, "transport_principal": ServicePilotPrincipal, "subject_id": "secrets-engine", "subject_type": "service", "tenant": "tenant:platform"} } func (s *ServicePilotInputs) Prepare(ctx context.Context, authorization string, r api.CheckRequest, now time.Time) (ServiceInput, error) { refuse := func() (ServiceInput, error) { return ServiceInput{}, ErrServiceInput } if s == nil || s.auth == nil || now.IsZero() || r.Action != "execute-ccr" || r.Tenant != "tenant:platform" || r.Subject.ID != "secrets-engine" || r.Subject.Type != "service" || r.Resource.System != "secrets-engine" || r.Resource.Type != "secret-catalog-lane" || r.Resource.Attributes["stage"] != "prod" || r.Context["human_control"] != true { return refuse() } for _, key := range []string{"task_execution_projection", "initiator", "represented_principal", "delegation", "verified", "owner_admission"} { if _, found := r.Context[key]; found { return refuse() } } if _, found := r.Subject.Attributes["principal"]; found { return refuse() } supplied, err := json.Marshal(r.Context["service_actor_binding"]) if err != nil { return refuse() } wanted, _ := json.Marshal(DeclaredServiceBinding()) if string(supplied) != string(wanted) { return refuse() } identity, err := s.auth.Authorize(ctx, authorization, []string{r.Resource.System}) if err != nil { return refuse() } record := s.auth.Record(identity) if record.Mode != callerauth.ModeEnforce || record.Principal != ServicePilotPrincipal || record.Audience != "flex-auth" || record.NotAfter.IsZero() || !record.NotAfter.After(now) { return refuse() } c, ok := r.Context["execution_contract"].(map[string]any) if !ok || c["contract_version"] != "SECRETS-EXEC-20261011-v1" || c["operation"] != "metadata-apply-ccr" || c["execution_actor"] != "service:secrets-engine" { return refuse() } deadline, ok := c["operation_deadline"].(string) if !ok { return refuse() } end, err := time.Parse(time.RFC3339Nano, deadline) if err != nil || !end.After(now) || end.After(record.NotAfter) { return refuse() } raw, err := json.Marshal(r) if err != nil { return refuse() } return ServiceInput{serialized: raw, caller: record}, nil }