Skip to content

Commit

Permalink
♻️ Migrate from Rocket to Axum
Browse files Browse the repository at this point in the history
  • Loading branch information
RemiBardon committed Aug 25, 2024
1 parent eab22d8 commit 1e49ad6
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 179 deletions.
100 changes: 0 additions & 100 deletions src/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ resolver = "2"

[workspace.dependencies]
axum = { version = "0.7", features = ["macros"] }
axum-extra = { version = "0.9", features = ["cookie-private"] }
axum-extra = { version = "0.9", features = ["cookie"] }
base64 = "0.22"
biscuit-auth = "5"
chrono = "0.4"
Expand Down
1 change: 0 additions & 1 deletion src/helpers/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ pub const THEME_NAME: &str = "Orangutan";
pub const DATA_FILE_EXTENSION: &str = "orangutan";
pub const DEFAULT_PROFILE: &str = "_default";
pub const ROOT_KEY_NAME: &'static str = "_biscuit_root";
pub const COOKIE_KEY_ENV_VAR_NAME: &'static str = "COOKIE_ENCRYPTION_KEY";

pub(super) const WEBSITE_DIR_NAME: &'static str = "website";

Expand Down
30 changes: 2 additions & 28 deletions src/orangutan-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,16 @@ use std::{fs, process::ExitCode};

use axum::{
body::Body,
extract::FromRef,
http::{Request, StatusCode},
middleware,
response::{IntoResponse, Response},
Router,
};
use axum_extra::extract::cookie::Key;
use orangutan_helpers::{
config::COOKIE_KEY_ENV_VAR_NAME,
generate::{self, *},
website_id::WebsiteId,
};
use request_guards::{handle_refresh_token, migrate_token, REVOKED_TOKENS};
use request_guards::{handle_refresh_token, REVOKED_TOKENS};
use tera::Tera;
use tokio::runtime::Handle;
use tower::Service;
Expand All @@ -40,17 +37,10 @@ use crate::{config::NOT_FOUND_FILE, routes::update_content_routes, util::error};
#[derive(Clone)]
struct AppState {
website_root: WebsiteRoot,
cookie_key: Key,
#[cfg(feature = "templating")]
tera: Tera,
}

impl FromRef<AppState> for Key {
fn from_ref(state: &AppState) -> Self {
state.cookie_key.clone()
}
}

