feat (http): login functions have been completed

This commit is contained in:
MedzikUser 2022-04-21 20:54:53 +02:00
parent e0e487fbf4
commit fd6e6fd0a8
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
7 changed files with 153 additions and 17 deletions

14
Cargo.lock generated
View File

@ -381,6 +381,17 @@ dependencies = [
"parking_lot 0.11.2", "parking_lot 0.11.2",
] ]
[[package]]
name = "futures-macro"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.21" version = "0.3.21"
@ -400,6 +411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-macro",
"futures-sink", "futures-sink",
"futures-task", "futures-task",
"pin-project-lite", "pin-project-lite",
@ -542,6 +554,7 @@ dependencies = [
name = "homedisk-utils" name = "homedisk-utils"
version = "0.0.0" version = "0.0.0"
dependencies = [ dependencies = [
"futures-util",
"hex", "hex",
"log", "log",
"serde", "serde",
@ -1684,7 +1697,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0" checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
dependencies = [ dependencies = [
"getrandom",
"sha1_smol", "sha1_smol",
] ]

View File

@ -1,9 +1,40 @@
use axum::Json; use axum::{Extension, Json};
use homedisk_types::{ use homedisk_types::{
auth::login::{Request, Response}, auth::login::{Request, Response},
config::types::Config,
errors::{AuthError, ServerError}, errors::{AuthError, ServerError},
token::{Claims, Token},
}; };
use homedisk_utils::database::{Database, User};
pub async fn handle(Json(_request): Json<Request>) -> Result<Json<Response>, ServerError> { pub async fn handle(
Err(ServerError::AuthError(AuthError::UserAlreadyExists)) db: Extension<Database>,
config: Extension<Config>,
Json(request): Json<Request>,
) -> Result<Json<Response>, ServerError> {
let user = User::new(&request.username, &request.password);
let response = match db.find_user(&user.username, &user.password).await {
Ok(res) => {
let token = Token::new(config.jwt.secret.as_bytes(), Claims::new(res.id)).unwrap();
Response::LoggedIn {
access_token: token.encoded,
}
}
Err(e) => {
use homedisk_utils::database::Error;
match e {
Error::UserNotFound => return Err(ServerError::AuthError(AuthError::UserNotFound)),
_ => {
return Err(ServerError::AuthError(AuthError::UnknowError(
e.to_string(),
)))
}
}
}
};
Ok(Json(response))
} }

View File

@ -8,6 +8,9 @@ pub enum Error {
#[error("user already exists")] #[error("user already exists")]
UserAlreadyExists, UserAlreadyExists,
#[error("generate jwt token")]
TokenGenerate,
#[error("unknow error")] #[error("unknow error")]
UnknowError(String), UnknowError(String),
} }

View File

@ -24,6 +24,7 @@ impl axum::response::IntoResponse for ServerError {
Self::AuthError(ref err) => match err { Self::AuthError(ref err) => match err {
AuthError::UserNotFound => StatusCode::BAD_REQUEST, AuthError::UserNotFound => StatusCode::BAD_REQUEST,
AuthError::UserAlreadyExists => StatusCode::NOT_ACCEPTABLE, AuthError::UserAlreadyExists => StatusCode::NOT_ACCEPTABLE,
AuthError::TokenGenerate => StatusCode::INTERNAL_SERVER_ERROR,
AuthError::UnknowError(_) => StatusCode::INTERNAL_SERVER_ERROR, AuthError::UnknowError(_) => StatusCode::INTERNAL_SERVER_ERROR,
}, },
}; };

View File

@ -10,6 +10,7 @@ database = ["sqlx", "uuid"]
[dependencies] [dependencies]
log = "0.4.16" log = "0.4.16"
futures-util = "0.3.21"
[dependencies.serde] [dependencies.serde]
version = "1.0.136" version = "1.0.136"
@ -33,7 +34,7 @@ features = ["runtime-tokio-rustls", "sqlite"]
optional = true optional = true
[dependencies.uuid] [dependencies.uuid]
version = "1.0.0" version = "1.0.0"
features = ["v4", "v5"] features = ["v5"]
optional = true optional = true
[dev-dependencies.tokio] [dev-dependencies.tokio]

View File

@ -4,8 +4,10 @@ use crate::crypto;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {
UserNotFound,
Crypto(crypto::Error), Crypto(crypto::Error),
SQLx(sqlx::Error), SQLx(sqlx::Error),
Io(std::io::Error),
} }
impl From<crypto::Error> for Error { impl From<crypto::Error> for Error {
@ -20,11 +22,19 @@ impl From<sqlx::Error> for Error {
} }
} }
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
Error::Io(err)
}
}
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Error::UserNotFound => write!(f, "user not found"),
Error::Crypto(err) => write!(f, "crypto error: {}", err), Error::Crypto(err) => write!(f, "crypto error: {}", err),
Error::SQLx(err) => write!(f, "sqlx error: {}", err), Error::SQLx(err) => write!(f, "sqlx error: {}", err),
Error::Io(err) => write!(f, "error: {}", err),
} }
} }
} }

