generated from coulomb/repo-seed
Closes FLEX-WP-0005 T04. Validates ADR-003's commitment to shape the
standalone core for cheap Topaz adapter work.
Spike output:
- docs/topaz-mapping-spike.md — vocabulary map (subject, group, tenant,
knowledge_base, document, plus parent / owner_team / reader / steward /
member relations), Rego module shape, decision envelope, wire-protocol
ranking (gRPC primary, REST fallback, embedding rejected), schema
restatement recommendation, implications for FLEX-WP-0002 / 0004.
- examples/topaz/ — runnable docker-compose deploying Topaz with the
flex-auth-shaped manifest. seed and probe one-shots cover three
scenarios: alice (steward) allow, bob (group→reader) allow, eve
(outsider) deny. End-to-end green on 2026-05-16:
probe: steward-allow OK (check=true)
probe: reader-allow OK (check=true)
probe: outsider-deny OK (check=false)
probe: all checks passed
Key findings recorded as Implementation Notes in the spike doc:
- Rego input contract bridging (Topaz raw shape ↔ flex-auth canonical
shape) is adapter scope, not core scope.
- Topaz identity objects are a Topaz convention; the adapter
materializes them at directory import time.
- Directory-only permission resolution is sufficient for the common
case; Rego is reserved for context-dependent decisions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
65 lines
1.8 KiB
Rego
65 lines
1.8 KiB
Rego
package flexauth.markitect.documents
|
|
|
|
import future.keywords.if
|
|
import future.keywords.in
|
|
|
|
# This module is the Rego extracted from a flex-auth Rego-in-Markdown
|
|
# policy package (ADR-002). Identical bytes ship to the standalone
|
|
# evaluator and to Topaz; only the resolution of ds.* differs.
|
|
#
|
|
# Decision shape per ADR-002:
|
|
# decision := {"effect": "...", "reason": "...", "obligations": [...]}
|
|
# flex-auth wraps this into the canonical decision envelope.
|
|
|
|
default decision := {"effect": "deny", "reason": "no_matching_rule"}
|
|
|
|
# Reader on the document (direct or via group, or inherited from the
|
|
# parent knowledge_base) is allowed to read/query/search.
|
|
decision := {"effect": "allow", "reason": "reader_relation"} if {
|
|
input.action in {"read", "query", "search"}
|
|
input.resource.type == "document"
|
|
is_reader
|
|
}
|
|
|
|
# A steward on the document or parent may always read and may also
|
|
# export (which carries an audit-export obligation).
|
|
decision := {"effect": "allow", "reason": "steward_role"} if {
|
|
input.action in {"read", "query", "search"}
|
|
input.resource.type == "document"
|
|
is_steward
|
|
}
|
|
|
|
decision := {
|
|
"effect": "allow",
|
|
"reason": "steward_export",
|
|
"obligations": [{"type": "record_export_receipt"}],
|
|
} if {
|
|
input.action == "export"
|
|
input.resource.type == "document"
|
|
is_steward
|
|
}
|
|
|
|
# Helpers — these consult the directory shim (standalone) or Topaz's
|
|
# ds.* builtins (delegated). The standalone evaluator registers
|
|
# ds.check_relation / ds.check_permission with identical signatures.
|
|
|
|
is_reader if {
|
|
ds.check_relation({
|
|
"object_type": "document",
|
|
"object_id": input.resource.id,
|
|
"relation": "reader",
|
|
"subject_type": "user",
|
|
"subject_id": input.subject.id,
|
|
})
|
|
}
|
|
|
|
is_steward if {
|
|
ds.check_relation({
|
|
"object_type": "document",
|
|
"object_id": input.resource.id,
|
|
"relation": "steward",
|
|
"subject_type": "user",
|
|
"subject_id": input.subject.id,
|
|
})
|
|
}
|