diff --git a/doc/db/token.md b/doc/db/token.md index ae96410..5cc8db4 100644 --- a/doc/db/token.md +++ b/doc/db/token.md @@ -1,5 +1,6 @@ | Name | Description | Type | |:----:|:-----------:|:----:| +| id | Token id | integer | | user_id | User id | integer | | token | Token | string | | created_at | Creation time | datetime | diff --git a/src/db/mod.rs b/src/db/mod.rs index 96d5d72..32fedf7 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,6 +1,8 @@ pub mod config; #[cfg(feature = "db_sqlite")] pub mod sqlite; +#[cfg(feature = "server")] +pub mod token; pub mod traits; #[cfg(feature = "server")] pub mod user; @@ -11,6 +13,8 @@ pub use config::PixivDownloaderDbConfig; pub use config::PixivDownloaderSqliteConfig; #[cfg(feature = "db_sqlite")] pub use sqlite::{PixivDownloaderSqlite, SqliteError}; +#[cfg(feature = "server")] +pub use token::Token; pub use traits::PixivDownloaderDb; #[cfg(feature = "server")] pub use user::User; diff --git a/src/db/sqlite/db.rs b/src/db/sqlite/db.rs index 311723a..c9401e2 100644 --- a/src/db/sqlite/db.rs +++ b/src/db/sqlite/db.rs @@ -1,8 +1,8 @@ -#[cfg(feature = "server")] -use super::super::User; use super::super::{ PixivDownloaderDb, PixivDownloaderDbConfig, PixivDownloaderDbError, PixivDownloaderSqliteConfig, }; +#[cfg(feature = "server")] +use super::super::{Token, User}; use super::SqliteError; use bytes::BytesMut; use futures_util::lock::Mutex; @@ -56,10 +56,12 @@ lang TEXT, translated TEXT );"; const TOKEN_TABLE: &'static str = "CREATE TABLE token ( +id INT, user_id INT, token TEXT, created_at DATETIME, -expired_at DATETIME +expired_at DATETIME, +PRIMARY KEY (id) );"; const USERS_TABLE: &'static str = "CREATE TABLE users ( id INT, @@ -77,13 +79,14 @@ v3 INT, v4 INT, PRIMARY KEY (id) );"; -const VERSION: [u8; 4] = [1, 0, 0, 2]; +const VERSION: [u8; 4] = [1, 0, 0, 3]; pub struct PixivDownloaderSqlite { db: Mutex, } impl PixivDownloaderSqlite { + #[cfg(feature = "server")] async fn _add_root_user( &self, name: &str, @@ -127,6 +130,10 @@ impl PixivDownloaderSqlite { if db_version < [1, 0, 0, 2] { tx.execute("ALTER TABLE users ADD is_admin BOOLEAN;", [])?; } + if db_version < [1, 0, 0, 3] { + tx.execute("DROP TABLE token;", [])?; + tx.execute(TOKEN_TABLE, [])?; + } self._write_version(&tx)?; tx.commit()?; } @@ -187,6 +194,22 @@ impl PixivDownloaderSqlite { Ok(tables) } + #[cfg(feature = "server")] + async fn get_token(&self, id: u64) -> Result, SqliteError> { + let con = self.db.lock().await; + Ok(con + .query_row("SELECT * FROM token WHERE id = ?;", [id], |row| { + Ok(Token { + id: row.get(0)?, + user_id: row.get(1)?, + token: row.get(2)?, + created_at: row.get(3)?, + expired_at: row.get(4)?, + }) + }) + .optional()?) + } + #[cfg(feature = "server")] async fn _get_user(&self, id: u64) -> Result, SqliteError> { let con = self.db.lock().await; @@ -267,6 +290,11 @@ impl PixivDownloaderDb for PixivDownloaderSqlite { Ok(self.get_user(0).await?.expect("Root user not found:")) } + #[cfg(feature = "server")] + async fn get_token(&self, id: u64) -> Result, PixivDownloaderDbError> { + Ok(self.get_token(id).await?) + } + #[cfg(feature = "server")] async fn get_user(&self, id: u64) -> Result, PixivDownloaderDbError> { Ok(self._get_user(id).await?) diff --git a/src/db/token.rs b/src/db/token.rs new file mode 100644 index 0000000..3abf7be --- /dev/null +++ b/src/db/token.rs @@ -0,0 +1,15 @@ +use chrono::{DateTime, Utc}; + +/// A token in the database +pub struct Token { + /// The token ID + pub id: u64, + /// The user ID of the token + pub user_id: u64, + /// The token + pub token: String, + /// The token's creation time + pub created_at: DateTime, + /// The token's expiration time + pub expired_at: DateTime, +} diff --git a/src/db/traits.rs b/src/db/traits.rs index 06cf018..6010aaf 100644 --- a/src/db/traits.rs +++ b/src/db/traits.rs @@ -1,7 +1,7 @@ use super::PixivDownloaderDbConfig; use super::PixivDownloaderDbError; #[cfg(feature = "server")] -use super::User; +use super::{Token, User}; #[async_trait] pub trait PixivDownloaderDb { @@ -24,6 +24,10 @@ pub trait PixivDownloaderDb { password: &[u8], ) -> Result; #[cfg(feature = "server")] + /// Get token by ID + /// * `id` - The token ID + async fn get_token(&self, id: u64) -> Result, PixivDownloaderDbError>; + #[cfg(feature = "server")] /// Get a user by ID /// * `id`: The user's ID async fn get_user(&self, id: u64) -> Result, PixivDownloaderDbError>; diff --git a/src/error.rs b/src/error.rs index d29fec8..b6c8c6a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -27,6 +27,7 @@ pub enum PixivDownloaderError { JSONError(json::Error), #[cfg(feature = "openssl")] OpenSSLError(openssl::error::ErrorStack), + ParseIntError(std::num::ParseIntError), } impl From<&str> for PixivDownloaderError { diff --git a/src/server/auth/user.rs b/src/server/auth/user.rs index f9c1b34..d029500 100644 --- a/src/server/auth/user.rs +++ b/src/server/auth/user.rs @@ -37,7 +37,10 @@ impl AuthUserContext { .await .try_err3(-1002, gettext("Failed to get parameters:"))?; if root_user.is_some() { - // # TODO auth + self.ctx + .verify_token(&req, ¶ms) + .await + .try_err3(-403, gettext("Failed to verify the token:"))?; } match &self.action { Some(act) => match act { diff --git a/src/server/context.rs b/src/server/context.rs index acc453f..4403351 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -1,9 +1,12 @@ use super::auth::RSAKey; use super::cors::CorsContext; +use super::params::RequestParams; use crate::db::{open_and_init_database, PixivDownloaderDb}; +use crate::error::PixivDownloaderError; use crate::get_helper; use crate::gettext; use futures_util::lock::Mutex; +use hyper::{Body, Request}; pub struct ServerContext { pub cors: CorsContext, @@ -22,4 +25,34 @@ impl ServerContext { rsa_key: Mutex::new(None), } } + + pub async fn verify_token( + &self, + req: &Request, + params: &RequestParams, + ) -> Result<(), PixivDownloaderError> { + let mut token_id = None; + match req.headers().get("X-TOKEN-ID") { + Some(v) => { + token_id.replace(v.to_str()?.to_owned()); + } + None => match params.get("token_id") { + Some(v) => { + token_id.replace(v.to_owned()); + } + None => {} + }, + } + let token_id = match token_id { + Some(token_id) => token_id, + None => return Err(PixivDownloaderError::from(gettext("Token id not found."))), + } + .parse::()?; + let token = self + .db + .get_token(token_id) + .await? + .ok_or(gettext("Token not found."))?; + Ok(()) + } }