package ai import ( "encoding/json" "errors" "fmt" "time" "git.ipao.vip/rogee/go-sip/internal/contract" ) type Authorization struct { AuthorizationID string `json:"authorization_id"` TenantID string `json:"tenant_id"` TenantKey string `json:"tenant_key"` AgentVersionID string `json:"agent_version_id"` ConfigSHA256 string `json:"config_sha256"` Mode Mode `json:"mode"` IssuedAt string `json:"issued_at"` ExpiresAt string `json:"expires_at"` Source string `json:"source"` CredentialRefs map[string]string `json:"credential_refs"` AllowedEgressPoolIDs []string `json:"allowed_egress_pool_ids"` Revoked bool `json:"revoked"` RevocationReason string `json:"revocation_reason"` } func ValidateAuthorization(raw []byte, snapshot Snapshot, tenantID, tenantKey, egressPoolID string, now time.Time) (Authorization, error) { if err := contract.ValidateSourceSchema("ai-authorization.schema.json", raw); err != nil { return Authorization{}, err } var authorization Authorization if err := json.Unmarshal(raw, &authorization); err != nil { return Authorization{}, err } if authorization.Revoked { return Authorization{}, errors.New("AI authorization is revoked") } if authorization.TenantID != tenantID || authorization.TenantKey != tenantKey { return Authorization{}, errors.New("AI authorization tenant binding mismatch") } if authorization.AgentVersionID != snapshot.AgentVersionID || authorization.ConfigSHA256 != snapshot.Digest || authorization.Mode != snapshot.Mode { return Authorization{}, errors.New("AI authorization does not match immutable snapshot") } issuedAt, err := time.Parse(time.RFC3339, authorization.IssuedAt) if err != nil { return Authorization{}, fmt.Errorf("parse AI authorization issued_at: %w", err) } expiresAt, err := time.Parse(time.RFC3339, authorization.ExpiresAt) if err != nil { return Authorization{}, fmt.Errorf("parse AI authorization expires_at: %w", err) } if !issuedAt.Before(expiresAt) || now.Before(issuedAt) || !now.Before(expiresAt) { return Authorization{}, errors.New("AI authorization is outside its validity window") } if egressPoolID != "" { allowed := false for _, value := range authorization.AllowedEgressPoolIDs { if value == egressPoolID { allowed = true break } } if !allowed { return Authorization{}, errors.New("AI authorization does not allow this egress pool") } } return authorization, nil }