;; identity/tests/account.sx — the account-security surface: \"apps with ;; access\" (grants_for / identity:grants) plus \"disconnect this app\" ;; (revoke_app / identity:revoke_app). Completes the per-subject view+action ;; pair alongside sessions and history. (define id-acct-test-count 0) (define id-acct-test-pass 0) (define id-acct-test-fails (list)) (define id-acct-test (fn (name actual expected) (set! id-acct-test-count (+ id-acct-test-count 1)) (if (= actual expected) (set! id-acct-test-pass (+ id-acct-test-pass 1)) (append! id-acct-test-fails {:name name :expected expected :actual actual})))) (define ida-ev erlang-eval-ast) (define idanm (fn (v) (get v :name))) (identity-load-all!) ;; ── token-level grants_for ─────────────────────────────────────── (id-acct-test "grants_for lists a subject's active grants" (ida-ev "R = identity_tokens:start(),\n identity_tokens:issue(R, alice, web, read),\n identity_tokens:issue(R, alice, cli, write),\n identity_tokens:issue(R, bob, web, read),\n length(identity_tokens:grants_for(R, alice))") 2) (id-acct-test "grants_for excludes revoked grants" (ida-ev "R = identity_tokens:start(),\n {ok, A} = identity_tokens:issue(R, alice, web, read),\n identity_tokens:issue(R, alice, cli, write),\n identity_tokens:revoke(R, A),\n length(identity_tokens:grants_for(R, alice))") 1) (id-acct-test "grants_for is empty for a subject with none" (ida-ev "R = identity_tokens:start(),\n identity_tokens:issue(R, alice, web, read),\n length(identity_tokens:grants_for(R, ghost))") 0) (id-acct-test "each grant entry carries the client" (idanm (ida-ev "R = identity_tokens:start(),\n identity_tokens:issue(R, alice, web, read),\n case identity_tokens:grants_for(R, alice) of\n [{Client, _Scope}] -> Client;\n _ -> other\n end")) "web") ;; ── token-level revoke_app (\"disconnect this app\") ──────────────── (id-acct-test "revoke_app revokes all of a subject's grants for one client" (ida-ev "R = identity_tokens:start(),\n identity_tokens:issue(R, alice, web, read),\n identity_tokens:issue(R, alice, web, write),\n identity_tokens:issue(R, alice, cli, read),\n identity_tokens:revoke_app(R, alice, web),\n length(identity_tokens:grants_for(R, alice))") 1) (id-acct-test "revoke_app deactivates that client's tokens" (idanm (ida-ev "R = identity_tokens:start(),\n {ok, T} = identity_tokens:issue(R, alice, web, read),\n identity_tokens:revoke_app(R, alice, web),\n case identity_tokens:introspect(R, T) of\n {active, _, _, _} -> active;\n {inactive} -> inactive\n end")) "inactive") (id-acct-test "revoke_app leaves another subject's same-client grant intact" (ida-ev "R = identity_tokens:start(),\n identity_tokens:issue(R, alice, web, read),\n identity_tokens:issue(R, bob, web, read),\n identity_tokens:revoke_app(R, alice, web),\n length(identity_tokens:grants_for(R, bob))") 1) ;; ── facade-level grants + revoke_app ───────────────────────────── (id-acct-test "identity:grants lists apps a subject has logged into" (ida-ev "Svc = identity:start(),\n identity:login(Svc, alice, web, read),\n identity:login(Svc, alice, mobile, read),\n length(identity:grants(Svc, alice))") 2) (id-acct-test "identity:revoke_app disconnects one app, leaving the rest" (ida-ev "Svc = identity:start(),\n identity:login(Svc, alice, web, read),\n identity:login(Svc, alice, mobile, read),\n identity:revoke_app(Svc, alice, web),\n length(identity:grants(Svc, alice))") 1) (id-acct-test "identity:grants is per-subject" (ida-ev "Svc = identity:start(),\n identity:login(Svc, alice, web, read),\n identity:login(Svc, bob, web, read),\n length(identity:grants(Svc, bob))") 1) (id-acct-test "revoke_app is audited as a revoke" (idanm (ida-ev "Svc = identity:start(),\n identity:login(Svc, alice, web, read),\n identity:revoke_app(Svc, alice, web),\n case identity:history(Svc, alice) of\n [login, issue, revoke] -> audited;\n Other -> Other\n end")) "audited") (define id-acct-test-summary (str "account " id-acct-test-pass "/" id-acct-test-count))