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
This commit is contained in:
parent
dc523416f6
commit
7b9dea72e3
8 changed files with 94 additions and 1 deletions
|
@ -217,4 +217,19 @@ impl<'a> Client<'a> {
|
||||||
|
|
||||||
Ok(())
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@ pub struct RegisterResponse {
|
||||||
pub session: String,
|
pub session: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct DeleteUserResponse {}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct LoginRequest {
|
pub struct LoginRequest {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|
|
@ -27,6 +27,7 @@ pub trait Database {
|
||||||
async fn get_user(&self, username: &str) -> Result<User>;
|
async fn get_user(&self, username: &str) -> Result<User>;
|
||||||
async fn get_user_session(&self, u: &User) -> Result<Session>;
|
async fn get_user_session(&self, u: &User) -> Result<Session>;
|
||||||
async fn add_user(&self, user: &NewUser) -> Result<i64>;
|
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(&self, user: &User) -> Result<i64>;
|
||||||
async fn count_history_cached(&self, user: &User) -> Result<i64>;
|
async fn count_history_cached(&self, user: &User) -> Result<i64>;
|
||||||
|
@ -336,6 +337,26 @@ impl Database for Postgres {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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)]
|
#[instrument(skip_all)]
|
||||||
async fn add_user(&self, user: &NewUser) -> Result<i64> {
|
async fn add_user(&self, user: &NewUser) -> Result<i64> {
|
||||||
let email: &str = &user.email;
|
let email: &str = &user.email;
|
||||||
|
|
|
@ -18,7 +18,7 @@ use uuid::Uuid;
|
||||||
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
use super::{ErrorResponse, ErrorResponseStatus, RespExt};
|
||||||
use crate::{
|
use crate::{
|
||||||
database::Database,
|
database::Database,
|
||||||
models::{NewSession, NewUser},
|
models::{NewSession, NewUser, User},
|
||||||
router::AppState,
|
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()))]
|
#[instrument(skip_all, fields(user.username = login.username.as_str()))]
|
||||||
pub async fn login<DB: Database>(
|
pub async fn login<DB: Database>(
|
||||||
state: State<AppState<DB>>,
|
state: State<AppState<DB>>,
|
||||||
|
|
|
@ -72,6 +72,7 @@ pub fn router<DB: Database + Clone + Send + Sync + 'static>(
|
||||||
.route("/history", post(handlers::history::add))
|
.route("/history", post(handlers::history::add))
|
||||||
.route("/history", delete(handlers::history::delete))
|
.route("/history", delete(handlers::history::delete))
|
||||||
.route("/user/:username", get(handlers::user::get))
|
.route("/user/:username", get(handlers::user::get))
|
||||||
|
.route("/account", delete(handlers::user::delete))
|
||||||
.route("/register", post(handlers::user::register))
|
.route("/register", post(handlers::user::register))
|
||||||
.route("/login", post(handlers::user::login));
|
.route("/login", post(handlers::user::login));
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ use eyre::{Result, WrapErr};
|
||||||
|
|
||||||
use atuin_client::{database::Database, settings::Settings};
|
use atuin_client::{database::Database, settings::Settings};
|
||||||
|
|
||||||
|
mod delete;
|
||||||
mod login;
|
mod login;
|
||||||
mod logout;
|
mod logout;
|
||||||
mod register;
|
mod register;
|
||||||
|
@ -27,6 +28,9 @@ pub enum Cmd {
|
||||||
/// Register with the configured server
|
/// Register with the configured server
|
||||||
Register(register::Cmd),
|
Register(register::Cmd),
|
||||||
|
|
||||||
|
/// Unregister with the configured server
|
||||||
|
Unregister,
|
||||||
|
|
||||||
/// Print the encryption key for transfer to another machine
|
/// Print the encryption key for transfer to another machine
|
||||||
Key {
|
Key {
|
||||||
/// Switch to base64 output of the key
|
/// Switch to base64 output of the key
|
||||||
|
@ -44,6 +48,7 @@ impl Cmd {
|
||||||
Self::Login(l) => l.run(&settings).await,
|
Self::Login(l) => l.run(&settings).await,
|
||||||
Self::Logout => logout::run(&settings),
|
Self::Logout => logout::run(&settings),
|
||||||
Self::Register(r) => r.run(&settings).await,
|
Self::Register(r) => r.run(&settings).await,
|
||||||
|
Self::Unregister => delete::run(&settings).await,
|
||||||
Self::Status => status::run(&settings, db).await,
|
Self::Status => status::run(&settings, db).await,
|
||||||
Self::Key { base64 } => {
|
Self::Key { base64 } => {
|
||||||
use atuin_client::encryption::{encode_key, load_key};
|
use atuin_client::encryption::{encode_key, load_key};
|
||||||
|
|
23
atuin/src/command/client/sync/delete.rs
Normal file
23
atuin/src/command/client/sync/delete.rs
Normal file
|
@ -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(())
|
||||||
|
}
|
|
@ -32,6 +32,14 @@ notifications (security breaches, changes to service, etc).
|
||||||
Upon success, you are also logged in :) Syncing should happen automatically from
|
Upon success, you are also logged in :) Syncing should happen automatically from
|
||||||
here!
|
here!
|
||||||
|
|
||||||
|
## Delete
|
||||||
|
|
||||||
|
You can delete your sync account with
|
||||||
|
|
||||||
|
```
|
||||||
|
atuin unregister
|
||||||
|
```
|
||||||
|
|
||||||
## Key
|
## Key
|
||||||
|
|
||||||
As all your data is encrypted, Atuin generates a key for you. It's stored in the
|
As all your data is encrypted, Atuin generates a key for you. It's stored in the
|
||||||
|
|
Loading…
Reference in a new issue