🗝
summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/config.rs2
-rw-r--r--src/server/falx.rs61
-rw-r--r--src/server/mod.rs12
-rw-r--r--src/server/nginx_check.rs41
4 files changed, 72 insertions, 44 deletions
diff --git a/src/server/config.rs b/src/server/config.rs
index 4563f34..31d1e1f 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -1,6 +1,7 @@
 use std::net::SocketAddr;
 use std::path::{Path, PathBuf};
 
+use cursive::reexports::ahash::HashSet;
 use eyre::Context;
 use tap::Pipe;
 
@@ -10,6 +11,7 @@ pub struct Config {
     pub web_base: String,
     pub cookie_domain: Option<String>,
     pub admin_socket: PathBuf,
+    pub handoffs: HashSet<String>,
     pub data: PathBuf,
 }
 
diff --git a/src/server/falx.rs b/src/server/falx.rs
new file mode 100644
index 0000000..03a8a0b
--- /dev/null
+++ b/src/server/falx.rs
@@ -0,0 +1,61 @@
+use axum::{
+    extract::{Path, State},
+    http::{HeaderMap, StatusCode, Uri},
+    response::{IntoResponse, Response},
+    routing::get,
+    Router,
+};
+use axum_extra::extract::CookieJar;
+
+use crate::server::store::Store;
+
+use super::{ApiState, Handoffs};
+
+pub fn bind(app: Router<ApiState>) -> Router<ApiState> {
+    app.route("/check/:token/:scope", get(check_))
+        .route("/handoff", get(handoff))
+}
+
+#[axum::debug_handler(state = ApiState)]
+async fn check_(
+    Path((token, scope)): Path<(String, String)>,
+    State(store): State<Store>,
+) -> Response {
+    let Some(name) = store.check_token(&token).await else {
+        return StatusCode::UNAUTHORIZED.into_response();
+    };
+    let Some(account) = store.get_account(&name).await else {
+        return StatusCode::UNAUTHORIZED.into_response();
+    };
+    if account.scopes.contains(&scope) {
+        StatusCode::OK.into_response()
+    } else {
+        StatusCode::FORBIDDEN.into_response()
+    }
+}
+
+#[axum::debug_handler(state = ApiState)]
+async fn handoff(
+    jar: CookieJar,
+    State(Handoffs(handoffs)): State<Handoffs>,
+    headers: HeaderMap,
+) -> Response {
+    let Some(origin) = headers.get("Origin") else {
+        return (StatusCode::BAD_REQUEST, "Missing Origin header").into_response();
+    };
+    let Some(origin) = origin
+        .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 Some(token) = jar.get("dissociate-token") else {
+        return (StatusCode::UNAUTHORIZED, "Authenticate cookie missing").into_response();
+    };
+    return (StatusCode::OK, token.value().to_string()).into_response();
+}
diff --git a/src/server/mod.rs b/src/server/mod.rs
index d9b3beb..81dc519 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -1,11 +1,11 @@
 mod admin;
 mod config;
+mod falx;
 mod login;
-mod nginx_check;
 mod panel;
 mod store;
 
-use std::{future::IntoFuture, path::PathBuf};
+use std::{future::IntoFuture, path::PathBuf, sync::Arc};
 
 use axum::{
     body::Body,
@@ -16,6 +16,7 @@ use axum::{
     Router,
 };
 use axum_extra::extract::CookieJar;
+use cursive::reexports::ahash::HashSet;
 use eyre::Context;
 use maud::{html, PreEscaped};
 use tap::Pipe;
@@ -41,12 +42,13 @@ pub async fn serve() -> eyre::Result<()> {
 
     let app = Router::new()
         .pipe(login::bind)
-        .pipe(nginx_check::bind)
+        .pipe(falx::bind)
         .pipe(panel::bind)
         .with_state(ApiState {
             store,
             cookie_domain: CookieDomain(config.cookie_domain),
             web_base: WebBase(config.web_base),
+            handoffs: Handoffs(Arc::new(config.handoffs)),
         })
         .fallback(get(|| async {
             render_html(
@@ -72,6 +74,7 @@ struct ApiState {
     pub store: Store,
     pub cookie_domain: CookieDomain,
     pub web_base: WebBase,
+    pub handoffs: Handoffs,
 }
 
 #[derive(Clone)]
@@ -80,6 +83,9 @@ struct CookieDomain(Option<String>);
 #[derive(Clone)]
 struct WebBase(String);
 
+#[derive(Clone)]
+struct Handoffs(Arc<HashSet<String>>);
+
 fn render_html(head: PreEscaped<impl AsRef<str>>, body: PreEscaped<impl AsRef<str>>) -> Response {
     let html = html! {
         (PreEscaped("<!doctype html>"))
diff --git a/src/server/nginx_check.rs b/src/server/nginx_check.rs
deleted file mode 100644
index 7b67f26..0000000
--- a/src/server/nginx_check.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-// for ngx_http_auth_request_module authentication
-// make sure you have cookie_domain set properly
-// depends on https://git.mia.jetzt/sysconf/tree/patches/nginx_auth_redirect.patch
-
-use axum::{
-    extract::{Path, State},
-    http::StatusCode,
-    response::{IntoResponse, Redirect, Response},
-    routing::get,
-    Router,
-};
-use axum_extra::extract::CookieJar;
-
-use crate::server::{account_auth, store::Store};
-
-use super::{ApiState, WebBase};
-
-pub fn bind(app: Router<ApiState>) -> Router<ApiState> {
-    app.route("/nginx_check/:scope", get(nginx_check))
-}
-
-#[axum::debug_handler(state = ApiState)]
-async fn nginx_check(
-    jar: CookieJar,
-    Path(scope): Path<String>,
-    State(store): State<Store>,
-    State(WebBase(web_base)): State<WebBase>,
-) -> Response {
-    let nevermind = || Redirect::to(&format!("{web_base}/logout")).into_response();
-    let Some(name) = account_auth(&jar, &store).await else {
-        return nevermind();
-    };
-    let Some(account) = store.get_account(&name).await else {
-        return nevermind();
-    };
-    if account.scopes.contains(&scope) {
-        StatusCode::OK.into_response()
-    } else {
-        StatusCode::FORBIDDEN.into_response()
-    }
-}