#[tokio::main]
async fn main() -> ExitCode {
let website_root = match WebsiteRoot::try_from_env() {
Expand All @@ -63,14 +53,6 @@ async fn main() -> ExitCode {

let mut app_state = AppState {
website_root,
// FIXME: Use predefined key.
cookie_key: Key::from(
std::env::var(COOKIE_KEY_ENV_VAR_NAME)
.expect(&format!(
"Environment variable '{COOKIE_KEY_ENV_VAR_NAME}' not defined."
))
.as_bytes(),
),
#[cfg(feature = "templating")]
tera: Default::default(),
};
Expand All @@ -93,15 +75,7 @@ async fn main() -> ExitCode {
let app = Router::new()
.nest("/", routes::router())
.layer(TraceLayer::new_for_http())
.route_layer(middleware::from_fn_with_state(
app_state.clone(),
handle_refresh_token,
))
// NOTE: Layers are ran in reverse order of insertion.
.route_layer(middleware::from_fn_with_state(
app_state.clone(),
migrate_token,
))
.route_layer(middleware::from_fn(handle_refresh_token))
.with_state(app_state);
// .register("/", catchers![unauthorized, forbidden, not_found])

Expand Down
45 changes: 7 additions & 38 deletions src/orangutan-server/src/request_guards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ use std::{
};

use axum::{
extract::{rejection::QueryRejection, FromRef, FromRequestParts, Query, Request, State},
extract::{rejection::QueryRejection, FromRequestParts, Query, Request},
http::{request, HeaderMap, HeaderValue, Uri},
middleware::Next,
response::{IntoResponse, Redirect, Response},
};
use axum_extra::{
either::Either,
extract::{cookie::Key, CookieJar, PrivateCookieJar},
};
use axum_extra::{either::Either, extract::CookieJar};
use biscuit_auth::{macros::authorizer, Biscuit};
use lazy_static::lazy_static;
use serde::Deserialize;
Expand All @@ -24,7 +21,6 @@ use tracing::{debug, trace};
use crate::{
config::*,
util::{add_cookie, add_padding, profiles},
AppState,
};

lazy_static! {
Expand Down Expand Up @@ -71,7 +67,6 @@ impl IntoResponse for TokenError {
impl<S> FromRequestParts<S> for Token
where
S: Send + Sync,
Key: FromRef<S>,
{
type Rejection = TokenError;

Expand Down Expand Up @@ -116,9 +111,7 @@ where
}

// Check cookies
// NOTE: For some reason, `rustc` can't figure out the type here… so we need to explicit it.
// It worked before, so it will probably wagically work again later. It just doesn't work now.
let cookies = PrivateCookieJar::<Key>::from_request_parts(parts, state)
let cookies = CookieJar::from_request_parts(parts, state)
.await
.map_err(|err| match err {})
.unwrap();
Expand Down Expand Up @@ -170,29 +163,6 @@ where
}
}

pub async fn migrate_token(
mut cookies: CookieJar,
mut private_cookies: PrivateCookieJar,
req: Request,
next: Next,
) -> Either<Response, (CookieJar, PrivateCookieJar, Redirect)> {
// trace!("Running token migration middleware…");
let Some(cookie) = cookies.get(TOKEN_COOKIE_NAME).cloned() else {
trace!("Token migration middleware found no outdated token, forwarding…");
return Either::E1(next.run(req).await);
};
trace!("Migrating outdated token…");

cookies = cookies.remove(cookie.clone());
private_cookies = private_cookies.add(cookie.clone());

Either::E2((
cookies,
private_cookies,
Redirect::to(&req.uri().to_string()),
))
}

#[derive(Deserialize)]
pub struct RefreshTokenQuery {
#[serde(default)]
Expand All @@ -202,8 +172,7 @@ pub struct RefreshTokenQuery {
}

pub async fn handle_refresh_token(
State(_app_state): State<AppState>,
cookies: PrivateCookieJar,
cookies: CookieJar,
Query(RefreshTokenQuery {
refresh_token,
force,
Expand All @@ -212,7 +181,7 @@ pub async fn handle_refresh_token(
token: Option<Token>,
req: Request,
next: Next,
) -> Result<Either<Response, (PrivateCookieJar, Redirect)>, crate::Error> {
) -> Result<Either<Response, (CookieJar, Redirect)>, crate::Error> {
trace!("Running refresh token middleware…");
let Some(refresh_token) = refresh_token else {
trace!("Refresh token middleware found no refresh token, forwarding…");
Expand Down Expand Up @@ -280,8 +249,8 @@ pub async fn handle_refresh_token(
fn redirect_to_same_page_without_query_param(
uri: &Uri,
query: &mut HashMap<String, String>,
cookies: PrivateCookieJar,
) -> Result<(PrivateCookieJar, Redirect), crate::Error> {
cookies: CookieJar,
) -> Result<(CookieJar, Redirect), crate::Error> {
query.remove(REFRESH_TOKEN_QUERY_PARAM_NAME);
// TODO: Check if we need to URL-encode keys and values or if they are still encoded.
let query_segs: Vec<String> = query.iter().map(|(k, v)| format!("{k}={v}")).collect();
Expand Down
6 changes: 3 additions & 3 deletions src/orangutan-server/src/routes/debug_routes.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::{Arc, RwLock};

use axum::{routing::get, Router};
use axum_extra::extract::PrivateCookieJar;
use axum_extra::extract::CookieJar;
use chrono::{DateTime, Utc};
use lazy_static::lazy_static;

Expand Down Expand Up @@ -48,10 +48,10 @@ pub(super) fn templates() -> Vec<(&'static str, &'static str)> {
}

// #[axum::debug_handler]
async fn clear_cookies(cookie_jar: PrivateCookieJar) -> (PrivateCookieJar, String) {
async fn clear_cookies(cookie_jar: CookieJar) -> (CookieJar, String) {
let mut empty_jar = cookie_jar.clone();
for cookie in cookie_jar.iter() {
empty_jar = empty_jar.remove(cookie);
empty_jar = empty_jar.remove(cookie.clone());
}

(empty_jar, "Success".to_string())
Expand Down
Loading

0 comments on commit 1e49ad6

Please sign in to comment.