Add LoggedInUser extractor
This commit is contained in:
parent
a82e4e1d50
commit
f5946e5ea0
|
@ -29,6 +29,9 @@ pub enum Error {
|
||||||
|
|
||||||
#[snafu(display("Failed to set group on file {}", path.display()))]
|
#[snafu(display("Failed to set group on file {}", path.display()))]
|
||||||
PermissionsChgrp { path: PathBuf, source: file_owner::FileOwnerError },
|
PermissionsChgrp { path: PathBuf, source: file_owner::FileOwnerError },
|
||||||
|
|
||||||
|
#[snafu(display("No cookie jar"))]
|
||||||
|
Cookie,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoResponse for Error {
|
impl IntoResponse for Error {
|
||||||
|
|
|
@ -11,8 +11,7 @@ use yunohost_api::{Username, Password};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::*,
|
error::*,
|
||||||
routes::COOKIE_NAME,
|
state::{COOKIE_NAME, RoutableAppState, sessions::LoggedInUser},
|
||||||
state::RoutableAppState,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, TryFromMultipart, Deserialize)]
|
#[derive(Debug, TryFromMultipart, Deserialize)]
|
||||||
|
@ -61,16 +60,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
pub async fn route(cookies: Cookies, state: State<RoutableAppState>, form: LoginForm) -> Result<String, Error> {
|
pub async fn route(user: Option<LoggedInUser>, cookies: Cookies, state: State<RoutableAppState>, form: LoginForm) -> Result<String, Error> {
|
||||||
trace!("ROUTE: /login/");
|
trace!("ROUTE: /login/");
|
||||||
if let Some(session_cookie) = cookies.get(COOKIE_NAME) {
|
|
||||||
trace!("User claims to have valid {} session: {}", COOKIE_NAME, &session_cookie);
|
|
||||||
if let Some(username) = state.sessions.verify_cookie(session_cookie.value()).await.context(SessionSnafu)? {
|
|
||||||
debug!("User claims were verified. They are identified as {}", &username);
|
|
||||||
return Ok(format!("Welcome back, {}! You were already logged in.", username));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("User claims for a {} session were unfounded. Performing login again.", COOKIE_NAME);
|
if let Some(username) = user {
|
||||||
|
return Ok(format!("Welcome back, {}! You were already logged in.", username));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Performing login attempt for user {}", &form.username);
|
debug!("Performing login attempt for user {}", &form.username);
|
||||||
|
@ -79,7 +73,7 @@ pub async fn route(cookies: Cookies, state: State<RoutableAppState>, form: Login
|
||||||
if state.check_login(&form.username, &form.password).await.unwrap() {
|
if state.check_login(&form.username, &form.password).await.unwrap() {
|
||||||
debug!("Login was successful for user {}. Saving cookie now.", &form.username);
|
debug!("Login was successful for user {}. Saving cookie now.", &form.username);
|
||||||
let (cookie_name, cookie_value) = state.sessions.make_session(COOKIE_NAME, &form.username).await;
|
let (cookie_name, cookie_value) = state.sessions.make_session(COOKIE_NAME, &form.username).await;
|
||||||
cookies.add(Cookie::new(cookie_name, cookie_value));
|
cookies.add(Cookie::new(COOKIE_NAME, cookie_value));
|
||||||
Ok(format!("Welcome {}", &form.username))
|
Ok(format!("Welcome {}", &form.username))
|
||||||
} else {
|
} else {
|
||||||
debug!("Login failed for user {}", &form.username);
|
debug!("Login failed for user {}", &form.username);
|
||||||
|
|
|
@ -9,8 +9,6 @@ use crate::state::RoutableAppState;
|
||||||
mod index;
|
mod index;
|
||||||
mod login;
|
mod login;
|
||||||
|
|
||||||
pub const COOKIE_NAME: &'static str = "yunohost.ssowat";
|
|
||||||
|
|
||||||
/// Build a router for the application, in a specific subpath eg `/yunohost/sso/`
|
/// Build a router for the application, in a specific subpath eg `/yunohost/sso/`
|
||||||
pub fn router(subpath: Option<String>, state: RoutableAppState) -> Router {
|
pub fn router(subpath: Option<String>, state: RoutableAppState) -> Router {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
|
|
|
@ -10,6 +10,8 @@ use sessions::SessionManager;
|
||||||
|
|
||||||
pub type RoutableAppState = Arc<AppState>;
|
pub type RoutableAppState = Arc<AppState>;
|
||||||
|
|
||||||
|
pub const COOKIE_NAME: &'static str = "yunohost.ssowat";
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub sessions: SessionManager,
|
pub sessions: SessionManager,
|
||||||
pub users: YunohostUsers,
|
pub users: YunohostUsers,
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
|
use axum::{
|
||||||
|
extract::FromRequestParts,
|
||||||
|
http::request::Parts,
|
||||||
|
RequestPartsExt,
|
||||||
|
};
|
||||||
use ring::{hmac,rand};
|
use ring::{hmac,rand};
|
||||||
use snafu::prelude::*;
|
use snafu::prelude::*;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
use tower_cookies::Cookies;
|
||||||
use yunohost_api::Username;
|
use yunohost_api::Username;
|
||||||
|
|
||||||
use crate::utils::time::now;
|
use crate::{
|
||||||
|
error::*,
|
||||||
|
state::{COOKIE_NAME, RoutableAppState},
|
||||||
|
utils::time::now,
|
||||||
|
};
|
||||||
|
|
||||||
/// An error related to session management
|
/// An error related to session management
|
||||||
#[derive(Debug, Snafu)]
|
#[derive(Debug, Snafu)]
|
||||||
|
@ -19,6 +29,10 @@ pub enum SessionError {
|
||||||
MalformedCookieTimestamp { timestamp: String, source: std::num::ParseIntError },
|
MalformedCookieTimestamp { timestamp: String, source: std::num::ParseIntError },
|
||||||
#[snafu(display("Malformed cookie username (empty)"))]
|
#[snafu(display("Malformed cookie username (empty)"))]
|
||||||
MalformedCookieUsername { source: yunohost_api::Error },
|
MalformedCookieUsername { source: yunohost_api::Error },
|
||||||
|
#[snafu(display("Invalid or expired session {}", cookie_name))]
|
||||||
|
InvalidOrExpired { cookie_name: String },
|
||||||
|
#[snafu(display("No session {}", cookie_name))]
|
||||||
|
NoSession { cookie_name: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds the currently active cookie-based user sessions for a certain cookie type.
|
/// Holds the currently active cookie-based user sessions for a certain cookie type.
|
||||||
|
@ -177,3 +191,58 @@ impl Cookie {
|
||||||
self.username.clone()
|
self.username.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LoggedInUser(Username);
|
||||||
|
|
||||||
|
impl LoggedInUser {
|
||||||
|
pub fn new(username: Username) -> LoggedInUser {
|
||||||
|
LoggedInUser(username)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_username(&self) -> &Username {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for LoggedInUser {
|
||||||
|
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl FromRequestParts<RoutableAppState> for LoggedInUser {
|
||||||
|
type Rejection = Error;
|
||||||
|
|
||||||
|
async fn from_request_parts(parts: &mut Parts, state: &RoutableAppState) -> Result<Self, Self::Rejection> {
|
||||||
|
// TODO: error
|
||||||
|
let cookies: Cookies = parts.extract().await.map_err(|_| Error::Cookie)?;
|
||||||
|
trace!("[SESSION:{}] Checking if the user has a valid session", COOKIE_NAME);
|
||||||
|
if let Some(session_cookie) = cookies.get(COOKIE_NAME) {
|
||||||
|
trace!("[SESSION:{}] User claims to have a session with cookie: {}", COOKIE_NAME, &session_cookie);
|
||||||
|
if let Some(username) = state.sessions.verify_cookie(session_cookie.value()).await.context(SessionSnafu)? {
|
||||||
|
debug!("[SESSION:{}] User {} resumed session.", COOKIE_NAME, &username);
|
||||||
|
return Ok(LoggedInUser::new(username));
|
||||||
|
} else {
|
||||||
|
trace!("[SESSION:{}] User session is invalid or has expired.", COOKIE_NAME);
|
||||||
|
return Err(Error::Session {
|
||||||
|
source: SessionError::InvalidOrExpired {
|
||||||
|
cookie_name: COOKIE_NAME.to_string(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trace!("[SESSION:{}] No current session cookie", COOKIE_NAME);
|
||||||
|
return Err(Error::Session {
|
||||||
|
source: SessionError::NoSession {
|
||||||
|
cookie_name: COOKIE_NAME.to_string(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user