This commit is contained in:
2022-09-25 09:57:42 +00:00
committed by GitHub
parent dfa9ba5576
commit ad8fe5e2dc
5 changed files with 224 additions and 1 deletions

View File

@@ -228,6 +228,28 @@ impl PixivDownloaderSqlite {
.optional()?)
}
#[cfg(feature = "server")]
async fn _get_user_by_username(&self, username: &str) -> Result<Option<User>, SqliteError> {
let con = self.db.lock().await;
Ok(con
.query_row(
"SELECT * FROM users WHERE username = ?;",
[username],
|row| {
let password: Vec<u8> = 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<Option<[u8; 4]>, 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<User, SqliteError> {
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<u8> = 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<u8> = 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<Option<User>, 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<User, PixivDownloaderDbError> {
Ok(self
._set_user(id, name, username, password, is_admin)
.await?)
}
}

View File

@@ -2,4 +2,5 @@
pub enum SqliteError {
DbError(rusqlite::Error),
DatabaseVersionTooNew,
UserNameAlreadyExists,
}

View File

@@ -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<Option<User>, PixivDownloaderDbError>;
#[cfg(feature = "server")]
/// Get a user by username
/// * `username`: The user's username
async fn get_user_by_username(
&self,
username: &str,
) -> Result<Option<User>, 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<User, PixivDownloaderDbError>;
}

View File

@@ -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 <opt>:")
.replace("<opt>", "is_admin"),
)?
.unwrap_or(false);
let id = params.get_u64("id").try_err3(
10,
&gettext("Failed to parse <opt>:").replace("<opt>", "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()),

View File

@@ -31,6 +31,49 @@ impl RequestParams {
}
}
/// Get parameter and return it as boolean.
/// * `name` - Parameter name.
pub fn get_bool<S: AsRef<str> + ?Sized>(
&self,
name: &S,
) -> Result<Option<bool>, 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::<i64>() {
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<S: AsRef<str> + ?Sized>(
&self,
name: &S,
) -> Result<Option<u64>, PixivDownloaderError> {
match self.get(name) {
Some(v) => {
let v = v.trim();
match v.parse::<u64>() {
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<S: AsRef<str> + ?Sized>(&self, name: &S) -> Option<&Vec<String>> {