View File

@ -1,5 +1,7 @@
use futures_util::TryStreamExt;
use log::debug; use log::debug;
use sqlx::{sqlite::SqliteQueryResult, Executor, SqlitePool}; use sqlx::{sqlite::SqliteQueryResult, Executor, Row, SqlitePool};
use user::User;
use super::{user, Error}; use super::{user, Error};
@ -27,7 +29,7 @@ impl Database {
/// ```ignore /// ```ignore
/// use homedisk_utils::database::{Database, User}; /// use homedisk_utils::database::{Database, User};
/// ///
/// let user = User::new("medzik", "SuperSecretPassword123"); /// let user = User::new("username", "password");
/// db.create_user(&user).await?; /// db.create_user(&user).await?;
/// ``` /// ```
pub async fn create_user(&self, user: &user::User) -> Result<SqliteQueryResult, Error> { pub async fn create_user(&self, user: &user::User) -> Result<SqliteQueryResult, Error> {
@ -40,6 +42,30 @@ impl Database {
Ok(self.conn.execute(query).await?) Ok(self.conn.execute(query).await?)
} }
/// Find user
pub async fn find_user(&self, username: &str, password: &str) -> Result<User, Error> {
debug!("search for user - {}", username);
let query =
sqlx::query_as::<_, User>("SELECT * FROM user WHERE username = ? AND password = ?")
.bind(username)
.bind(password);
let mut stream = self.conn.fetch(query);
let row = stream.try_next().await?.ok_or(Error::UserNotFound)?;
let id = row.try_get("id")?;
let username = row.try_get("username")?;
let password = row.try_get("password")?;
Ok(User {
id,
username,
password,
})
}
} }
#[cfg(test)] #[cfg(test)]
@ -54,15 +80,7 @@ mod tests {
Database::open("sqlite::memory:").await.expect("open db") Database::open("sqlite::memory:").await.expect("open db")
} }
#[tokio::test] async fn new_user(db: &Database) {
async fn open_db_in_memory() {
open_db().await;
}
#[tokio::test]
async fn create_user() {
let db = open_db().await;
// create user table // create user table
db.conn db.conn
.execute(sqlx::query( .execute(sqlx::query(
@ -72,7 +90,67 @@ mod tests {
.expect("create tables"); .expect("create tables");
// create new user // create new user
let user = User::new("medzik", "SuperSecretPassword123"); let user = User::new("medzik", "Qwerty1234!");
db.create_user(&user).await.expect("create user"); db.create_user(&user).await.expect("create user");
} }
#[tokio::test]
async fn open_db_in_memory() {
open_db().await;
}
#[tokio::test]
async fn create_user() {
let db = open_db().await;
new_user(&db).await;
}
#[tokio::test]
async fn find_user() {
let db = open_db().await;
new_user(&db).await;
let user = User::new("medzik", "Qwerty1234!");
let res = db
.find_user(&user.username, &user.password)
.await
.expect("find user");
assert_eq!(res.password, user.password)
}
#[tokio::test]
async fn find_user_wrong_password() {
let db = open_db().await;
new_user(&db).await;
let user = User::new("medzik", "wrong password 123!");
let err = db
.find_user(&user.username, &user.password)
.await
.unwrap_err();
assert_eq!(err.to_string(), "user not found")
}
#[tokio::test]
async fn find_user_wrong_username() {
let db = open_db().await;
new_user(&db).await;
let user = User::new("not_exists_user", "secret password of a not existing user");
let err = db
.find_user(&user.username, &user.password)
.await
.unwrap_err();
assert_eq!(err.to_string(), "user not found")
}
} }