86 lines
3.0 KiB
Rust
86 lines
3.0 KiB
Rust
use axum::{
|
|
extract::FromRequestParts,
|
|
http::request::Parts,
|
|
RequestPartsExt,
|
|
};
|
|
use snafu::prelude::*;
|
|
use std::borrow::Cow;
|
|
use tower_cookies::{Cookies, Cookie};
|
|
use yunohost_api::Username;
|
|
|
|
use crate::{
|
|
error::*,
|
|
state::{COOKIE_NAME, RoutableAppState, sessions::*},
|
|
};
|
|
|
|
pub struct LoggedOutUser(Session);
|
|
|
|
impl LoggedOutUser {
|
|
pub fn new(session: Session) -> Self {
|
|
Self(session)
|
|
}
|
|
|
|
pub fn username(&self) -> &Username {
|
|
self.0.username()
|
|
}
|
|
|
|
pub fn cookie(&self) -> Cookie {
|
|
Cookie::build(
|
|
Cow::Owned(self.0.cookie_name.to_string()),
|
|
Cow::Owned(self.0.content.to_string()),
|
|
).path(
|
|
Cow::Owned("/".to_string())
|
|
).finish()
|
|
}
|
|
}
|
|
|
|
impl From<Session> for LoggedOutUser {
|
|
fn from(s: Session) -> LoggedOutUser {
|
|
LoggedOutUser::new(s)
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl FromRequestParts<RoutableAppState> for LoggedOutUser {
|
|
type Rejection = Error;
|
|
|
|
async fn from_request_parts(parts: &mut Parts, state: &RoutableAppState) -> Result<Self, Self::Rejection> {
|
|
let cookies: Cookies = parts.extract().await.map_err(|_| Error::Cookie)?;
|
|
trace!("[SESSION:{}/logout] Checking if the user has a valid session", COOKIE_NAME);
|
|
trace!("{:#?}", parts.headers);
|
|
let session_cookie = if let Some(session_cookie) = cookies.get(COOKIE_NAME) {
|
|
trace!("[SESSION:{}/logout] User claims to have a session with cookie: {}", COOKIE_NAME, &session_cookie);
|
|
session_cookie
|
|
} else {
|
|
trace!("[SESSION:{}/logout] No current session cookie", COOKIE_NAME);
|
|
return Err(Error::Session {
|
|
source: SessionError::NoSession {
|
|
cookie_name: COOKIE_NAME.to_string(),
|
|
}
|
|
});
|
|
};
|
|
|
|
if let Some(session) = state.sessions.verify_cookie_session(session_cookie.value()).await.context(SessionSnafu)? {
|
|
debug!("[SESSION:{}/logout] User {} resumed session, requesting to log out.", COOKIE_NAME, &session.username);
|
|
if state.sessions.invalidate_session(session.timestamp, &session.content).await {
|
|
return Ok(session.into());
|
|
} else {
|
|
warn!("[SESSION:{}/logout] User {} requested logout but was already logged out. Here's the session:\n{:#?}", COOKIE_NAME, &session.username, &session);
|
|
warn!("This is probably a race condition but is completely harmless.");
|
|
return Err(Error::Session {
|
|
source: SessionError::InvalidOrExpired {
|
|
cookie_name: COOKIE_NAME.to_string(),
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
trace!("[SESSION:{}/logout] User session is invalid or has expired.", COOKIE_NAME);
|
|
return Err(Error::Session {
|
|
source: SessionError::InvalidOrExpired {
|
|
cookie_name: COOKIE_NAME.to_string(),
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|