1
//! A3: the header fragment is the only way into the account page.
2

            
3
use crate::common::{create_mock_jwt_auth, create_mock_user};
4
use axum::{Extension, response::IntoResponse};
5

            
6
3
async fn fragment(role: &str) -> String {
7
3
    let mut user = create_mock_user();
8
3
    user.role = role.to_string();
9
3
    let response = web::handler::get_logout_link(Extension(create_mock_jwt_auth(user)))
10
3
        .await
11
3
        .expect("fragment")
12
3
        .into_response();
13

            
14
3
    let body = axum::body::to_bytes(response.into_body(), usize::MAX)
15
3
        .await
16
3
        .expect("body");
17
3
    String::from_utf8(body.to_vec()).expect("utf8")
18
3
}
19

            
20
#[supp_macro::shared_runtime_test]
21
1
async fn every_user_gets_a_link_to_their_account_page() {
22
1
    let markup = fragment("user").await;
23
1
    assert!(
24
1
        markup.contains("/account/ssh-key"),
25
        "without this link the password form is unreachable: {markup}"
26
    );
27
1
    assert!(markup.contains("/api/auth/logout"), "got: {markup}");
28
}
29

            
30
#[supp_macro::shared_runtime_test]
31
1
async fn only_an_admin_is_offered_the_registration_link() {
32
1
    assert!(
33
1
        !fragment("user").await.contains("/register"),
34
        "a non-admin must not be shown a link that 401s"
35
    );
36
1
    assert!(fragment("admin").await.contains("/register"));
37
}
38

            
39
/// The middleware must not append a refreshed session on top of a handler that
40
/// just ended one. Logout does not bump `session_generation`, so a refresh
41
/// minted on the way in stays valid, and its rotated Redis UUID is one logout
42
/// never saw and cannot delete.
43
#[test]
44
1
fn a_session_ending_response_is_recognised() {
45
    use axum::body::Body;
46
    use axum::http::{Response, header};
47

            
48
2
    let build = |cookie: String| {
49
2
        let mut response = Response::new(Body::empty());
50
2
        response
51
2
            .headers_mut()
52
2
            .append(header::SET_COOKIE, cookie.parse().expect("header"));
53
2
        response
54
2
    };
55

            
56
1
    let ended = web::handler::session_end_headers("/login");
57
1
    let expiry = ended
58
1
        .get_all(header::SET_COOKIE)
59
1
        .iter()
60
1
        .find_map(|v| v.to_str().ok().filter(|c| c.starts_with("access_token")))
61
1
        .expect("an access_token expiry")
62
1
        .to_string();
63

            
64
1
    assert!(web::jwt_auth::ends_session(&build(expiry)));
65
1
    assert!(!web::jwt_auth::ends_session(&build(
66
1
        "access_token=a.b.c; Path=/; Max-Age=900".to_string()
67
1
    )));
68
1
}