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",
]
[[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]]
name = "futures-sink"
version = "0.3.21"
@ -400,6 +411,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
dependencies = [
"futures-core",
"futures-macro",
"futures-sink",
"futures-task",
"pin-project-lite",
@ -542,6 +554,7 @@ dependencies = [
name = "homedisk-utils"
version = "0.0.0"
dependencies = [
"futures-util",
"hex",
"log",
"serde",
@ -1684,7 +1697,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cfcd319456c4d6ea10087ed423473267e1a071f3bc0aa89f80d60997843c6f0"
dependencies = [
"getrandom",
"sha1_smol",
]

View File

@ -1,9 +1,40 @@
use axum::Json;
use axum::{Extension, Json};
use homedisk_types::{
auth::login::{Request, Response},
config::types::Config,
errors::{AuthError, ServerError},
token::{Claims, Token},
};
use homedisk_utils::database::{Database, User};
pub async fn handle(Json(_request): Json<Request>) -> Result<Json<Response>, ServerError> {
Err(ServerError::AuthError(AuthError::UserAlreadyExists))
pub async fn handle(
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")]
UserAlreadyExists,
#[error("generate jwt token")]
TokenGenerate,
#[error("unknow error")]
UnknowError(String),
}

View File

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

View File

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

View File

@ -4,8 +4,10 @@ use crate::crypto;
#[derive(Debug)]
pub enum Error {
UserNotFound,
Crypto(crypto::Error),
SQLx(sqlx::Error),
Io(std::io::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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::UserNotFound => write!(f, "user not found"),
Error::Crypto(err) => write!(f, "crypto 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 sqlx::{sqlite::SqliteQueryResult, Executor, SqlitePool};
use sqlx::{sqlite::SqliteQueryResult, Executor, Row, SqlitePool};
use user::User;
use super::{user, Error};
@ -27,7 +29,7 @@ impl Database {
/// ```ignore
/// use homedisk_utils::database::{Database, User};
///
/// let user = User::new("medzik", "SuperSecretPassword123");
/// let user = User::new("username", "password");
/// db.create_user(&user).await?;
/// ```
pub async fn create_user(&self, user: &user::User) -> Result<SqliteQueryResult, Error> {
@ -40,6 +42,30 @@ impl Database {
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)]
@ -54,15 +80,7 @@ mod tests {
Database::open("sqlite::memory:").await.expect("open db")
}
#[tokio::test]
async fn open_db_in_memory() {
open_db().await;
}
#[tokio::test]
async fn create_user() {
let db = open_db().await;
async fn new_user(db: &Database) {
// create user table
db.conn
.execute(sqlx::query(
@ -72,7 +90,67 @@ mod tests {
.expect("create tables");
// create new user
let user = User::new("medzik", "SuperSecretPassword123");
let user = User::new("medzik", "Qwerty1234!");
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")
}
}