From ad8fe5e2dc999a2b930a08918e9f2c8de83625ae Mon Sep 17 00:00:00 2001 From: lifegpc Date: Sun, 25 Sep 2022 09:57:42 +0000 Subject: [PATCH] Update --- src/db/sqlite/db.rs | 119 ++++++++++++++++++++++++++++++++++++++++ src/db/sqlite/error.rs | 1 + src/db/traits.rs | 25 +++++++++ src/server/auth/user.rs | 37 ++++++++++++- src/server/params.rs | 43 +++++++++++++++ 5 files changed, 224 insertions(+), 1 deletion(-) diff --git a/src/db/sqlite/db.rs b/src/db/sqlite/db.rs index c9401e2..827d0da 100644 --- a/src/db/sqlite/db.rs +++ b/src/db/sqlite/db.rs @@ -228,6 +228,28 @@ impl PixivDownloaderSqlite { .optional()?) } + #[cfg(feature = "server")] + async fn _get_user_by_username(&self, username: &str) -> Result, SqliteError> { + let con = self.db.lock().await; + Ok(con + .query_row( + "SELECT * FROM users WHERE username = ?;", + [username], + |row| { + let password: Vec = row.get(3)?; + let password: &[u8] = &password; + Ok(User { + id: row.get(0)?, + name: row.get(1)?, + username: row.get(2)?, + password: BytesMut::from(password), + is_admin: row.get(4)?, + }) + }, + ) + .optional()?) + } + async fn _read_version(&self) -> Result, SqliteError> { let con = self.db.lock().await; let mut stmt = con.prepare("SELECT v1, v2, v3, v4 FROM version WHERE id='main';")?; @@ -239,6 +261,81 @@ impl PixivDownloaderSqlite { } } + #[cfg(feature = "server")] + async fn _set_user( + &self, + id: u64, + name: &str, + username: &str, + password: &[u8], + is_admin: bool, + ) -> Result { + let con = self.db.lock().await; + let has_user = con + .query_row("SELECT * FROM users WHERE id = ?;", [id], |_| Ok(())) + .optional() + .is_ok(); + let has_username = con + .query_row( + "SELECT * FROM users WHERE username = ?;", + [username], + |row| { + if has_user { + let uid: u64 = row.get(0)?; + Ok(uid != id) + } else { + Ok(true) + } + }, + ) + .optional()? + .unwrap_or(false); + if !has_user { + if has_username { + Err(SqliteError::UserNameAlreadyExists) + } else { + con.execute( + "INSERT INTO users (name, username, password, is_admin) VALUES (?, ?, ?, ?);", + (name, username, password, is_admin), + )?; + Ok(con.query_row( + "SELECT * FROM users WHERE username = ?;", + [username], + |row| { + let password: Vec = row.get(3)?; + let password: &[u8] = &password; + Ok(User { + id: row.get(0)?, + name: row.get(1)?, + username: row.get(2)?, + password: BytesMut::from(password), + is_admin: row.get(4)?, + }) + }, + )?) + } + } else { + if has_username { + Err(SqliteError::UserNameAlreadyExists) + } else { + con.execute("INSERT OR REPLACE INTO users (id, name, username, password, is_admin) VALUES (?, ?, ?, ?, ?);", (id, name, username, password, is_admin))?; + Ok( + con.query_row("SELECT * FROM users WHERE id = ?;", [id], |row| { + let password: Vec = row.get(3)?; + let password: &[u8] = &password; + Ok(User { + id: row.get(0)?, + name: row.get(1)?, + username: row.get(2)?, + password: BytesMut::from(password), + is_admin: row.get(4)?, + }) + })?, + ) + } + } + } + fn _write_version<'a>(&self, ts: &Transaction<'a>) -> Result<(), SqliteError> { let mut stmt = ts.prepare( "INSERT OR REPLACE INTO version (id, v1, v2, v3, v4) VALUES ('main', ?, ?, ?, ?);", @@ -300,10 +397,32 @@ impl PixivDownloaderDb for PixivDownloaderSqlite { Ok(self._get_user(id).await?) } + #[cfg(feature = "server")] + async fn get_user_by_username( + &self, + username: &str, + ) -> Result, PixivDownloaderDbError> { + Ok(self._get_user_by_username(username).await?) + } + async fn init(&self) -> Result<(), PixivDownloaderDbError> { if !self._check_database().await? { self._create_table().await?; } Ok(()) } + + #[cfg(feature = "server")] + async fn set_user( + &self, + id: u64, + name: &str, + username: &str, + password: &[u8], + is_admin: bool, + ) -> Result { + Ok(self + ._set_user(id, name, username, password, is_admin) + .await?) + } } diff --git a/src/db/sqlite/error.rs b/src/db/sqlite/error.rs index 3f7dc7c..ef0ca2a 100644 --- a/src/db/sqlite/error.rs +++ b/src/db/sqlite/error.rs @@ -2,4 +2,5 @@ pub enum SqliteError { DbError(rusqlite::Error), DatabaseVersionTooNew, + UserNameAlreadyExists, } diff --git a/src/db/traits.rs b/src/db/traits.rs index 6010aaf..2876ccd 100644 --- a/src/db/traits.rs +++ b/src/db/traits.rs @@ -31,6 +31,31 @@ pub trait PixivDownloaderDb { /// Get a user by ID /// * `id`: The user's ID async fn get_user(&self, id: u64) -> Result, PixivDownloaderDbError>; + #[cfg(feature = "server")] + /// Get a user by username + /// * `username`: The user's username + async fn get_user_by_username( + &self, + username: &str, + ) -> Result, PixivDownloaderDbError>; /// Initialize the database (create tables, migrate data, etc.) async fn init(&self) -> Result<(), PixivDownloaderDbError>; + #[cfg(feature = "server")] + /// Set a user's information by ID + /// * `id`: The user's ID + /// * `name`: The user's name + /// * `username`: The user's username + /// * `password`: The user's hashed password + /// * `is_admin`: Whether the user is an admin + /// # Note + /// If the user does not exist, a new user will be created and `id` must not be applied. + /// If the user's `username` is taked by another user, the operation must failed. + async fn set_user( + &self, + id: u64, + name: &str, + username: &str, + password: &[u8], + is_admin: bool, + ) -> Result; } diff --git a/src/server/auth/user.rs b/src/server/auth/user.rs index 683ab77..d1f1b03 100644 --- a/src/server/auth/user.rs +++ b/src/server/auth/user.rs @@ -88,7 +88,42 @@ impl AuthUserContext { .try_err3(8, gettext("Failed to add user to database:"))?; Ok(user.to_json2()) } else { - Ok(json::object! {}) + let mut is_admin = params + .get_bool("is_admin") + .try_err3( + 9, + &gettext("Failed to parse :") + .replace("", "is_admin"), + )? + .unwrap_or(false); + let id = params.get_u64("id").try_err3( + 10, + &gettext("Failed to parse :").replace("", "id"), + )?; + match id { + Some(id) => { + if id == 0 { + is_admin = true; + } + let user = self + .ctx + .db + .set_user( + id, + name, + username, + &hashed_password, + is_admin, + ) + .await + .try_err3( + 12, + gettext("Failed to set user in database:"), + )?; + Ok(user.to_json2()) + } + None => Ok(json::object! {}), + } } } None => return Err((5, gettext("No RSA key found.")).into()), diff --git a/src/server/params.rs b/src/server/params.rs index 8ed77df..300852f 100644 --- a/src/server/params.rs +++ b/src/server/params.rs @@ -31,6 +31,49 @@ impl RequestParams { } } + /// Get parameter and return it as boolean. + /// * `name` - Parameter name. + pub fn get_bool + ?Sized>( + &self, + name: &S, + ) -> Result, PixivDownloaderError> { + match self.get(name) { + Some(v) => { + let v = v.trim(); + if v == "true" { + Ok(Some(true)) + } else if v == "false" { + Ok(Some(false)) + } else { + match v.parse::() { + Ok(v) => return Ok(Some(v != 0)), + Err(_) => {} + } + Err(gettext("Invalid boolean value.").into()) + } + } + None => Ok(None), + } + } + + /// Get parameter and return it as [u64]. + /// * `name` - Parameter name. + pub fn get_u64 + ?Sized>( + &self, + name: &S, + ) -> Result, PixivDownloaderError> { + match self.get(name) { + Some(v) => { + let v = v.trim(); + match v.parse::() { + Ok(v) => Ok(Some(v)), + Err(_) => Err(gettext("Invalid unsigned 64bit integer value.").into()), + } + } + None => Ok(None), + } + } + /// Get all parameters with same name. /// * `name` - Parameter name. pub fn get_all + ?Sized>(&self, name: &S) -> Option<&Vec> {