// Package ect implements Execution Context Tokens (ECTs) per // draft-nennemann-wimse-execution-context-00. package ect import "time" // ECTType is the JOSE typ value for ECTs. const ECTType = "wimse-exec+jwt" // PolDecision values per Section 4.2.3. const ( PolDecisionApproved = "approved" PolDecisionRejected = "rejected" PolDecisionPendingHumanReview = "pending_human_review" ) // Payload holds ECT JWT claims per Section 4.2. type Payload struct { // Standard JWT claims (required unless noted) Iss string `json:"iss"` // REQUIRED: issuer, SPIFFE ID Sub string `json:"sub,omitempty"` Aud Audience `json:"aud"` // REQUIRED Iat int64 `json:"iat"` // REQUIRED: NumericDate Exp int64 `json:"exp"` // REQUIRED Jti string `json:"jti"` // REQUIRED: UUID // Execution context (Section 4.2.2) Wid string `json:"wid,omitempty"` // OPTIONAL: workflow ID, UUID Tid string `json:"tid"` // REQUIRED: task ID, UUID ExecAct string `json:"exec_act"` // REQUIRED Par []string `json:"par"` // REQUIRED: parent task IDs // Policy evaluation (Section 4.2.3) Pol string `json:"pol"` // REQUIRED PolDecision string `json:"pol_decision"` // REQUIRED: approved | rejected | pending_human_review PolEnforcer string `json:"pol_enforcer,omitempty"` // OPTIONAL PolTimestamp int64 `json:"pol_timestamp,omitempty"` // OPTIONAL // Data integrity (Section 4.2.4) InpHash string `json:"inp_hash,omitempty"` OutHash string `json:"out_hash,omitempty"` InpClassification string `json:"inp_classification,omitempty"` // Task metadata (Section 4.2.5) ExecTimeMs int `json:"exec_time_ms,omitempty"` RegulatedDomain string `json:"regulated_domain,omitempty"` ModelVersion string `json:"model_version,omitempty"` WitnessedBy []string `json:"witnessed_by,omitempty"` // Compensation (Section 4.2.6) CompensationRequired bool `json:"compensation_required,omitempty"` CompensationReason string `json:"compensation_reason,omitempty"` // Extensions (Section 4.2.7) Ext map[string]interface{} `json:"ext,omitempty"` } // Audience is aud claim: string or array of strings. type Audience []string // MarshalJSON encodes aud as single string if one element, else array. func (a Audience) MarshalJSON() ([]byte, error) { if len(a) == 1 { return marshalJSONString(a[0]), nil } return marshalJSONStringArray(a), nil } // UnmarshalJSON decodes aud from string or array. func (a *Audience) UnmarshalJSON(data []byte) error { return unmarshalAudience(data, a) } // ValidPolDecision returns true if s is a registered pol_decision value. func ValidPolDecision(s string) bool { return s == PolDecisionApproved || s == PolDecisionRejected || s == PolDecisionPendingHumanReview } // ContainsAudience returns true if verifierID is in the audience. func (p *Payload) ContainsAudience(verifierID string) bool { for _, id := range p.Aud { if id == verifierID { return true } } return false } // IATTime returns p.Iat as time.Time. func (p *Payload) IATTime() time.Time { return time.Unix(p.Iat, 0) } // ExpTime returns p.Exp as time.Time. func (p *Payload) ExpTime() time.Time { return time.Unix(p.Exp, 0) }