api_auth.rs
⎇
Raw
1//! Authentication: first-boot setup, login, session, logout, disabling.
2
3mod common;
4
5use std::sync::Arc;
6
7use axum::http::StatusCode;
8use common::*;
9use serde_json::json;
10
11#[tokio::test]
12async fn first_boot_me_reports_first_boot() {
13 let env = Env::new().await;
14 let c = Client::new(env.app.clone());
15 let r = c.get("/api/auth/me").await;
16 assert_eq!(r.status, StatusCode::OK);
17 let j = r.json();
18 assert_eq!(j["first_boot"], true);
19 assert!(j["user"].is_null());
20 assert_eq!(j["roots"], json!([]));
21}
22
23#[tokio::test]
24async fn setup_validates_input() {
25 let env = Env::new().await;
26 let c = Client::new(env.app.clone());
27 // Blank name.
28 let r = c
29 .post_json(
30 "/api/auth/setup",
31 &json!({ "name": " ", "password": "longenough1" }),
32 )
33 .await;
34 assert_eq!(r.status, StatusCode::BAD_REQUEST);
35 // Name too long (65 chars).
36 let r = c
37 .post_json(
38 "/api/auth/setup",
39 &json!({ "name": "a".repeat(65), "password": "longenough1" }),
40 )
41 .await;
42 assert_eq!(r.status, StatusCode::BAD_REQUEST);
43 // Password too short.
44 let r = c
45 .post_json(
46 "/api/auth/setup",
47 &json!({ "name": "admin", "password": "short" }),
48 )
49 .await;
50 assert_eq!(r.status, StatusCode::BAD_REQUEST);
51 // Nothing was created.
52 assert_eq!(c.get("/api/auth/me").await.json()["first_boot"], true);
53}
54
55#[tokio::test]
56async fn setup_creates_admin_and_sets_cookie() {
57 let env = Env::new().await;
58 let admin = env.admin().await;
59
60 // /me reports the admin with the whole root (rw).
61 let r = admin.get("/api/auth/me").await;
62 assert_eq!(r.status, StatusCode::OK);
63 let j = r.json();
64 assert_eq!(j["first_boot"], false);
65 assert_eq!(j["user"]["name"], "admin");
66 assert_eq!(j["user"]["is_admin"], true);
67 let roots = j["roots"].as_array().unwrap();
68 assert_eq!(roots.len(), 1);
69 assert_eq!(roots[0]["path"], ".");
70 assert_eq!(roots[0]["mode"], "rw");
71 // The whole root is named after the root folder itself (`--root-name`
72 // overrides this; see `root_name_is_configurable`).
73 assert_eq!(roots[0]["name"], env.state.root_name);
74 assert_eq!(
75 roots[0]["name"],
76 env.root.path().file_name().unwrap().to_str().unwrap()
77 );
78
79 // Setup is only available on first boot.
80 let anon = Client::new(env.app.clone());
81 let r = anon
82 .post_json(
83 "/api/auth/setup",
84 &json!({ "name": "x", "password": "longenough1" }),
85 )
86 .await;
87 assert_eq!(r.status, StatusCode::CONFLICT);
88}
89
90#[tokio::test]
91async fn login_flows() {
92 let env = Env::new().await;
93 let _admin = env.admin().await;
94 let c = Client::new(env.app.clone());
95
96 let r = c
97 .post_json(
98 "/api/auth/login",
99 &json!({ "name": "admin", "password": "wrongpass1" }),
100 )
101 .await;
102 assert_eq!(r.status, StatusCode::UNAUTHORIZED);
103
104 let r = c
105 .post_json(
106 "/api/auth/login",
107 &json!({ "name": "ghost", "password": "whatever1" }),
108 )
109 .await;
110 assert_eq!(r.status, StatusCode::UNAUTHORIZED);
111
112 let r = c
113 .post_json(
114 "/api/auth/login",
115 &json!({ "name": "admin", "password": "admin1234" }),
116 )
117 .await;
118 assert_eq!(r.status, StatusCode::OK);
119 assert!(session_cookie(&r).is_some());
120}
121
122#[tokio::test]
123async fn me_requires_valid_session() {
124 let env = Env::new().await;
125 let _admin = env.admin().await;
126
127 // No cookie at all.
128 let anon = Client::new(env.app.clone());
129 assert_eq!(
130 anon.get("/api/auth/me").await.status,
131 StatusCode::UNAUTHORIZED
132 );
133
134 // Bogus session token.
135 let mut bogus = Client::new(env.app.clone());
136 bogus.set_cookie("not-a-real-token");
137 assert_eq!(
138 bogus.get("/api/auth/me").await.status,
139 StatusCode::UNAUTHORIZED
140 );
141}
142
143#[tokio::test]
144async fn logout_invalidates_session() {
145 let env = Env::new().await;
146 let admin = env.admin().await;
147 let r = admin.post_json("/api/auth/logout", &json!({})).await;
148 assert_eq!(r.status, StatusCode::OK);
149 // The Set-Cookie header clears the cookie.
150 assert!(
151 r.header("set-cookie")
152 .unwrap()
153 .starts_with("fbng_session=;")
154 );
155 // The old cookie no longer authenticates.
156 assert_eq!(
157 admin.get("/api/auth/me").await.status,
158 StatusCode::UNAUTHORIZED
159 );
160}
161
162#[tokio::test]
163async fn disabled_user_loses_session_and_cannot_login() {
164 let env = Env::new().await;
165 let admin = env.admin().await;
166
167 create_user(&admin, "bob", "bobpass123", &[("docs", "rw")]).await;
168 let bob = login(&env, "bob", "bobpass123").await;
169 assert_eq!(bob.get("/api/auth/me").await.status, StatusCode::OK);
170
171 // Admin disables bob.
172 let id = user_id(&admin, "bob").await;
173 let r = admin
174 .put_json(
175 &format!("/api/admin/users/{id}"),
176 &json!({ "active": false }),
177 )
178 .await;
179 assert_eq!(r.status, StatusCode::OK);
180
181 // bob's live session is rejected…
182 assert_eq!(
183 bob.get("/api/auth/me").await.status,
184 StatusCode::UNAUTHORIZED
185 );
186 // …and he cannot log in again.
187 let anon = Client::new(env.app.clone());
188 let r = anon
189 .post_json(
190 "/api/auth/login",
191 &json!({ "name": "bob", "password": "bobpass123" }),
192 )
193 .await;
194 assert_eq!(r.status, StatusCode::UNAUTHORIZED);
195}
196
197#[tokio::test]
198async fn root_name_is_configurable() {
199 let mut env = Env::new().await;
200 // Rebuild the router with a custom root name (what `--root-name` /
201 // `FILEBROWSER_ROOT_NAME` sets on the state).
202 let state = Arc::new(server::error::AppState {
203 db: env.state.db.clone(),
204 root: env.state.root.clone(),
205 root_name: "Media".to_string(),
206 https: false,
207 public_url: None,
208 thumbs: None,
209 });
210 env.app = server::api::router(state.clone());
211 env.state = state;
212 let admin = env.admin().await;
213 let j = admin.get("/api/auth/me").await.json();
214 assert_eq!(j["roots"][0]["name"], "Media");
215 // Subroots keep their own folder name.
216 create_user(&admin, "bob", "bobpass123", &[("docs/inner", "rw")]).await;
217 let bob = login(&env, "bob", "bobpass123").await;
218 assert_eq!(
219 bob.get("/api/auth/me").await.json()["roots"][0]["name"],
220 "inner"
221 );
222}
223
224#[tokio::test]
225async fn public_url_reaches_client() {
226 let mut env = Env::new().await;
227 let state = Arc::new(server::error::AppState {
228 db: env.state.db.clone(),
229 root: env.state.root.clone(),
230 root_name: env.state.root_name.clone(),
231 https: false,
232 public_url: Some("https://files.example.com".to_string()),
233 thumbs: None,
234 });
235 env.app = server::api::router(state.clone());
236 env.state = state;
237 let admin = env.admin().await;
238 assert_eq!(
239 admin.get("/api/auth/me").await.json()["public_url"],
240 "https://files.example.com"
241 );
242}
243