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