;; lib/acl/schema.sx — ACL sorts and EDB predicate vocabulary.
;;
;; Datalog is untyped; this module is the schema-as-data layer. It declares
;; the subject/resource/action sorts and the arity of every EDB predicate the
;; ACL engine recognises, plus light validators. Facts that pass these checks
;; are well-formed inputs to lib/acl/engine.sx.

(define acl-subject-kinds (quote (user group role service)))
(define acl-resource-kinds (quote (page post thread peer)))

;; Actions are open-ended (a grant may name any action symbol), but these are
;; the platform's well-known verbs.
(define acl-actions (quote (read edit comment moderate federate)))

;; EDB predicate name -> arity.
;;   Phase 1: actor/resource/grant/deny.
;;   Phase 2: member_of (subject->group/role), child_of (resource->parent),
;;            role_grant (role->action,resource).
;;   Phase 4: peer (addr->kind), trust (peer->level),
;;            delegate (peer->subj,action,resource), level_covers (level->action).
(define acl-edb-arity {:role_grant 3 :child_of 2 :trust 2 :peer 2 :actor 2 :level_covers 2 :delegate 4 :member_of 2 :deny 3 :grant 3 :resource 2})

(define
  acl-member?
  (fn
    (x xs)
    (cond
      ((= (len xs) 0) false)
      ((= (first xs) x) true)
      (else (acl-member? x (rest xs))))))

(define acl-subject-kind? (fn (k) (acl-member? k acl-subject-kinds)))

(define acl-resource-kind? (fn (k) (acl-member? k acl-resource-kinds)))

(define acl-known-action? (fn (a) (acl-member? a acl-actions)))

;; A fact is a list whose head is a predicate symbol. Valid when the predicate
;; is known and the argument count matches the declared arity.
(define
  acl-fact-valid?
  (fn
    (f)
    (and
      (list? f)
      (> (len f) 0)
      (symbol? (first f))
      (let
        ((pred (symbol->string (first f))))
        (and
          (has-key? acl-edb-arity pred)
          (= (- (len f) 1) (get acl-edb-arity pred)))))))

;; Return the sublist of facts that fail acl-fact-valid?. Empty list means the
;; whole set is well-formed. acl-build-db stays lenient (Datalog accepts any
;; tuple, and custom action symbols are allowed); callers opt in to checking.
(define
  acl-validate-facts
  (fn
    (facts)
    (let
      ((bad (list)))
      (do
        (for-each
          (fn (f) (when (not (acl-fact-valid? f)) (append! bad f)))
          facts)
        bad))))

(define
  acl-facts-valid?
  (fn (facts) (= (len (acl-validate-facts facts)) 0)))
