package taskexecution import ( "bytes" "context" "encoding/json" "errors" "github.com/netkingdom/access-engine/internal/callerauth" "github.com/netkingdom/access-engine/pkg/api" "io" "time" ) var ErrServiceInput = errors.New("service task execution input refused") const ServicePilotPackageID = "secrets-engine.task-execution.service-pilot" const ServicePilotPackageVersion = "v1" const ServicePilotMappingVersion = "proposed-service-task-caller/v1" const ServicePilotPrincipal = "system:serviceaccount:secrets-engine:secrets-engine" // ServicePilotInputs installs the exact B1 service input mapping for signed DENY. // It does not validate Approval facts or establish executable eligibility. // 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 } // RequestAt rechecks transport and operation lifetime at the runtime use site. // Request() remains a detached source snapshot, never an execution capability. func (s ServiceInput) RequestAt(now time.Time) (api.CheckRequest, error) { r, err := s.Request() if err != nil || now.IsZero() || s.caller.Audience != "flex-auth" || !s.caller.NotAfter.After(now) { return api.CheckRequest{}, ErrServiceInput } contract, ok := r.Context["execution_contract"].(map[string]any) if !ok { return api.CheckRequest{}, ErrServiceInput } value, ok := contract["operation_deadline"].(string) if !ok { return api.CheckRequest{}, ErrServiceInput } deadline, err := time.Parse(time.RFC3339Nano, value) if err != nil || !deadline.After(now) || deadline.After(s.caller.NotAfter) { return api.CheckRequest{}, ErrServiceInput } return r, nil } // DecodeServiceRequest reuses the owner strict JSON scanner for this bounded // task-only route. It never changes producer canonicalization or legacy input. func DecodeServiceRequest(reader io.Reader) (api.CheckRequest, error) { raw, err := io.ReadAll(io.LimitReader(reader, 524289)) var request api.CheckRequest if err != nil || len(raw) > 524288 || strictObject(raw, &request) != nil { return request, ErrServiceInput } decoder := json.NewDecoder(bytes.NewReader(raw)) decoder.DisallowUnknownFields() if decoder.Decode(&request) != nil { return api.CheckRequest{}, ErrServiceInput } return request, nil } // 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 }