acl: Phase 1 direct grants + deny-overrides, 24 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 56s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 56s
Datalog ACL layer (schema/facts/engine/api) over lib/datalog/. Direct grant permits unless explicit deny names same (S,A,R) — deny-overrides via stratified negation. Conformance wrapper + scoreboard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
29
lib/acl/engine.sx
Normal file
29
lib/acl/engine.sx
Normal file
@@ -0,0 +1,29 @@
|
||||
;; lib/acl/engine.sx — ACL ruleset + decision reducer over lib/datalog/.
|
||||
;;
|
||||
;; The engine is a thin layer: it owns the permit ruleset (SX data rules) and
|
||||
;; reduces a (subject, action, resource) decision to a Datalog query against a
|
||||
;; db built from EDB facts. The rule engine itself is Datalog's.
|
||||
;;
|
||||
;; Phase 1 policy — direct grants with deny-overrides:
|
||||
;;
|
||||
;; permit(S, A, R) :- grant(S, A, R), not deny(S, A, R).
|
||||
;;
|
||||
;; A grant permits unless an explicit deny names the same (S, A, R). Deny wins:
|
||||
;; the negated literal {:neg (deny S A R)} stratifies cleanly because deny is an
|
||||
;; EDB relation (no rule derives it), so the fixpoint is well-defined.
|
||||
|
||||
(define
|
||||
acl-phase1-rules
|
||||
(quote ((permit S A R <- (grant S A R) {:neg (deny S A R)}))))
|
||||
|
||||
;; Build a Datalog db from a list of EDB facts under the Phase 1 ruleset.
|
||||
(define acl-build-db (fn (facts) (dl-program-data facts acl-phase1-rules)))
|
||||
|
||||
;; Core decision: does the db permit subject S to perform action A on
|
||||
;; resource R? Reduces to a ground Datalog query on the derived `permit`
|
||||
;; relation — non-empty result means permitted.
|
||||
(define
|
||||
acl-permit?
|
||||
(fn
|
||||
(db subj act res)
|
||||
(> (len (dl-query db (list (quote permit) subj act res))) 0)))
|
||||
Reference in New Issue
Block a user