diff --git a/doc/api/auth.zh_CN.md b/doc/api/auth.zh_CN.md index 9bbd38f..817f9f8 100644 --- a/doc/api/auth.zh_CN.md +++ b/doc/api/auth.zh_CN.md @@ -25,6 +25,11 @@ * 路径: `/api/auth/user/change/password`、 `/auth/user/change/password` * 方法: `GET` 或 `POST` * 鉴权: 需要 +## 删除用户 +* 路径: `/api/auth/user/delete`、 `/auth/user/delete` +* 方法: `GET` 或 `POST` +* RESTful: `DELETE /api/auth/user` 或 `DELETE /auth/user` +* 鉴权: 需要(仅管理员) ## 获取Token * 路径: `/api/auth/token/add`、 `/auth/token/add` * 方法: `GET` 或 `POST` diff --git a/src/db/sqlite/db.rs b/src/db/sqlite/db.rs index 66dedcb..ce2081d 100644 --- a/src/db/sqlite/db.rs +++ b/src/db/sqlite/db.rs @@ -220,6 +220,13 @@ impl PixivDownloaderSqlite { Ok(()) } + #[cfg(feature = "server")] + fn _delete_user(tx: &Transaction, id: u64) -> Result { + let af = tx.execute("DELETE FROM users WHERE id = ?;", [id])?; + tx.execute("DELETE FROM token WHERE user_id = ?;", [id])?; + Ok(af > 0) + } + /// Get all exists tables async fn _get_exists_table(&self) -> Result, SqliteError> { let con = self.db.lock().await; @@ -555,6 +562,15 @@ impl PixivDownloaderDb for PixivDownloaderSqlite { .expect("User not found:")) } + #[cfg(feature = "server")] + async fn delete_user(&self, id: u64) -> Result { + let mut db = self.db.lock().await; + let tx = db.transaction()?; + let re = Self::_delete_user(&tx, id)?; + tx.commit()?; + Ok(re) + } + #[cfg(feature = "server")] async fn get_token(&self, id: u64) -> Result, PixivDownloaderDbError> { Ok(self.get_token(id).await?) diff --git a/src/db/traits.rs b/src/db/traits.rs index 3af5e97..a2cc521 100644 --- a/src/db/traits.rs +++ b/src/db/traits.rs @@ -55,6 +55,12 @@ pub trait PixivDownloaderDb { is_admin: bool, ) -> Result; #[cfg(feature = "server")] + /// Delete a user + /// * `id` - User ID + /// # Note + /// All tokens of the user will be deleted. + async fn delete_user(&self, id: u64) -> Result; + #[cfg(feature = "server")] /// Get token by ID /// * `id` - The token ID async fn get_token(&self, id: u64) -> Result, PixivDownloaderDbError>; diff --git a/src/server/auth/user.rs b/src/server/auth/user.rs index 0c67a83..b36698f 100644 --- a/src/server/auth/user.rs +++ b/src/server/auth/user.rs @@ -15,6 +15,8 @@ pub enum AuthUserAction { ChangeName, /// Change a user's password. ChangePassword, + /// Delete a user. + Delete, /// Update a existed user. Update, } @@ -221,6 +223,34 @@ impl AuthUserContext { None => Err((5, gettext("No RSA key found.")).into()), } } + AuthUserAction::Delete => { + if root_user.is_some() { + if !user.as_ref().expect("User not found:").is_admin { + return Err((9, gettext("Admin privileges required.")).into()); + } + } + let id = params.get_u64("id").try_err3( + 13, + &gettext("Failed to parse :").replace("", "user id"), + )?; + let username = params.get("username"); + let user = if let Some(id) = id { + self.ctx.db.get_user(id).await + } else if let Some(username) = username { + self.ctx.db.get_user_by_username(username).await + } else { + return Err((14, gettext("No user id or username specified.")).into()); + } + .try_err3(-1001, gettext("Failed to operate the database:"))? + .try_err3(15, gettext("User not found."))?; + let re = self + .ctx + .db + .delete_user(user.id) + .await + .try_err3(-1001, gettext("Failed to operate the database:"))?; + Ok(json::JsonValue::Boolean(re)) + } AuthUserAction::Update => { if root_user.is_some() { if !user.as_ref().expect("User not found:").is_admin { @@ -337,6 +367,8 @@ impl ResponseJsonFor for AuthUserContext { allow_headers = [CONTENT_TYPE, X_SIGN, X_TOKEN_ID], OPTIONS, PUT, + PATCH, + DELETE, ); builder } else { @@ -364,8 +396,10 @@ pub struct AuthUserRoute { impl AuthUserRoute { pub fn new() -> Self { Self { - regex: Regex::new(r"^(/+api)?/+auth/+user(/+(add|update|change/+(name|password)))?$") - .unwrap(), + regex: Regex::new( + r"^(/+api)?/+auth/+user(/+(add|update|delete|change/+(name|password)))?$", + ) + .unwrap(), } } } @@ -394,6 +428,7 @@ impl MatchRoute for AuthUserRoute { let m = m.as_str().trim_start_matches("/"); match m { "add" => Some(AuthUserAction::Add), + "delete" => Some(AuthUserAction::Delete), "update" => Some(AuthUserAction::Update), _ => { if m.starts_with("change/") { @@ -416,6 +451,8 @@ impl MatchRoute for AuthUserRoute { Some(AuthUserAction::Add) } else if m == Method::PATCH { Some(AuthUserAction::Update) + } else if m == Method::DELETE { + Some(AuthUserAction::Delete) } else { None }