diff options
| author | Yannick Ulrich <yannick.ulrich@durham.ac.uk> | 2023-05-16 22:00:59 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-05-16 22:00:59 +0100 |
| commit | 7b9dea72e3d2435f75825e8e66a04285332d5aa5 (patch) | |
| tree | e2b9ae2f8df86105b09547a55dc7ac455a55c0c8 | |
| parent | Include bash preexec warning (#983) (diff) | |
| download | atuin-7b9dea72e3d2435f75825e8e66a04285332d5aa5.zip | |
feat: add delete account option (attempt 2) (#980)
* Added DELETE register endpoint
* Added remove function to database
* Added unregister to client
* Updated docs
* Renamed functions
* Reformatting
* Used execute instead of fetch in delete_user
| -rw-r--r-- | atuin-client/src/api_client.rs | 15 | ||||
| -rw-r--r-- | atuin-common/src/api.rs | 3 | ||||
| -rw-r--r-- | atuin-server/src/database.rs | 21 | ||||
| -rw-r--r-- | atuin-server/src/handlers/user.rs | 19 | ||||
| -rw-r--r-- | atuin-server/src/router.rs | 1 | ||||
| -rw-r--r-- | atuin/src/command/client/sync.rs | 5 | ||||
| -rw-r--r-- | atuin/src/command/client/sync/delete.rs | 23 | ||||
| -rw-r--r-- | docs/docs/commands/sync.md | 8 |
8 files changed, 94 insertions, 1 deletions
diff --git a/atuin-client/src/api_client.rs b/atuin-client/src/api_client.rs index 80051ba6..2abb8159 100644 --- a/atuin-client/src/api_client.rs +++ b/atuin-client/src/api_client.rs @@ -217,4 +217,19 @@ impl<'a> Client<'a> { Ok(()) } + + pub async fn delete(&self) -> Result<()> { + let url = format!("{}/register", self.sync_addr); + let url = Url::parse(url.as_str())?; + + let resp = self.client.delete(url).send().await?; + + if resp.status() == 403 { + bail!("invalid login details"); + } else if resp.status() == 200 { + Ok(()) + } else { + bail!("Unknown error"); + } + } } diff --git a/atuin-common/src/api.rs b/atuin-common/src/api.rs index e9809329..2eff464e 100644 --- a/atuin-common/src/api.rs +++ b/atuin-common/src/api.rs @@ -20,6 +20,9 @@ pub struct RegisterResponse { } #[derive(Debug, Serialize, Deserialize)] +pub struct DeleteUserResponse {} + +#[derive(Debug, Serialize, Deserialize)] pub struct LoginRequest { pub username: String, pub password: String, diff --git a/atuin-server/src/database.rs b/atuin-server/src/database.rs index 7f3e5dac..e7057f6b 100644 --- a/atuin-server/src/database.rs +++ b/atuin-server/src/database.rs @@ -27,6 +27,7 @@ pub trait Database { async fn get_user(&self, username: &str) -> Result<User>; async fn get_user_session(&self, u: &User) -> Result<Session>; async fn add_user(&self, user: &NewUser) -> Result<i64>; + async fn delete_user(&self, u: &User) -> Result<()>; async fn count_history(&self, user: &User) -> Result<i64>; async fn count_history_cached(&self, user: &User) -> Result<i64>; @@ -337,6 +338,26 @@ impl Database for Postgres { } #[instrument(skip_all)] + async fn delete_user(&self, u: &User) -> Result<()> { + sqlx::query("delete from sessions where user_id = $1") + .bind(u.id) + .execute(&self.pool) + .await?; + + sqlx::query("delete from users where id = $1") + .bind(u.id) + .execute(&self.pool) + .await?; + + sqlx::query("delete from history where user_id = $1") + .bind(u.id) + .execute(&self.pool) + .await?; + + Ok(()) + } + + #[instrument(skip_all)] async fn add_user(&self, user: &NewUser) -> Result<i64> { let email: &str = &user.email; let username: &str = &user.username; diff --git a/atuin-server/src/handlers/user.rs b/atuin-server/src/handlers/user.rs index 89aa0601..ec2131e1 100644 --- a/atuin-server/src/handlers/user.rs +++ b/atuin-server/src/handlers/user.rs @@ -18,7 +18,7 @@ use uuid::Uuid; use super::{ErrorResponse, ErrorResponseStatus, RespExt}; use crate::{ database::Database, - models::{NewSession, NewUser}, + models::{NewSession, NewUser, User}, router::AppState, }; @@ -138,6 +138,23 @@ pub async fn register<DB: Database>( } } +#[instrument(skip_all, fields(user.id = user.id))] +pub async fn delete<DB: Database>( + user: User, + state: State<AppState<DB>>, +) -> Result<Json<DeleteUserResponse>, ErrorResponseStatus<'static>> { + debug!("request to delete user {}", user.id); + + let db = &state.0.database; + if let Err(e) = db.delete_user(&user).await { + error!("failed to delete user: {}", e); + + return Err(ErrorResponse::reply("failed to delete user") + .with_status(StatusCode::INTERNAL_SERVER_ERROR)); + }; + Ok(Json(DeleteUserResponse {})) +} + #[instrument(skip_all, fields(user.username = login.username.as_str()))] pub async fn login<DB: Database>( state: State<AppState<DB>>, diff --git a/atuin-server/src/router.rs b/atuin-server/src/router.rs index 58aac3bd..20b11f45 100644 --- a/atuin-server/src/router.rs +++ b/atuin-server/src/router.rs @@ -72,6 +72,7 @@ pub fn router<DB: Database + Clone + Send + Sync + 'static>( .route("/history", post(handlers::history::add)) .route("/history", delete(handlers::history::delete)) .route("/user/:username", get(handlers::user::get)) + .route("/account", delete(handlers::user::delete)) .route("/register", post(handlers::user::register)) .route("/login", post(handlers::user::login)); diff --git a/atuin/src/command/client/sync.rs b/atuin/src/command/client/sync.rs index 3980e13d..12664be5 100644 --- a/atuin/src/command/client/sync.rs +++ b/atuin/src/command/client/sync.rs @@ -3,6 +3,7 @@ use eyre::{Result, WrapErr}; use atuin_client::{database::Database, settings::Settings}; +mod delete; mod login; mod logout; mod register; @@ -27,6 +28,9 @@ pub enum Cmd { /// Register with the configured server Register(register::Cmd), + /// Unregister with the configured server + Unregister, + /// Print the encryption key for transfer to another machine Key { /// Switch to base64 output of the key @@ -44,6 +48,7 @@ impl Cmd { Self::Login(l) => l.run(&settings).await, Self::Logout => logout::run(&settings), Self::Register(r) => r.run(&settings).await, + Self::Unregister => delete::run(&settings).await, Self::Status => status::run(&settings, db).await, Self::Key { base64 } => { use atuin_client::encryption::{encode_key, load_key}; diff --git a/atuin/src/command/client/sync/delete.rs b/atuin/src/command/client/sync/delete.rs new file mode 100644 index 00000000..63e5b747 --- /dev/null +++ b/atuin/src/command/client/sync/delete.rs @@ -0,0 +1,23 @@ +use atuin_client::{api_client, encryption::load_encoded_key, settings::Settings}; +use eyre::{bail, Result}; +use std::path::PathBuf; + +pub async fn run(settings: &Settings) -> Result<()> { + let session_path = settings.session_path.as_str(); + + if !PathBuf::from(session_path).exists() { + bail!("You are not logged in"); + } + + let client = api_client::Client::new( + &settings.sync_address, + &settings.session_token, + load_encoded_key(settings)?, + )?; + + client.delete().await?; + + println!("Your account is deleted"); + + Ok(()) +} diff --git a/docs/docs/commands/sync.md b/docs/docs/commands/sync.md index d77a660f..8fbb0c47 100644 --- a/docs/docs/commands/sync.md +++ b/docs/docs/commands/sync.md @@ -32,6 +32,14 @@ notifications (security breaches, changes to service, etc). Upon success, you are also logged in :) Syncing should happen automatically from here! +## Delete + +You can delete your sync account with + +``` +atuin unregister +``` + ## Key As all your data is encrypted, Atuin generates a key for you. It's stored in the |
