Skip to content

Commit 4c2049a

Browse files
committed
✅ Improve authorization testing of the UI pages
Why: - The DMZ checks view permissions automatically, so the tests for individual pages don't need to check that. - Added permission checks to viewing printouts and settings pages. Uses the same code which determines whether the links to those pages are visible in the navigation, so it should not be possible to access the pages with a hand-crafted URL. - Made the error messages a more generic "Access denied" to avoid leaking information and to improve code reuse. The stack trace will anyway tell which permission check failed.
1 parent 1cec917 commit 4c2049a

16 files changed

+101
-140
lines changed

src/territory_bro/domain/dmz.clj

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,19 @@
6161
(when-not (auth/logged-in?)
6262
(http-response/unauthorized! "Not logged in")))
6363

64+
(defn access-denied! []
65+
(require-logged-in!) ; if the user is not logged in, first prompt them to log in - they might have access after logging in
66+
(http-response/forbidden! "Access denied"))
67+
6468
(defn- super-user? []
6569
(let [super-users (:super-users config/env)
6670
user auth/*user*]
6771
(or (contains? super-users (:user/id user))
6872
(contains? super-users (:sub user)))))
6973

7074
(defn sudo [session]
71-
(require-logged-in!)
7275
(when-not (super-user?)
73-
(http-response/forbidden! "Not super user"))
76+
(access-denied!))
7477
(log/info "Super user promotion")
7578
(assoc session ::sudo? true))
7679

@@ -147,9 +150,7 @@
147150
(dissoc :congregation/loans-csv-url)
148151
(dissoc :congregation/schema-name))
149152
congregation))
150-
(do
151-
(require-logged-in!)
152-
(http-response/forbidden! "No congregation access"))))
153+
(access-denied!)))
153154

154155
(defn list-congregations []
155156
(let [user-id (auth/current-user-id)]
@@ -195,9 +196,7 @@
195196
(if (= "demo" cong-id)
196197
(assoc territory :congregation/id "demo")
197198
(enrich-do-not-calls territory)))
198-
(do
199-
(require-logged-in!)
200-
(http-response/forbidden! "No territory access"))))
199+
(access-denied!)))
201200

202201
(defn list-territories [cong-id {:keys [fetch-loans?]}]
203202
(cond

src/territory_bro/ui/printouts_page.clj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858

5959
(defn model! [request]
6060
(let [cong-id (get-in request [:path-params :congregation])
61+
_ (when-not (dmz/view-printouts-page? cong-id)
62+
(dmz/access-denied!))
6163
congregation (dmz/get-congregation cong-id)
6264
regions (->> (dmz/list-regions cong-id)
6365
(sort-by (comp str :region/name)

src/territory_bro/ui/settings_page.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121

2222
(defn model! [request]
2323
(let [cong-id (get-in request [:path-params :congregation])
24-
congregation (if (= "demo" cong-id) ; TODO: replace with permission check for editing settings
25-
(http-response/not-found! "Not available in demo")
26-
(dmz/get-congregation cong-id))
24+
_ (when-not (dmz/view-settings-page? cong-id)
25+
(dmz/access-denied!))
26+
congregation (dmz/get-congregation cong-id)
2727
users (dmz/list-congregation-users cong-id)
2828
new-user (some-> (get-in request [:params :new-user])
2929
(parse-uuid))]

test/territory_bro/browser_test.clj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@
231231
(is (= "/congregation/demo/settings" settings-path))
232232
(doto *driver*
233233
(b/go (str *base-url* settings-path))
234-
(b/wait-has-text h1 "Page not found"))))))
234+
(b/wait-has-text h1 "Welcome")) ; access denied, so redirects the anonymous user to Auth0 login screen
235+
(is (str/includes? (b/get-url *driver*) ".auth0.com/"))))))
235236

236237

237238
(deftest registration-test

test/territory_bro/domain/dmz_test.clj

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,10 @@
109109
:response {:status 401
110110
:body "Not logged in"
111111
:headers {}}})
112-
(def no-congregation-access
112+
(def access-denied
113113
{:type :ring.util.http-response/response
114114
:response {:status 403
115-
:body "No congregation access"
116-
:headers {}}})
117-
(def no-territory-access
118-
{:type :ring.util.http-response/response
119-
:response {:status 403
120-
:body "No territory access"
115+
:body "Access denied"
121116
:headers {}}})
122117

123118

@@ -140,7 +135,7 @@
140135

141136
(testutil/with-user-id (UUID. 0 0x666)
142137
(testing "no permissions"
143-
(is (thrown-match? ExceptionInfo no-congregation-access
138+
(is (thrown-match? ExceptionInfo access-denied
144139
(dmz/get-congregation cong-id)))))
145140

146141
(testutil/with-anonymous-user
@@ -182,7 +177,7 @@
182177

183178
(testutil/with-user-id (UUID. 0 0x666)
184179
(testing "no permissions"
185-
(is (thrown-match? ExceptionInfo no-territory-access
180+
(is (thrown-match? ExceptionInfo access-denied
186181
(dmz/get-territory cong-id territory-id)))))
187182

188183
(testutil/with-anonymous-user

test/territory_bro/ui/congregation_page_test.clj

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
[territory-bro.test.testutil :as testutil]
1111
[territory-bro.ui.congregation-page :as congregation-page]
1212
[territory-bro.ui.html :as html])
13-
(:import (clojure.lang ExceptionInfo)
14-
(java.util UUID)))
13+
(:import (java.util UUID)))
1514

1615
(def model
1716
{:congregation {:congregation/name "Example Congregation"}
@@ -34,32 +33,12 @@
3433
(congregation/admin-permissions-granted cong-id user-id)])
3534
(testutil/with-user-id user-id
3635

37-
(testing "logged in"
36+
(testing "default"
3837
(is (= model (congregation-page/model! request))))
3938

40-
(testing "logged in, no access"
41-
(is (thrown-match? ExceptionInfo
42-
{:type :ring.util.http-response/response
43-
:response {:status 403
44-
:body "No congregation access"
45-
:headers {}}}
46-
(congregation-page/model! {:path-params {:congregation (UUID/randomUUID)}}))))
47-
48-
(testing "anonymous user"
49-
(testutil/with-anonymous-user
50-
(is (thrown-match? ExceptionInfo
51-
{:type :ring.util.http-response/response
52-
:response {:status 401
53-
:body "Not logged in"
54-
:headers {}}}
55-
(congregation-page/model! request)))))
56-
5739
(testing "demo congregation"
5840
(let [request {:path-params {:congregation "demo"}}]
59-
(is (= demo-model
60-
(congregation-page/model! request)
61-
(testutil/with-anonymous-user
62-
(congregation-page/model! request)))))))))))
41+
(is (= demo-model (congregation-page/model! request))))))))))
6342

6443
(deftest view-test
6544
(testing "full permissions"

test/territory_bro/ui/home_page_test.clj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@
5858
(replace-in [:congregations 1 :congregation/id] (UUID. 0 2) cong-id2))
5959
(home-page/model! request)))))
6060

61-
(testing "anonymous user"
61+
(testing "anonymous"
6262
(testutil/with-anonymous-user
6363
(is (= anonymous-model (home-page/model! request))))))
6464

6565
(binding [config/env {:demo-congregation nil}]
66-
(testing "anonymous user, no demo"
66+
(testing "anonymous, no demo"
6767
(testutil/with-anonymous-user
6868
(is (= no-demo-model (home-page/model! request)))))))))
6969

@@ -102,7 +102,7 @@
102102
(replace-in [:demo-available?] true false)))
103103
html/visible-text))))
104104

105-
(testing "anonymous user"
105+
(testing "anonymous"
106106
(is (= (html/normalize-whitespace
107107
(str introduction
108108
"Login
@@ -112,7 +112,7 @@
112112
(-> (home-page/view anonymous-model)
113113
html/visible-text))))
114114

115-
(testing "anonymous user, no demo"
115+
(testing "anonymous, no demo"
116116
(is (= (html/normalize-whitespace
117117
(str introduction
118118
"Login

test/territory_bro/ui/join_page_test.clj

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
(ns territory-bro.ui.join-page-test
66
(:require [clojure.test :refer :all]
77
[matcher-combinators.test :refer :all]
8+
[territory-bro.domain.dmz-test :as dmz-test]
89
[territory-bro.test.fixtures :refer :all]
910
[territory-bro.test.testutil :as testutil]
1011
[territory-bro.ui.html :as html]
@@ -22,13 +23,9 @@
2223
(testutil/with-user-id user-id
2324
(is (= model (join-page/model! request)))))
2425

25-
(testing "anonymous user"
26+
(testing "anonymous"
2627
(testutil/with-anonymous-user
27-
(is (thrown-match? ExceptionInfo
28-
{:type :ring.util.http-response/response
29-
:response {:status 401
30-
:body "Not logged in"
31-
:headers {}}}
28+
(is (thrown-match? ExceptionInfo dmz-test/not-logged-in
3229
(join-page/model! request)))))))
3330

3431
(deftest view-test

test/territory_bro/ui/open_share_page_test.clj

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
[reitit.core :as reitit]
99
[territory-bro.domain.dmz :as dmz]
1010
[territory-bro.domain.share :as share]
11+
[territory-bro.infra.authentication :as auth]
1112
[territory-bro.infra.config :as config]
1213
[territory-bro.infra.middleware :as middleware]
1314
[territory-bro.test.fixtures :refer :all]
@@ -23,7 +24,6 @@
2324
share-id (UUID. 0 3)
2425
share-key "abc123"
2526
demo-share-key (share/demo-share-key territory-id)
26-
user-id (UUID. 0 0x10)
2727
request {:path-params {:share-key share-key}}]
2828
(testutil/with-events [{:event/type :share.event/share-created
2929
:congregation/id cong-id
@@ -32,13 +32,13 @@
3232
:share/key share-key
3333
:share/type :link}]
3434
(binding [config/env {:now #(Instant/now)}]
35+
(testutil/with-anonymous-user
3536

36-
(testing "open regular share"
37-
(testutil/with-user-id user-id
37+
(testing "open regular share"
3838
(with-fixtures [fake-dispatcher-fixture]
3939
(let [response (open-share-page/open-share! request)]
4040
(is (= {:command/type :share.command/record-share-opened
41-
:command/user user-id
41+
:command/user auth/anonymous-user-id
4242
:share/id share-id}
4343
(dissoc @*last-command :command/time))
4444
"records a history of opening the share")
@@ -48,10 +48,9 @@
4848
::middleware/mutative-operation? true
4949
:body ""}
5050
response)
51-
"stores in session which shares the user has opened")))))
51+
"stores in session which shares the user has opened"))))
5252

53-
(testing "keeps existing session state, supports opening multiple shares"
54-
(testutil/with-anonymous-user
53+
(testing "keeps existing session state, supports opening multiple shares"
5554
(with-fixtures [fake-dispatcher-fixture]
5655
(let [another-share-id (UUID/randomUUID)
5756
request (assoc request :session {::dmz/opened-shares #{another-share-id}
@@ -60,11 +59,10 @@
6059
(is (= {::dmz/opened-shares #{share-id
6160
another-share-id}
6261
:other-session-state "stuff"}
63-
(:session response)))))))
62+
(:session response))))))
6463

65-
(testing "open demo share"
66-
(let [request {:path-params {:share-key demo-share-key}}]
67-
(testutil/with-anonymous-user
64+
(testing "open demo share"
65+
(let [request {:path-params {:share-key demo-share-key}}]
6866
(with-fixtures [fake-dispatcher-fixture]
6967
(let [response (open-share-page/open-share! request)]
7068
(is (= {:status 303
@@ -73,11 +71,10 @@
7371
response)
7472
"redirects to demo, without touching session state")
7573
(is (nil? @*last-command)
76-
"does not record that a demo share was opened"))))))
74+
"does not record that a demo share was opened")))))
7775

78-
(testing "share not found"
79-
(let [request {:path-params {:share-key "bad key"}}]
80-
(testutil/with-anonymous-user
76+
(testing "share not found"
77+
(let [request {:path-params {:share-key "bad key"}}]
8178
(with-fixtures [fake-dispatcher-fixture]
8279
(is (thrown-match? ExceptionInfo
8380
{:type :ring.util.http-response/response

test/territory_bro/ui/printouts_page_test.clj

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
(ns territory-bro.ui.printouts-page-test
66
(:require [clojure.string :as str]
77
[clojure.test :refer :all]
8+
[matcher-combinators.test :refer :all]
89
[territory-bro.domain.congregation :as congregation]
910
[territory-bro.domain.dmz :as dmz]
11+
[territory-bro.domain.dmz-test :as dmz-test]
1012
[territory-bro.domain.do-not-calls :as do-not-calls]
1113
[territory-bro.domain.do-not-calls-test :as do-not-calls-test]
1214
[territory-bro.domain.share :as share]
@@ -20,7 +22,8 @@
2022
[territory-bro.ui.html :as html]
2123
[territory-bro.ui.map-interaction-help-test :as map-interaction-help-test]
2224
[territory-bro.ui.printouts-page :as printouts-page])
23-
(:import (java.time Clock Duration Instant ZoneOffset ZonedDateTime)
25+
(:import (clojure.lang ExceptionInfo)
26+
(java.time Clock Duration Instant ZoneOffset ZonedDateTime)
2427
(java.util UUID)))
2528

2629
(def cong-id (UUID. 0 1))
@@ -139,6 +142,14 @@
139142
(binding [dmz/*state* (permissions/revoke dmz/*state* user-id [:share-territory-link cong-id])]
140143
(is (= no-qr-codes-model (printouts-page/model! request)))))
141144

145+
(testing "has opened a share, cannot see all territories"
146+
(let [user-id (UUID/randomUUID)
147+
share-id (UUID/randomUUID)]
148+
(testutil/with-user-id user-id
149+
(binding [dmz/*state* (share/grant-opened-shares dmz/*state* [share-id] user-id)]
150+
(is (thrown-match? ExceptionInfo dmz-test/access-denied
151+
(printouts-page/model! request)))))))
152+
142153
(testing "demo congregation"
143154
(binding [config/env {:demo-congregation cong-id}]
144155
(let [request {:path-params {:congregation "demo"}}]

0 commit comments

Comments
 (0)