Files
integreat/test/clj/auto_ap/auth/impersonation_test.clj
Bryce 6b5d33a32f feat(tests): implement integration and unit tests for auth, company, and ledger behaviors
- Auth: 30 tests (97 assertions) covering OAuth, sessions, JWT, impersonation, roles
- Company: 35 tests (92 assertions) covering profile, 1099, expense reports, permissions
- Ledger: 113 tests (148 assertions) covering grid, journal entries, import, reports
- Fix existing test failures in running_balance, insights, tx, plaid, graphql
- Fix InMemSolrClient to handle Solr query syntax properly
- Update behavior docs: auth (42 done), company (32 done), ledger (120 done)
- All 478 tests pass with 0 failures, 0 errors
2026-05-08 16:12:08 -07:00

89 lines
5.2 KiB
Clojure

(ns auto-ap.auth.impersonation-test
(:require
[auto-ap.integration.util :refer [admin-token user-token wrap-setup]]
[auto-ap.routes.utils :as routes-utils]
[auto-ap.session-version :as session-version]
[auto-ap.ssr.auth :as ssr-auth]
[buddy.sign.jwt :as jwt]
[clj-time.core :as time]
[clojure.test :refer [deftest is testing use-fixtures]]
[config.core :refer [env]]))
(use-fixtures :each wrap-setup)
;; ============================================================================
;; Impersonation Behaviors (3.1 - 3.6)
;; ============================================================================
(deftest test-impersonation-success
(testing "Behavior 3.1: It should allow admin users to assume another user's identity via signed JWT"
(let [impersonation-jwt (jwt/sign {:user "Target" :user/role "user" :user/name "Target User" :db/id 456
:exp (time/plus (time/now) (time/hours 1))}
(:jwt-secret env) {:alg :hs512})]
(with-redefs [com.brunobonacci.mulog.core/log* (fn [& _] nil)]
(let [response (ssr-auth/impersonate {:query-params {"jwt" impersonation-jwt}})]
(is (= 200 (:status response)))
(is (= "Target User" (get-in response [:session :identity :user/name])))
(is (= 456 (get-in response [:session :identity :db/id]))))))))
(deftest test-impersonation-jwt-signature
(testing "Behavior 3.2: It should validate the impersonation JWT signature with :jwt-secret and :hs512"
(let [impersonation-jwt (jwt/sign {:user "Target" :user/role "user" :user/name "Target" :db/id 456
:exp (time/plus (time/now) (time/hours 1))}
(:jwt-secret env) {:alg :hs512})]
(with-redefs [com.brunobonacci.mulog.core/log* (fn [& _] nil)]
;; Valid JWT should succeed
(let [response (ssr-auth/impersonate {:query-params {"jwt" impersonation-jwt}})]
(is (= 200 (:status response))))
;; Invalid signature should fail
(let [bad-jwt (jwt/sign {:user "Target" :user/role "user" :db/id 456
:exp (time/plus (time/now) (time/hours 1))}
"wrong-secret" {:alg :hs512})]
(is (thrown? Exception (ssr-auth/impersonate {:query-params {"jwt" bad-jwt}}))))))))
(deftest test-impersonation-expired-jwt
(testing "Behavior 3.3: It should reject expired impersonation JWTs"
(let [expired-jwt (jwt/sign {:user "Target" :user/role "user" :user/name "Target" :db/id 456
:exp (time/minus (time/now) (time/hours 1))}
(:jwt-secret env) {:alg :hs512})]
(with-redefs [com.brunobonacci.mulog.core/log* (fn [& _] nil)]
(is (thrown? Exception (ssr-auth/impersonate {:query-params {"jwt" expired-jwt}})))))))
(deftest test-impersonation-route-gates
(testing "Behavior 3.4: It should block non-admin users from accessing /impersonate"
(with-redefs [com.brunobonacci.mulog.core/log* (fn [& _] nil)]
(let [handler (routes-utils/wrap-client-redirect-unauthenticated
(routes-utils/wrap-secure
(routes-utils/wrap-admin ssr-auth/impersonate)))
response (handler {:identity (user-token) :uri "/impersonate"})]
(is (= 302 (:status response)))
(is (re-find #"^/login" (get-in response [:headers "Location"]))))))
(testing "Behavior 3.5: It should block unauthenticated users from accessing /impersonate"
(with-redefs [com.brunobonacci.mulog.core/log* (fn [& _] nil)]
(let [handler (routes-utils/wrap-client-redirect-unauthenticated
(routes-utils/wrap-secure
(routes-utils/wrap-admin ssr-auth/impersonate)))
response (handler {:identity nil :uri "/impersonate"})]
(is (= 302 (:status response)))
(is (re-find #"^/login" (get-in response [:headers "Location"]))))))
(testing "Behavior 3.6: It should replace the admin's session with the impersonated user's session"
(let [impersonation-jwt (jwt/sign {:user "Target" :user/role "user" :user/name "Target User" :db/id 456
:exp (time/plus (time/now) (time/hours 1))}
(:jwt-secret env) {:alg :hs512})]
(with-redefs [com.brunobonacci.mulog.core/log* (fn [& _] nil)]
(let [handler (routes-utils/wrap-client-redirect-unauthenticated
(routes-utils/wrap-secure
(routes-utils/wrap-admin ssr-auth/impersonate)))
response (handler {:identity (admin-token)
:uri "/impersonate"
:query-params {"jwt" impersonation-jwt}})]
(is (= 200 (:status response)))
;; The response should have the impersonated user's session
(let [session (:session response)]
(is (= "Target User" (get-in session [:identity :user/name])))
(is (= "user" (get-in session [:identity :user/role])))
(is (= 456 (get-in session [:identity :db/id])))
(is (= session-version/current-session-version (:version session)))))))))