Lines
100 %
Functions
Branches
//! A3: the header fragment is the only way into the account page.
use crate::common::{create_mock_jwt_auth, create_mock_user};
use axum::{Extension, response::IntoResponse};
async fn fragment(role: &str) -> String {
let mut user = create_mock_user();
user.role = role.to_string();
let response = web::handler::get_logout_link(Extension(create_mock_jwt_auth(user)))
.await
.expect("fragment")
.into_response();
let body = axum::body::to_bytes(response.into_body(), usize::MAX)
.expect("body");
String::from_utf8(body.to_vec()).expect("utf8")
}
#[supp_macro::shared_runtime_test]
async fn every_user_gets_a_link_to_their_account_page() {
let markup = fragment("user").await;
assert!(
markup.contains("/account/ssh-key"),
"without this link the password form is unreachable: {markup}"
);
assert!(markup.contains("/api/auth/logout"), "got: {markup}");
async fn only_an_admin_is_offered_the_registration_link() {
!fragment("user").await.contains("/register"),
"a non-admin must not be shown a link that 401s"
assert!(fragment("admin").await.contains("/register"));
/// The middleware must not append a refreshed session on top of a handler that
/// just ended one. Logout does not bump `session_generation`, so a refresh
/// minted on the way in stays valid, and its rotated Redis UUID is one logout
/// never saw and cannot delete.
#[test]
fn a_session_ending_response_is_recognised() {
use axum::body::Body;
use axum::http::{Response, header};
let build = |cookie: String| {
let mut response = Response::new(Body::empty());
response
.headers_mut()
.append(header::SET_COOKIE, cookie.parse().expect("header"));
};
let ended = web::handler::session_end_headers("/login");
let expiry = ended
.get_all(header::SET_COOKIE)
.iter()
.find_map(|v| v.to_str().ok().filter(|c| c.starts_with("access_token")))
.expect("an access_token expiry")
.to_string();
assert!(web::jwt_auth::ends_session(&build(expiry)));
assert!(!web::jwt_auth::ends_session(&build(
"access_token=a.b.c; Path=/; Max-Age=900".to_string()
)));