diff options
Diffstat (limited to 'src/server/account.rs')
-rw-r--r-- | src/server/account.rs | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/src/server/account.rs b/src/server/account.rs new file mode 100644 index 0000000..b2c294f --- /dev/null +++ b/src/server/account.rs @@ -0,0 +1,104 @@ +use std::time::SystemTime; + +use axum::{ + body::Body, + extract::State, + http::{HeaderMap, StatusCode, Uri}, + response::{IntoResponse, Response}, + routing::get, + Router, +}; +use axum_extra::extract::CookieJar; +use maud::html; +use tap::Pipe; + +use crate::server::{store::Store, Handoffs, Nevermind}; + +use super::{account_auth, render_html, ApiState}; + +pub fn bind(app: Router<ApiState>) -> Router<ApiState> { + app.route("/", get(get_panel)) + .route("/scopes", get(get_scopes)) + .route("/handoff", get(get_handoff)) +} + +#[axum::debug_handler(state = ApiState)] +async fn get_panel(jar: CookieJar, State(store): State<Store>) -> Result<Response, Response> { + Ok(account_auth(&jar, &store) + .await + .prompt_login()? + .pipe(render_normal_panel)) +} + +fn render_normal_panel(name: String) -> Response { + render_html( + html!(title { "dissociate" }), + html! { + p { "currently logged in as " (name) } + a href="/logout" { button { "log out" } } + }, + ) +} + +#[axum::debug_handler(state = ApiState)] +async fn get_scopes(jar: CookieJar, State(store): State<Store>) -> Result<Response, Response> { + let name = account_auth(&jar, &store).await.prompt_login()?; + let account = store.get_account(&name).await.prompt_logout()?; + let body = account.scopes.join(" "); + + Ok(Response::builder() + .status(StatusCode::OK) + .header("Content-Type", "text/plain") + .body(body.into()) + .unwrap()) +} + +#[axum::debug_handler(state = ApiState)] +async fn get_handoff( + jar: CookieJar, + State(Handoffs(handoffs)): State<Handoffs>, + State(store): State<Store>, + headers: HeaderMap, +) -> Response { + let Some(origin_header) = headers.get("Origin") else { + return (StatusCode::BAD_REQUEST, "Missing Origin header").into_response(); + }; + let Some(origin) = origin_header + .to_str() + .ok() + .and_then(|origin| origin.parse::<Uri>().ok()) + .and_then(|origin| origin.host().map(ToString::to_string)) + else { + return (StatusCode::BAD_REQUEST, "Malformed Origin header").into_response(); + }; + if !handoffs.contains(&origin) { + return (StatusCode::FORBIDDEN, "Origin not registered for handoff").into_response(); + } + + let builder = Response::builder() + .header("Access-Control-Allow-Credentials", "true") + .header("Access-Control-Allow-Origin", origin_header); + + if let Some(token) = jar.get("dissociate-token") { + if let Some((_, expires)) = store.check_token(token.value()).await { + let expires_in = expires + .duration_since(SystemTime::now()) + .unwrap_or_default() + .as_secs(); + + return builder + .status(StatusCode::OK) + .body(Body::from(format!( + r#"{{"token":"{}","expiresIn":{}}}"#, + token.value().to_string(), + expires_in, + ))) + .unwrap(); + } + } + + builder + .status(StatusCode::UNAUTHORIZED) + .body(Body::empty()) + .unwrap() +} |