api_auth.rs
⎇
Raw
1//! Authentication: first-boot setup, login, session, logout, disabling.
2
3mod common;
4
5use axum::http::StatusCode;
6use common::*;
7use serde_json::json;
8
9#[tokio::test]
10async fn first_boot_me_reports_first_boot() {
11 let env = Env::new().await;
12 let c = Client::new(env.app.clone());
13 let r = c.get("/api/auth/me").await;
14 assert_eq!(r.status, StatusCode::OK);
15 let j = r.json();
16 assert_eq!(j["first_boot"], true);
17 assert!(j["user"].is_null());
18 assert_eq!(j["roots"], json!([]));
19}
20
21#[tokio::test]
22async fn setup_validates_input() {
23 let env = Env::new().await;
24 let c = Client::new(env.app.clone());
25 // Blank name.
26 let r = c
27 .post_json(
28 "/api/auth/setup",
29 &json!({ "name": " ", "password": "longenough1" }),
30 )
31 .await;
32 assert_eq!(r.status, StatusCode::BAD_REQUEST);
33 // Name too long (65 chars).
34 let r = c
35 .post_json(
36 "/api/auth/setup",
37 &json!({ "name": "a".repeat(65), "password": "longenough1" }),
38 )
39 .await;
40 assert_eq!(r.status, StatusCode::BAD_REQUEST);
41 // Password too short.
42 let r = c
43 .post_json(
44 "/api/auth/setup",
45 &json!({ "name": "admin", "password": "short" }),
46 )
47 .await;
48 assert_eq!(r.status, StatusCode::BAD_REQUEST);
49 // Nothing was created.
50 assert_eq!(c.get("/api/auth/me").await.json()["first_boot"], true);
51}
52
53#[tokio::test]
54async fn setup_creates_admin_and_sets_cookie() {
55 let env = Env::new().await;
56 let admin = env.admin().await;
57
58 // /me reports the admin with the whole root (rw).
59 let r = admin.get("/api/auth/me").await;
60 assert_eq!(r.status, StatusCode::OK);
61 let j = r.json();
62 assert_eq!(j["first_boot"], false);
63 assert_eq!(j["user"]["name"], "admin");
64 assert_eq!(j["user"]["is_admin"], true);
65 let roots = j["roots"].as_array().unwrap();
66 assert_eq!(roots.len(), 1);
67 assert_eq!(roots[0]["path"], ".");
68 assert_eq!(roots[0]["mode"], "rw");
69
70 // Setup is only available on first boot.
71 let anon = Client::new(env.app.clone());
72 let r = anon
73 .post_json(
74 "/api/auth/setup",
75 &json!({ "name": "x", "password": "longenough1" }),
76 )
77 .await;
78 assert_eq!(r.status, StatusCode::CONFLICT);
79}
80
81#[tokio::test]
82async fn login_flows() {
83 let env = Env::new().await;
84 let _admin = env.admin().await;
85 let c = Client::new(env.app.clone());
86
87 let r = c
88 .post_json(
89 "/api/auth/login",
90 &json!({ "name": "admin", "password": "wrongpass1" }),
91 )
92 .await;
93 assert_eq!(r.status, StatusCode::UNAUTHORIZED);
94
95 let r = c
96 .post_json(
97 "/api/auth/login",
98 &json!({ "name": "ghost", "password": "whatever1" }),
99 )
100 .await;
101 assert_eq!(r.status, StatusCode::UNAUTHORIZED);
102
103 let r = c
104 .post_json(
105 "/api/auth/login",
106 &json!({ "name": "admin", "password": "admin1234" }),
107 )
108 .await;
109 assert_eq!(r.status, StatusCode::OK);
110 assert!(session_cookie(&r).is_some());
111}
112
113#[tokio::test]
114async fn me_requires_valid_session() {
115 let env = Env::new().await;
116 let _admin = env.admin().await;
117
118 // No cookie at all.
119 let anon = Client::new(env.app.clone());
120 assert_eq!(
121 anon.get("/api/auth/me").await.status,
122 StatusCode::UNAUTHORIZED
123 );
124
125 // Bogus session token.
126 let mut bogus = Client::new(env.app.clone());
127 bogus.set_cookie("not-a-real-token");
128 assert_eq!(
129 bogus.get("/api/auth/me").await.status,
130 StatusCode::UNAUTHORIZED
131 );
132}
133
134#[tokio::test]
135async fn logout_invalidates_session() {
136 let env = Env::new().await;
137 let admin = env.admin().await;
138 let r = admin.post_json("/api/auth/logout", &json!({})).await;
139 assert_eq!(r.status, StatusCode::OK);
140 // The Set-Cookie header clears the cookie.
141 assert!(r
142 .header("set-cookie")
143 .unwrap()
144 .starts_with("fbng_session=;"));
145 // The old cookie no longer authenticates.
146 assert_eq!(
147 admin.get("/api/auth/me").await.status,
148 StatusCode::UNAUTHORIZED
149 );
150}
151
152#[tokio::test]
153async fn disabled_user_loses_session_and_cannot_login() {
154 let env = Env::new().await;
155 let admin = env.admin().await;
156
157 create_user(&admin, "bob", "bobpass123", &[("docs", "rw")]).await;
158 let bob = login(&env, "bob", "bobpass123").await;
159 assert_eq!(bob.get("/api/auth/me").await.status, StatusCode::OK);
160
161 // Admin disables bob.
162 let id = user_id(&admin, "bob").await;
163 let r = admin
164 .put_json(
165 &format!("/api/admin/users/{id}"),
166 &json!({ "active": false }),
167 )
168 .await;
169 assert_eq!(r.status, StatusCode::OK);
170
171 // bob's live session is rejected…
172 assert_eq!(
173 bob.get("/api/auth/me").await.status,
174 StatusCode::UNAUTHORIZED
175 );
176 // …and he cannot log in again.
177 let anon = Client::new(env.app.clone());
178 let r = anon
179 .post_json(
180 "/api/auth/login",
181 &json!({ "name": "bob", "password": "bobpass123" }),
182 )
183 .await;
184 assert_eq!(r.status, StatusCode::UNAUTHORIZED);
185}
186