From 0b6ca5cb8ca4c46265e08e13053260d9b5cff568 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Thu, 11 Jun 2026 18:02:55 +0200 Subject: feat(server): Make user stuff stateless --- crates/turtle/src/atuin_client/auth.rs | 181 --------------------------------- 1 file changed, 181 deletions(-) delete mode 100644 crates/turtle/src/atuin_client/auth.rs (limited to 'crates/turtle/src/atuin_client/auth.rs') diff --git a/crates/turtle/src/atuin_client/auth.rs b/crates/turtle/src/atuin_client/auth.rs deleted file mode 100644 index 620e127e..00000000 --- a/crates/turtle/src/atuin_client/auth.rs +++ /dev/null @@ -1,181 +0,0 @@ -use eyre::{Context, Result, bail}; -use reqwest::{Url, header::USER_AGENT}; - -use crate::{ - atuin_client::api_client, - atuin_common::{ - api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest}, - tls::ensure_crypto_provider, - }, -}; - -use crate::atuin_client::settings::Settings; - -static APP_USER_AGENT: &str = concat!("atuin/", env!("CARGO_PKG_VERSION")); - -/// Result of an auth operation -pub(crate) struct AuthResponse { - pub(crate) session: String, -} - -/// Resolve the appropriate [`AuthClient`] for the current settings. -pub(crate) async fn auth_client(settings: &Settings) -> LegacyAuthClient { - LegacyAuthClient::new( - &settings.sync_address, - settings.session_token().await.ok(), - settings.network_connect_timeout, - settings.network_timeout, - ) -} - -// --------------------------------------------------------------------------- -// Legacy backend — talks to the Rust sync server -// --------------------------------------------------------------------------- - -pub(crate) struct LegacyAuthClient { - address: String, - session_token: Option, - connect_timeout: u64, - timeout: u64, -} - -impl LegacyAuthClient { - pub(crate) fn new( - address: &str, - session_token: Option, - connect_timeout: u64, - timeout: u64, - ) -> Self { - Self { - address: address.to_string(), - session_token, - connect_timeout, - timeout, - } - } - - fn authenticated_client(&self) -> Result { - let token = self - .session_token - .as_deref() - .ok_or_else(|| eyre::eyre!("Not logged in"))?; - - ensure_crypto_provider(); - let mut headers = reqwest::header::HeaderMap::new(); - headers.insert( - reqwest::header::AUTHORIZATION, - format!("Token {token}").parse()?, - ); - headers.insert(USER_AGENT, APP_USER_AGENT.parse()?); - headers.insert(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION.parse()?); - - Ok(reqwest::Client::builder() - .default_headers(headers) - .connect_timeout(std::time::Duration::new(self.connect_timeout, 0)) - .timeout(std::time::Duration::new(self.timeout, 0)) - .build()?) - } -} - -impl LegacyAuthClient { - /// Log in with username + password, optionally providing a TOTP code. - pub(crate) async fn login(&self, username: &str, password: &str) -> Result { - // The legacy server has no 2FA support; totp_code is ignored. - let resp = api_client::login( - &self.address, - LoginRequest { - username: username.to_string(), - password: password.to_string(), - }, - ) - .await?; - - Ok(AuthResponse { - session: resp.session, - }) - } - - /// Register a new account. - pub(crate) async fn register( - &self, - username: &str, - email: &str, - password: &str, - ) -> Result { - let resp = api_client::register(&self.address, username, email, password).await?; - Ok(AuthResponse { - session: resp.session, - }) - } - - /// Change the account password, optionally providing a TOTP code. - pub(crate) async fn change_password( - &self, - current_password: &str, - new_password: &str, - _totp_code: Option<&str>, - ) -> Result<()> { - let client = self.authenticated_client()?; - let url = make_url(&self.address, "/account/password")?; - - let resp = client - .patch(&url) - .json(&ChangePasswordRequest { - current_password: current_password.to_string(), - new_password: new_password.to_string(), - }) - .send() - .await?; - - match resp.status().as_u16() { - 200 => Ok(()), - 401 => bail!("current password is incorrect"), - 403 => bail!("invalid login details"), - _ => bail!("unknown error"), - } - } - - /// Delete the account, requiring the current password and optionally a TOTP code. - pub(crate) async fn delete_account( - &self, - password: &str, - _totp_code: Option<&str>, - ) -> Result<()> { - let client = self.authenticated_client()?; - let url = make_url(&self.address, "/account")?; - - let resp = client - .delete(&url) - .json(&serde_json::json!({ "password": password })) - .send() - .await?; - - match resp.status().as_u16() { - 200 => Ok(()), - 401 => bail!("password is incorrect"), - 403 => bail!("invalid login details"), - _ => bail!("unknown error"), - } - } -} - -// --------------------------------------------------------------------------- -// Shared helpers -// --------------------------------------------------------------------------- - -fn make_url(address: &str, path: &str) -> Result { - let address = if address.ends_with('/') { - address.to_string() - } else { - format!("{address}/") - }; - - let path = path.strip_prefix('/').unwrap_or(path); - - let url = Url::parse(&address) - .context("failed to parse server address")? - .join(path) - .context("failed to join URL path")?; - - Ok(url.to_string()) -} -- cgit v1.3.1