refactor: drop /web/ URL prefix from browser routes
The /web/ prefix predated the /api/ split; today it just clutters every URL
without disambiguating anything. All 16 browser routes move to the apex
(/cases, /login, /magic, /events, /audio/...). The 6 /api/* routes are
unchanged. A new /->>/cases redirect closes the apex 404.
The open-redirect guard in magic.rs and case_actions.rs flips from a
positive whitelist (starts_with("/web/")) to a deny-list: same-origin path,
not protocol-relative, not under /api/, no \. The /api/ exclusion is now
load-bearing and covered by tests.
Pre-production: no transition redirects.
This commit is contained in:
@@ -86,7 +86,7 @@ async fn create_with_valid_key_returns_token() {
|
||||
let app = test_app();
|
||||
let resp = app
|
||||
.oneshot(create_request(
|
||||
r#"{"return_to":"/web/cases/abc"}"#,
|
||||
r#"{"return_to":"/cases/abc"}"#,
|
||||
Some(API_KEY),
|
||||
))
|
||||
.await
|
||||
@@ -106,7 +106,7 @@ async fn consume_valid_token_sets_cookie_and_redirects() {
|
||||
let create_resp = app
|
||||
.clone()
|
||||
.oneshot(create_request(
|
||||
r#"{"return_to":"/web/cases/abc"}"#,
|
||||
r#"{"return_to":"/cases/abc"}"#,
|
||||
Some(API_KEY),
|
||||
))
|
||||
.await
|
||||
@@ -125,7 +125,7 @@ async fn consume_valid_token_sets_cookie_and_redirects() {
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"/web/cases/abc"
|
||||
"/cases/abc"
|
||||
);
|
||||
assert_eq!(
|
||||
resp.headers()
|
||||
@@ -167,16 +167,13 @@ async fn consume_token_is_one_time_use() {
|
||||
assert_eq!(first.status(), StatusCode::SEE_OTHER);
|
||||
assert_eq!(
|
||||
first.headers().get(header::LOCATION).unwrap(),
|
||||
"/web/cases" // default
|
||||
"/cases" // default
|
||||
);
|
||||
|
||||
let second = app.oneshot(consume_request(&token)).await.unwrap();
|
||||
// Second use: token gone, redirect to login.
|
||||
assert_eq!(second.status(), StatusCode::SEE_OTHER);
|
||||
assert_eq!(
|
||||
second.headers().get(header::LOCATION).unwrap(),
|
||||
"/web/login"
|
||||
);
|
||||
assert_eq!(second.headers().get(header::LOCATION).unwrap(), "/login");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -217,7 +214,7 @@ async fn consume_expired_token_redirects_to_login() {
|
||||
PendingMagicLink {
|
||||
slug: "dr_a".into(),
|
||||
role: "doctor".into(),
|
||||
return_to: "/web/cases".into(),
|
||||
return_to: "/cases".into(),
|
||||
expires_at: Instant::now() - Duration::from_secs(1),
|
||||
},
|
||||
);
|
||||
@@ -226,7 +223,7 @@ async fn consume_expired_token_redirects_to_login() {
|
||||
let app = doctate_server::create_router_with_state(state);
|
||||
let resp = app.oneshot(consume_request(&token)).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::SEE_OTHER);
|
||||
assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/web/login");
|
||||
assert_eq!(resp.headers().get(header::LOCATION).unwrap(), "/login");
|
||||
|
||||
// And the token must be removed from the store, even though it expired.
|
||||
assert!(store.read().await.get(&token).is_none());
|
||||
|
||||
Reference in New Issue
Block a user