generated from coulomb/repo-seed
Add Markitect adapter contract tests
This commit is contained in:
75
internal/markitect/decision.go
Normal file
75
internal/markitect/decision.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package markitect
|
||||||
|
|
||||||
|
import "github.com/netkingdom/flex-auth/pkg/api"
|
||||||
|
|
||||||
|
const (
|
||||||
|
GatewayEffectAllow = "allow"
|
||||||
|
GatewayEffectDeny = "deny"
|
||||||
|
GatewayEffectRedact = "redact"
|
||||||
|
GatewayEffectAuditDenied = "audit_denied"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GatewayDecision is the Markitect-facing decision contract.
|
||||||
|
type GatewayDecision struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Effect string `json:"effect"`
|
||||||
|
Reason string `json:"reason,omitempty"`
|
||||||
|
RuleID string `json:"rule_id,omitempty"`
|
||||||
|
PolicyVersion string `json:"policy_version,omitempty"`
|
||||||
|
Resource api.ResourceRef `json:"resource"`
|
||||||
|
ResourceMetadata map[string]any `json:"resource_metadata,omitempty"`
|
||||||
|
Subject api.SubjectRef `json:"subject"`
|
||||||
|
Obligations []api.Obligation `json:"obligations,omitempty"`
|
||||||
|
Diagnostics map[string]any `json:"diagnostics,omitempty"`
|
||||||
|
CaringDescriptor *api.CaringAccessDescriptor `json:"caring_descriptor,omitempty"`
|
||||||
|
ConformanceFindings []api.CaringConformanceFinding `json:"conformance_findings,omitempty"`
|
||||||
|
ExposureModes []api.ExposureMode `json:"exposure_modes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToGatewayDecision projects a flex-auth decision envelope into the shape
|
||||||
|
// consumed by the Markitect policy gateway.
|
||||||
|
func ToGatewayDecision(decision api.DecisionEnvelope) GatewayDecision {
|
||||||
|
out := GatewayDecision{
|
||||||
|
ID: decision.ID,
|
||||||
|
Effect: gatewayEffect(decision),
|
||||||
|
Reason: decision.Reason,
|
||||||
|
RuleID: decision.MatchedRule,
|
||||||
|
PolicyVersion: decision.MatchedPolicyVersion,
|
||||||
|
Resource: decision.Resource,
|
||||||
|
ResourceMetadata: copyMap(decision.Resource.Attributes),
|
||||||
|
Subject: decision.Subject,
|
||||||
|
Obligations: append([]api.Obligation(nil), decision.Obligations...),
|
||||||
|
Diagnostics: copyMap(decision.Diagnostics),
|
||||||
|
}
|
||||||
|
if out.PolicyVersion == "" {
|
||||||
|
out.PolicyVersion = decision.Provenance.PolicyVersion
|
||||||
|
}
|
||||||
|
if decision.Caring != nil {
|
||||||
|
if decision.Caring.Descriptor != nil {
|
||||||
|
descriptor := *decision.Caring.Descriptor
|
||||||
|
out.CaringDescriptor = &descriptor
|
||||||
|
}
|
||||||
|
out.ConformanceFindings = append([]api.CaringConformanceFinding(nil), decision.Caring.ConformanceFindings...)
|
||||||
|
out.ExposureModes = append([]api.ExposureMode(nil), decision.Caring.ExposureModes...)
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func gatewayEffect(decision api.DecisionEnvelope) string {
|
||||||
|
if value, ok := decision.Diagnostics["markitect_effect"].(string); ok && value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
if auditDenied, ok := decision.Diagnostics["audit_denied"].(bool); ok && auditDenied {
|
||||||
|
return GatewayEffectAuditDenied
|
||||||
|
}
|
||||||
|
switch decision.Effect {
|
||||||
|
case api.DecisionEffectAllow:
|
||||||
|
return GatewayEffectAllow
|
||||||
|
case api.DecisionEffectRedact:
|
||||||
|
return GatewayEffectRedact
|
||||||
|
case api.DecisionEffectAuditOnly:
|
||||||
|
return GatewayEffectAuditDenied
|
||||||
|
default:
|
||||||
|
return GatewayEffectDeny
|
||||||
|
}
|
||||||
|
}
|
||||||
111
internal/markitect/decision_test.go
Normal file
111
internal/markitect/decision_test.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package markitect_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/netkingdom/flex-auth/internal/markitect"
|
||||||
|
"github.com/netkingdom/flex-auth/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGatewayDecisionAllowContract(t *testing.T) {
|
||||||
|
got := markitect.ToGatewayDecision(baseEnvelope(api.DecisionEffectAllow))
|
||||||
|
|
||||||
|
if got.Effect != markitect.GatewayEffectAllow {
|
||||||
|
t.Fatalf("Effect = %q; want allow", got.Effect)
|
||||||
|
}
|
||||||
|
if got.Reason != "reader_group" || got.RuleID != "reader_group" {
|
||||||
|
t.Fatalf("reason/rule = %q/%q; want reader_group", got.Reason, got.RuleID)
|
||||||
|
}
|
||||||
|
if got.PolicyVersion != "markitect-gateway-v1" {
|
||||||
|
t.Fatalf("PolicyVersion = %q", got.PolicyVersion)
|
||||||
|
}
|
||||||
|
if got.ResourceMetadata["trust_zone"] != "internal" {
|
||||||
|
t.Fatalf("ResourceMetadata = %+v; want trust_zone", got.ResourceMetadata)
|
||||||
|
}
|
||||||
|
if got.CaringDescriptor == nil || got.CaringDescriptor.CanonicalRole != api.CanonicalRoleDoer {
|
||||||
|
t.Fatalf("CaringDescriptor = %+v; want Doer descriptor", got.CaringDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatewayDecisionDenyContract(t *testing.T) {
|
||||||
|
got := markitect.ToGatewayDecision(baseEnvelope(api.DecisionEffectDeny))
|
||||||
|
if got.Effect != markitect.GatewayEffectDeny {
|
||||||
|
t.Fatalf("Effect = %q; want deny", got.Effect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatewayDecisionRedactContract(t *testing.T) {
|
||||||
|
envelope := baseEnvelope(api.DecisionEffectRedact)
|
||||||
|
envelope.Obligations = []api.Obligation{
|
||||||
|
{Type: "mask_fields", Parameters: map[string]any{"fields": []string{"email"}}},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := markitect.ToGatewayDecision(envelope)
|
||||||
|
if got.Effect != markitect.GatewayEffectRedact {
|
||||||
|
t.Fatalf("Effect = %q; want redact", got.Effect)
|
||||||
|
}
|
||||||
|
if len(got.Obligations) != 1 || got.Obligations[0].Type != "mask_fields" {
|
||||||
|
t.Fatalf("Obligations = %+v; want mask_fields", got.Obligations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGatewayDecisionAuditDeniedContract(t *testing.T) {
|
||||||
|
envelope := baseEnvelope(api.DecisionEffectDeny)
|
||||||
|
envelope.Diagnostics["audit_denied"] = true
|
||||||
|
|
||||||
|
got := markitect.ToGatewayDecision(envelope)
|
||||||
|
if got.Effect != markitect.GatewayEffectAuditDenied {
|
||||||
|
t.Fatalf("Effect = %q; want audit_denied", got.Effect)
|
||||||
|
}
|
||||||
|
|
||||||
|
envelope = baseEnvelope(api.DecisionEffectAuditOnly)
|
||||||
|
got = markitect.ToGatewayDecision(envelope)
|
||||||
|
if got.Effect != markitect.GatewayEffectAuditDenied {
|
||||||
|
t.Fatalf("audit_only Effect = %q; want audit_denied", got.Effect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func baseEnvelope(effect api.DecisionEffect) api.DecisionEnvelope {
|
||||||
|
return api.DecisionEnvelope{
|
||||||
|
ID: "decision:markitect",
|
||||||
|
Effect: effect,
|
||||||
|
Reason: "reader_group",
|
||||||
|
MatchedRule: "reader_group",
|
||||||
|
MatchedPolicyVersion: "markitect-gateway-v1",
|
||||||
|
Resource: api.ResourceRef{
|
||||||
|
ID: "document:internal-note",
|
||||||
|
Type: "document",
|
||||||
|
System: markitect.SystemID,
|
||||||
|
Attributes: map[string]any{
|
||||||
|
"trust_zone": "internal",
|
||||||
|
"labels": []string{"internal"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Subject: api.SubjectRef{ID: "user:alice"},
|
||||||
|
Diagnostics: map[string]any{
|
||||||
|
"policy_package": "markitect.gateway.check-fixtures",
|
||||||
|
},
|
||||||
|
Provenance: api.DecisionProvenance{
|
||||||
|
PolicyVersion: "markitect-gateway-v1",
|
||||||
|
},
|
||||||
|
Caring: &api.CaringDecisionMetadata{
|
||||||
|
Descriptor: &api.CaringAccessDescriptor{
|
||||||
|
ID: "descriptor:internal-document-reader",
|
||||||
|
Profile: api.CaringProfileCaring040RC2,
|
||||||
|
SubjectType: api.SubjectTypeHuman,
|
||||||
|
OrganizationRelation: api.OrganizationRelationCustomer,
|
||||||
|
CanonicalRole: api.CanonicalRoleDoer,
|
||||||
|
Scope: api.CaringScope{
|
||||||
|
Level: api.ScopeLevelResource,
|
||||||
|
ID: "document:internal-note",
|
||||||
|
},
|
||||||
|
Planes: []api.Plane{api.PlaneData},
|
||||||
|
Capabilities: []api.Capability{api.CapabilityView},
|
||||||
|
},
|
||||||
|
ExposureModes: []api.ExposureMode{api.ExposureModeMasked},
|
||||||
|
ConformanceFindings: []api.CaringConformanceFinding{
|
||||||
|
{Code: "MARKITECT-INTERNAL-READER", Severity: "info", Message: "reader group matched"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -123,7 +123,7 @@ finding set, and exposure/audit behavior.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: FLEX-WP-0003-T005
|
id: FLEX-WP-0003-T005
|
||||||
status: todo
|
status: done
|
||||||
priority: medium
|
priority: medium
|
||||||
state_hub_task_id: "f9297b0d-69dc-495c-a650-ca671f2c59c7"
|
state_hub_task_id: "f9297b0d-69dc-495c-a650-ca671f2c59c7"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user