From 907f22bfd4681db9856c01004ac993a281db997a Mon Sep 17 00:00:00 2001 From: selfhoster1312 Date: Sun, 27 Aug 2023 19:40:47 +0000 Subject: [PATCH] Add more debugging information in authorize route, and cargo fmt --- src/cli.rs | 2 +- src/error.rs | 24 +++++-- src/main.rs | 16 +++-- src/routes/authorize.rs | 65 +++++++++++++------ src/routes/login.rs | 47 ++++++++++---- src/routes/logout.rs | 7 +- src/routes/mod.rs | 3 +- src/state/login.rs | 47 +++++++++----- src/state/logout.rs | 61 +++++++++++------ src/state/mod.rs | 13 +++- src/state/sessions.rs | 90 +++++++++++++++++--------- src/utils/fs.rs | 48 +++++++------- src/utils/mod.rs | 2 +- src/utils/socket.rs | 27 +++----- src/utils/time.rs | 2 +- yunohost-api/src/lib.rs | 2 + yunohost-api/src/permissions/ssowat.rs | 15 +++++ 17 files changed, 307 insertions(+), 164 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index acfabd8..64d4796 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,4 +8,4 @@ use std::path::PathBuf; pub struct Cli { /// Where to place the UNIX socket for SSOWat pub path: PathBuf, -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs index 3211963..bb32ed2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -7,7 +7,10 @@ use std::path::PathBuf; #[snafu(visibility(pub))] pub enum Error { #[snafu(display("Failed to spawn unix socket at {}", path.display()))] - SocketCreate { path: PathBuf, source: std::io::Error }, + SocketCreate { + path: PathBuf, + source: std::io::Error, + }, #[snafu(display("Failed to spawn a web server"))] Server { source: hyper::Error }, @@ -16,19 +19,30 @@ pub enum Error { Yunohost { source: yunohost_api::Error }, #[snafu(display("{}", source))] - Session { source: crate::state::sessions::SessionError }, + Session { + source: crate::state::sessions::SessionError, + }, #[snafu(display("Failed to executed tokio task"))] TokioTask { source: tokio::task::JoinError }, #[snafu(display("Failed to set permissions on file {}", path.display()))] - Permissions { path: PathBuf, source: std::io::Error }, + Permissions { + path: PathBuf, + source: std::io::Error, + }, #[snafu(display("Failed to set owner on file {}", path.display()))] - PermissionsChown { path: PathBuf, source: file_owner::FileOwnerError }, + PermissionsChown { + path: PathBuf, + source: file_owner::FileOwnerError, + }, #[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, diff --git a/src/main.rs b/src/main.rs index 0c325b9..77f02d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,11 @@ -#[macro_use] extern crate async_trait; -#[macro_use] extern crate axum; -#[macro_use] extern crate log; -#[macro_use] extern crate serde; +#[macro_use] +extern crate async_trait; +#[macro_use] +extern crate axum; +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde; use clap::Parser; @@ -25,9 +29,7 @@ async fn main() -> Result<(), error::Error> { .await .unwrap(); - let state = Arc::new( - state::AppState::new().await? - ); + let state = Arc::new(state::AppState::new().await?); let app = routes::router(Some("/ssowat/".to_string()), state); utils::socket::serve(&path, app).await?; diff --git a/src/routes/authorize.rs b/src/routes/authorize.rs index 717aca8..db1854a 100644 --- a/src/routes/authorize.rs +++ b/src/routes/authorize.rs @@ -1,36 +1,40 @@ use axum::{ - RequestPartsExt, - extract::{FromRequestParts, State}, - http::{StatusCode, header::HeaderMap, request::Parts}, + extract::{FromRequestParts, State}, + http::{header::HeaderMap, request::Parts, StatusCode}, + response::IntoResponse, + RequestPartsExt, }; use snafu::prelude::*; use url::Url; use crate::{ error::*, - state::{RoutableAppState, LoggedInUser}, state::sessions::*, + state::{LoggedInUser, RoutableAppState}, }; // TODO: Implement as a typed header +#[derive(Debug)] pub struct OriginalURI(Url); #[async_trait] impl FromRequestParts for OriginalURI { type Rejection = Error; - async fn from_request_parts(parts: &mut Parts, state: &RoutableAppState) -> Result { - let headers: HeaderMap = parts.extract().await.unwrap(); - if let Some(uri) = headers.get("X-Original-URI") { - // TODO: error - Ok( - OriginalURI(Url::parse(uri.to_str().unwrap()).unwrap()) - ) - } else { - // TODO: error - panic!() - } - } + async fn from_request_parts( + parts: &mut Parts, + state: &RoutableAppState, + ) -> Result { + let headers: HeaderMap = parts.extract().await.unwrap(); + if let Some(uri) = headers.get("X-Original-URI") { + trace!("Received original URI: {}", uri.to_str().unwrap()); + // TODO: error + Ok(OriginalURI(Url::parse(uri.to_str().unwrap()).unwrap())) + } else { + // TODO: error + panic!() + } + } } impl AsRef for OriginalURI { @@ -39,24 +43,43 @@ impl AsRef for OriginalURI { } } - #[debug_handler] -pub async fn route(user: Option, uri: Option, state: State) -> StatusCode { +pub async fn route( + user: Option, + uri: Option, + state: State, +) -> impl IntoResponse { + debug!("Starting authorization request"); if uri.is_none() { panic!(); } let uri = uri.unwrap(); let username = user.map(|u| u.username().clone()); + debug!( + "Requesting authorization for user {:?} URI {}", + username, + uri.0.as_str() + ); match state.permissions.ssowat_config() { Ok(conf) => { + let mut headers = HeaderMap::new(); if conf.user_has_permission_for_uri(username.as_ref(), uri.as_ref()) { - StatusCode::OK + debug!("User {:?} is authorized.", username); + if let Some(username) = username { + headers.insert("Remote-User", username.as_str().parse().unwrap()); + headers.insert("user", username.as_str().parse().unwrap()); + } + let res = (StatusCode::OK, headers).into_response(); + debug!("{:?}", res); + res } else { - StatusCode::FORBIDDEN + debug!("User {:?} is not authorized.", username); + (StatusCode::FORBIDDEN, headers).into_response() } - }, Err(e) => { + } + Err(e) => { panic!() } } diff --git a/src/routes/login.rs b/src/routes/login.rs index 1d4bc8d..35f7793 100644 --- a/src/routes/login.rs +++ b/src/routes/login.rs @@ -1,16 +1,16 @@ use axum::{ - extract::{FromRequest, Form, Json, State}, + extract::{Form, FromRequest, Json, State}, http::{self, Request, StatusCode}, response::{IntoResponse, Response}, RequestExt, }; use axum_typed_multipart::{TryFromMultipart, TypedMultipart}; use tower_cookies::Cookies; -use yunohost_api::{Username, Password}; +use yunohost_api::{Password, Username}; use crate::{ error::*, - state::{COOKIE_NAME, RoutableAppState, LoggedInUser}, + state::{LoggedInUser, RoutableAppState, COOKIE_NAME}, }; #[derive(Debug, TryFromMultipart, Deserialize)] @@ -29,16 +29,20 @@ where B::Data: Into, B::Error: Into + Send + std::error::Error, B: Send + 'static + axum::body::HttpBody, - S: Send + S: Send, { type Rejection = Response; async fn from_request(req: Request, _state: &S) -> Result { let headers = req.headers(); - if let Some(mime) = headers.get(http::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()) { + if let Some(mime) = headers + .get(http::header::CONTENT_TYPE) + .and_then(|v| v.to_str().ok()) + { if mime.starts_with("application/json") { - let Json(login_form): Json = req.extract().await.map_err(IntoResponse::into_response)?; + let Json(login_form): Json = + req.extract().await.map_err(IntoResponse::into_response)?; return Ok(login_form); } @@ -48,7 +52,8 @@ where } if mime.starts_with("multipart/form-data") { - let TypedMultipart(login_form): TypedMultipart = req.extract().await.map_err(IntoResponse::into_response)?; + let TypedMultipart(login_form): TypedMultipart = + req.extract().await.map_err(IntoResponse::into_response)?; return Ok(login_form); } Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()) @@ -59,19 +64,37 @@ where } #[debug_handler] -pub async fn route(user: Option, cookies: Cookies, state: State, form: LoginForm) -> Result { +pub async fn route( + user: Option, + cookies: Cookies, + state: State, + form: LoginForm, +) -> Result { trace!("ROUTE: /login/"); if let Some(username) = user { - return Ok(format!("Welcome back, {}! You were already logged in.", username)); + return Ok(format!( + "Welcome back, {}! You were already logged in.", + username + )); } debug!("Performing login attempt for user {}", &form.username); // No cookie, or cookie is invalid. Perform login. - if state.check_login(&form.username, &form.password).await.unwrap() { - debug!("Login was successful for user {}. Saving cookie now.", &form.username); - let session = state.sessions.make_session(COOKIE_NAME, &form.username).await; + if state + .check_login(&form.username, &form.password) + .await + .unwrap() + { + debug!( + "Login was successful for user {}. Saving cookie now.", + &form.username + ); + let session = state + .sessions + .make_session(COOKIE_NAME, &form.username) + .await; cookies.add(session.cookie()); Ok(format!("Welcome {}", session.username())) } else { diff --git a/src/routes/logout.rs b/src/routes/logout.rs index 800bd2f..441f901 100644 --- a/src/routes/logout.rs +++ b/src/routes/logout.rs @@ -1,4 +1,4 @@ -use tower_cookies::{Cookies, Cookie}; +use tower_cookies::{Cookie, Cookies}; use std::borrow::Cow; @@ -7,7 +7,10 @@ use crate::state::LoggedOutUser; pub async fn route(user: Option, cookies: Cookies) -> String { if let Some(user) = user { let cookie = user.cookie(); - cookies.remove(Cookie::new(Cow::Owned(cookie.name().to_string()), Cow::Owned(cookie.value().to_string()))); + cookies.remove(Cookie::new( + Cow::Owned(cookie.name().to_string()), + Cow::Owned(cookie.value().to_string()), + )); return format!("Goodbye, {}. You are now logged out.", user.username()); } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 384ddd7..a2a165a 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -21,8 +21,7 @@ pub fn router(subpath: Option, state: RoutableAppState) -> Router { .layer(CookieManagerLayer::new()) .with_state(state); if let Some(p) = subpath { - Router::new() - .nest(&p, app) + Router::new().nest(&p, app) } else { app } diff --git a/src/state/login.rs b/src/state/login.rs index 8384974..0716ccc 100644 --- a/src/state/login.rs +++ b/src/state/login.rs @@ -1,15 +1,11 @@ -use axum::{ - extract::FromRequestParts, - http::request::Parts, - RequestPartsExt, -}; +use axum::{extract::FromRequestParts, http::request::Parts, RequestPartsExt}; use snafu::prelude::*; use tower_cookies::Cookies; use yunohost_api::Username; use crate::{ error::*, - state::{COOKIE_NAME, RoutableAppState, sessions::*}, + state::{sessions::*, RoutableAppState, COOKIE_NAME}, }; #[derive(Clone, Debug)] @@ -33,8 +29,6 @@ impl LoggedInUser { pub fn timestamp(&self) -> i64 { self.timestamp } - - } impl From for LoggedInUser { @@ -59,30 +53,51 @@ impl AsRef for LoggedInUser { impl FromRequestParts for LoggedInUser { type Rejection = Error; - async fn from_request_parts(parts: &mut Parts, state: &RoutableAppState) -> Result { + async fn from_request_parts( + parts: &mut Parts, + state: &RoutableAppState, + ) -> Result { let cookies: Cookies = parts.extract().await.map_err(|_| Error::Cookie)?; - trace!("[SESSION:{}/login] Checking if the user has a valid session", COOKIE_NAME); + trace!( + "[SESSION:{}/login] Checking if the user has a valid session", + COOKIE_NAME + ); let session_cookie = if let Some(session_cookie) = cookies.get(COOKIE_NAME) { - trace!("[SESSION:{}/login] User claims to have a session with cookie: {}", COOKIE_NAME, &session_cookie); + trace!( + "[SESSION:{}/login] User claims to have a session with cookie: {}", + COOKIE_NAME, + &session_cookie + ); session_cookie } else { trace!("[SESSION:{}/login] 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:{}/login] User {} resumed session.", COOKIE_NAME, &session.username); + if let Some(session) = state + .sessions + .verify_cookie_session(session_cookie.value()) + .await + .context(SessionSnafu)? + { + debug!( + "[SESSION:{}/login] User {} resumed session.", + COOKIE_NAME, &session.username + ); return Ok(session.into()); } else { - trace!("[SESSION:{}/login] User session is invalid or has expired.", COOKIE_NAME); + trace!( + "[SESSION:{}/login] User session is invalid or has expired.", + COOKIE_NAME + ); return Err(Error::Session { source: SessionError::InvalidOrExpired { cookie_name: COOKIE_NAME.to_string(), - } + }, }); } } diff --git a/src/state/logout.rs b/src/state/logout.rs index ed0fde2..a036039 100644 --- a/src/state/logout.rs +++ b/src/state/logout.rs @@ -1,16 +1,12 @@ -use axum::{ - extract::FromRequestParts, - http::request::Parts, - RequestPartsExt, -}; +use axum::{extract::FromRequestParts, http::request::Parts, RequestPartsExt}; use snafu::prelude::*; use std::borrow::Cow; -use tower_cookies::{Cookies, Cookie}; +use tower_cookies::{Cookie, Cookies}; use yunohost_api::Username; use crate::{ error::*, - state::{COOKIE_NAME, RoutableAppState, sessions::*}, + state::{sessions::*, RoutableAppState, COOKIE_NAME}, }; pub struct LoggedOutUser(Session); @@ -28,9 +24,9 @@ impl LoggedOutUser { Cookie::build( Cow::Owned(self.0.cookie_name.to_string()), Cow::Owned(self.0.content.to_string()), - ).path( - Cow::Owned("/".to_string()) - ).finish() + ) + .path(Cow::Owned("/".to_string())) + .finish() } } @@ -44,25 +40,47 @@ impl From for LoggedOutUser { impl FromRequestParts for LoggedOutUser { type Rejection = Error; - async fn from_request_parts(parts: &mut Parts, state: &RoutableAppState) -> Result { + async fn from_request_parts( + parts: &mut Parts, + state: &RoutableAppState, + ) -> Result { let cookies: Cookies = parts.extract().await.map_err(|_| Error::Cookie)?; - trace!("[SESSION:{}/logout] Checking if the user has a valid session", COOKIE_NAME); + 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); + 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 { + 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); @@ -70,15 +88,18 @@ impl FromRequestParts for LoggedOutUser { 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); + 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(), - } + }, }); } } diff --git a/src/state/mod.rs b/src/state/mod.rs index 206582c..78c4e68 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -1,5 +1,5 @@ use snafu::prelude::*; -use yunohost_api::{YunohostUsers, Username, Password, YunohostPermissions}; +use yunohost_api::{Password, Username, YunohostPermissions, YunohostUsers}; use std::sync::Arc; @@ -33,7 +33,14 @@ impl AppState { }) } - pub async fn check_login(&self, username: &Username, password: &Password) -> Result { - self.users.check_credentials(username, password).await.context(YunohostSnafu) + pub async fn check_login( + &self, + username: &Username, + password: &Password, + ) -> Result { + self.users + .check_credentials(username, password) + .await + .context(YunohostSnafu) } } diff --git a/src/state/sessions.rs b/src/state/sessions.rs index 926264e..f79e549 100644 --- a/src/state/sessions.rs +++ b/src/state/sessions.rs @@ -1,4 +1,4 @@ -use ring::{hmac,rand}; +use ring::{hmac, rand}; use snafu::prelude::*; use std::borrow::Cow; use tokio::sync::RwLock; @@ -16,9 +16,15 @@ pub enum SessionError { #[snafu(display("Malformed cookie: {}", content))] MalformedCookie { content: String }, #[snafu(display("Malformed hex cookie signature: {}", sig))] - MalformedCookieSig { sig: String, source: hex::FromHexError }, + MalformedCookieSig { + sig: String, + source: hex::FromHexError, + }, #[snafu(display("Malformed cookie UNIX timestamp: {}", timestamp))] - MalformedCookieTimestamp { timestamp: String, source: std::num::ParseIntError }, + MalformedCookieTimestamp { + timestamp: String, + source: std::num::ParseIntError, + }, #[snafu(display("Malformed cookie username (empty)"))] MalformedCookieUsername { source: yunohost_api::Error }, #[snafu(display("Invalid or expired session {}", cookie_name))] @@ -44,7 +50,6 @@ pub struct SessionManager { pub invalidated_cookies: RwLock>, /// Expiration duration for set cookies, in seconds pub expiration_secs: u64, - } impl SessionManager { @@ -59,7 +64,7 @@ impl SessionManager { cookies: RwLock::new(Vec::new()), invalidated_cookies: RwLock::new(Vec::new()), // TODO: make expiration configurable - expiration_secs: 3600 * 24 * 7 + expiration_secs: 3600 * 24 * 7, }) } @@ -67,15 +72,24 @@ impl SessionManager { /// - Err(_) if the cookie format is really wrong /// - Ok(Some(Session)) if the user is still logged in /// - Ok(None) if the user is no longer lgoged in (invalid/expired cookie) - pub async fn verify_cookie_session(&self, cookie_claim: &str) -> Result, SessionError> { + pub async fn verify_cookie_session( + &self, + cookie_claim: &str, + ) -> Result, SessionError> { // First check the expiration of the claimed cookie. // If the timestamp was messed with, it will fail further verification. if let Some(claimed_timestamp) = Session::has_expired(cookie_claim, self.expiration_secs)? { // The claimed timestamp is still valid. Check if we ever had that cookie in memory. // If the server was restarted, user will have to login again. - if let Some(valid_cookie) = self.find_cookie(&self.cookies, claimed_timestamp, &cookie_claim).await { + if let Some(valid_cookie) = self + .find_cookie(&self.cookies, claimed_timestamp, &cookie_claim) + .await + { // Make sure the session hasn't been invalidated - if let Some(_invalidated_cookie) = self.find_cookie(&self.invalidated_cookies, claimed_timestamp, &cookie_claim).await { + if let Some(_invalidated_cookie) = self + .find_cookie(&self.invalidated_cookies, claimed_timestamp, &cookie_claim) + .await + { // User has logged out or been removed from the system Ok(None) } else { @@ -98,12 +112,10 @@ impl SessionManager { pub async fn make_session(&self, cookie_name: &str, username: &Username) -> Session { let now = now(); let signable_payload = format!("{now}:{cookie_name}:{username}"); - let signed_payload = hmac::sign(&self.secret, signable_payload.as_bytes()).as_ref().to_vec(); - let cookie_payload = format!( - "{}:{}", - signable_payload, - hex::encode(&signed_payload), - ); + let signed_payload = hmac::sign(&self.secret, signable_payload.as_bytes()) + .as_ref() + .to_vec(); + let cookie_payload = format!("{}:{}", signable_payload, hex::encode(&signed_payload),); let cookie = Session { cookie_name: cookie_name.to_string(), @@ -126,9 +138,16 @@ impl SessionManager { /// Invalidated a previously-set session cookie pub async fn invalidate_session(&self, timestamp: i64, content: &str) -> bool { if let Some(cookie) = self.find_cookie(&self.cookies, timestamp, content).await { - debug!("[SESSION:{}] User {} is logging out", &cookie.cookie_name, &cookie.username); + debug!( + "[SESSION:{}] User {} is logging out", + &cookie.cookie_name, &cookie.username + ); // Make sure the cookie was not already invalidated to avoid DOSing our invalidated cookie storage - if self.find_cookie(&self.invalidated_cookies, timestamp, content).await.is_none() { + if self + .find_cookie(&self.invalidated_cookies, timestamp, content) + .await + .is_none() + { let mut jar = self.invalidated_cookies.write().await; jar.push(cookie); return true; @@ -140,12 +159,20 @@ impl SessionManager { } /// Helper method to find a cookie with a specific timestamp, name and username in a cookie jar - async fn find_cookie(&self, jar: &RwLock>, timestamp: i64, content: &str) -> Option { - jar.read().await.iter().find(|cookie| { - // First compare the timestamp (cheapest operation to invalidate the match) - cookie.timestamp == timestamp - && cookie.content == content - }).cloned() + async fn find_cookie( + &self, + jar: &RwLock>, + timestamp: i64, + content: &str, + ) -> Option { + jar.read() + .await + .iter() + .find(|cookie| { + // First compare the timestamp (cheapest operation to invalidate the match) + cookie.timestamp == timestamp && cookie.content == content + }) + .cloned() } } @@ -167,13 +194,16 @@ pub struct Session { impl Session { /// Extrats the timestamp from a stringy cookie pub fn timestamp(cookie: &str) -> Result { - let (timestamp, _rest) = cookie.split_once(':') - .context(MalformedCookieSnafu { content: cookie.to_string() })?; - let timestamp: i64 = timestamp.parse().context(MalformedCookieTimestampSnafu { timestamp })?; + let (timestamp, _rest) = cookie.split_once(':').context(MalformedCookieSnafu { + content: cookie.to_string(), + })?; + let timestamp: i64 = timestamp + .parse() + .context(MalformedCookieTimestampSnafu { timestamp })?; Ok(timestamp) } - + /// Verifies whether a given cookie string has expired, before parsing it entirely. Returns: /// - Err(SessionError) when the cookie is malformed /// - Ok(Some(timestamp)) when the cookie is still valid @@ -193,7 +223,7 @@ impl Session { Ok(None) } } - + /// The typed [`yunohost_api::Username`] for which this cookie is deemed valid. pub fn username(&self) -> &Username { &self.username @@ -203,8 +233,8 @@ impl Session { Cookie::build( Cow::Owned(self.cookie_name.to_string()), Cow::Owned(self.content.to_string()), - ).path( - Cow::Owned("/".to_string()) - ).finish() + ) + .path(Cow::Owned("/".to_string())) + .finish() } } diff --git a/src/utils/fs.rs b/src/utils/fs.rs index 6c7ebee..baca535 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -1,15 +1,8 @@ use file_owner::PathExt; use snafu::prelude::*; -use tokio::{ - fs::set_permissions, - task::spawn_blocking, -}; +use tokio::{fs::set_permissions, task::spawn_blocking}; -use std::{ - fs::Permissions, - os::unix::fs::PermissionsExt, - path::Path, -}; +use std::{fs::Permissions, os::unix::fs::PermissionsExt, path::Path}; use crate::error::*; @@ -45,34 +38,41 @@ impl FSPermissions { pub async fn apply_to(&self, path: &Path) -> Result<(), Error> { if let Some(mode) = self.mode { - set_permissions( - path, - Permissions::from_mode(mode) - ).await.context(PermissionsSnafu { path: path.to_path_buf()})?; + set_permissions(path, Permissions::from_mode(mode)) + .await + .context(PermissionsSnafu { + path: path.to_path_buf(), + })?; } if let Some(owner) = &self.owner { let owner = owner.to_string(); let path = path.to_path_buf(); let _ = spawn_blocking(move || -> Result<(), Error> { - Ok( - path.set_owner(owner.as_str()) - .context(PermissionsChownSnafu { path: path.to_path_buf() })? - ) - }).await.context(TokioTaskSnafu)?; + Ok(path + .set_owner(owner.as_str()) + .context(PermissionsChownSnafu { + path: path.to_path_buf(), + })?) + }) + .await + .context(TokioTaskSnafu)?; } if let Some(group) = &self.group { let group = group.to_string(); let path = path.to_path_buf(); let _ = spawn_blocking(move || -> Result<(), Error> { - Ok( - path.set_group(group.as_str()) - .context(PermissionsChgrpSnafu { path: path.to_path_buf() })? - ) - }).await.context(TokioTaskSnafu)?; + Ok(path + .set_group(group.as_str()) + .context(PermissionsChgrpSnafu { + path: path.to_path_buf(), + })?) + }) + .await + .context(TokioTaskSnafu)?; } Ok(()) } -} \ No newline at end of file +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index dea1626..6d7238c 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,3 @@ pub mod fs; +pub mod socket; pub mod time; -pub mod socket; \ No newline at end of file diff --git a/src/utils/socket.rs b/src/utils/socket.rs index c6eb59b..df9cfe3 100644 --- a/src/utils/socket.rs +++ b/src/utils/socket.rs @@ -1,7 +1,4 @@ -use axum::{ - Router, - extract::connect_info, -}; +use axum::{extract::connect_info, Router}; use futures::ready; use hyper::{ client::connect::{Connected, Connection}, @@ -21,10 +18,7 @@ use tokio::{ }; use tower::BoxError; -use crate::{ - error::*, - utils::fs::FSPermissions, -}; +use crate::{error::*, utils::fs::FSPermissions}; pub struct ServerAccept { uds: UnixListener, @@ -32,9 +26,7 @@ pub struct ServerAccept { impl ServerAccept { pub fn new(uds: UnixListener) -> ServerAccept { - ServerAccept { - uds, - } + ServerAccept { uds } } } @@ -64,10 +56,7 @@ impl AsyncWrite for ClientConnection { Pin::new(&mut self.stream).poll_write(cx, buf) } - fn poll_flush( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - ) -> Poll> { + fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { Pin::new(&mut self.stream).poll_flush(cx) } @@ -121,16 +110,16 @@ pub async fn serve(path: &Path, app: Router) -> Result<(), Error> { .await .unwrap(); - let uds = UnixListener::bind(path.clone()) - .context(SocketCreateSnafu { path: path.clone() })?; + let uds = UnixListener::bind(path.clone()).context(SocketCreateSnafu { path: path.clone() })?; // TODO: make proper permissions // Apply 777 permissions FSPermissions::new().chmod(0o777).apply_to(&path).await?; - + hyper::Server::builder(ServerAccept::new(uds)) .serve(app.into_make_service_with_connect_info::()) - .await.context(ServerSnafu)?; + .await + .context(ServerSnafu)?; Ok(()) } diff --git a/src/utils/time.rs b/src/utils/time.rs index 86bc693..f5627dd 100644 --- a/src/utils/time.rs +++ b/src/utils/time.rs @@ -1,5 +1,5 @@ use chrono::Utc; pub fn now() -> i64 { - Utc::now().timestamp() + Utc::now().timestamp() } diff --git a/yunohost-api/src/lib.rs b/yunohost-api/src/lib.rs index bb9d62b..5a7bc90 100644 --- a/yunohost-api/src/lib.rs +++ b/yunohost-api/src/lib.rs @@ -1,3 +1,5 @@ +#[macro_use] extern crate log; + mod cache; pub use cache::JsonCache; mod credentials; diff --git a/yunohost-api/src/permissions/ssowat.rs b/yunohost-api/src/permissions/ssowat.rs index 60e87ae..c1aa81b 100644 --- a/yunohost-api/src/permissions/ssowat.rs +++ b/yunohost-api/src/permissions/ssowat.rs @@ -22,6 +22,7 @@ impl SSOWatConfig { if let Some(domain) = uri.domain() { if ! self.domains.contains(&domain.to_string()) { // Domain not managed + trace!("Domain {} not managed by Yunohost", &domain); return None; } @@ -31,19 +32,25 @@ impl SSOWatConfig { .trim_start_matches("s") .trim_start_matches("://"); + trace!("Checking permissions for {}", stripped_uri); + // Check which app matches this URI, to find corresponding permission for (key, val) in &self.permissions { for uri_format in &val.uris { if uri_format.starts_with("re:") { + trace!("Checking if URI matches regex: {}", uri_format); let uri_format = uri_format.trim_start_matches("re:"); // TODO: generate regex in advance // TODO: error let re = Regex::new(uri_format).unwrap(); if re.is_match(stripped_uri) { + trace!("Found URI matches regex app: {}", key); return Some(key.clone()); } } else { + trace!("Checking if URI starts with: {}", uri_format); if stripped_uri.starts_with(uri_format) { + trace!("Found URI matches app: {}", key); return Some(key.clone()); } } @@ -51,9 +58,11 @@ impl SSOWatConfig { } // No app URI matched + trace!("No application matched for URI {}", stripped_uri); return None; } else { // No domain (eg. http://8.8.8.8/) + trace!("No domain requested for permission request"); return None; } @@ -96,3 +105,9 @@ pub struct Permission { pub struct PermissionName { name: String, } + +impl std::fmt::Display for PermissionName { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.name.fmt(fmt) + } +}