From c5947b352d326b2e113efefbeab6a3bd92d6dae2 Mon Sep 17 00:00:00 2001 From: jb-alvarado Date: Mon, 13 Jun 2022 13:54:36 +0200 Subject: [PATCH] add user --- src/api/handles.rs | 59 ++++++++++++++++++++++++---------------------- src/api/routes.rs | 31 +++++++++++++++++------- src/api/utils.rs | 35 +++++++++------------------ src/bin/ffpapi.rs | 5 ++-- 4 files changed, 68 insertions(+), 62 deletions(-) diff --git a/src/api/handles.rs b/src/api/handles.rs index 89f14da3..c4f6ce40 100644 --- a/src/api/handles.rs +++ b/src/api/handles.rs @@ -1,6 +1,9 @@ use std::path::Path; -use actix_web::web; +use argon2::{ + password_hash::{rand_core::OsRng, SaltString}, + Argon2, PasswordHasher, +}; use faccess::PathExt; use rand::{distributions::Alphanumeric, Rng}; use simplelog::*; @@ -27,7 +30,7 @@ pub fn db_path() -> Result> { Ok(db_path) } -async fn cretea_schema() -> Result { +async fn create_schema() -> Result { let conn = db_connection().await?; let query = "PRAGMA foreign_keys = ON; CREATE TABLE IF NOT EXISTS global @@ -73,7 +76,7 @@ pub async fn db_init() -> Result<&'static str, Box> { if !Sqlite::database_exists(&db_path).await.unwrap_or(false) { Sqlite::create_database(&db_path).await.unwrap(); - match cretea_schema().await { + match create_schema().await { Ok(_) => info!("Database created Successfully"), Err(e) => panic!("{e}"), } @@ -132,16 +135,17 @@ pub async fn db_get_settings(id: &i64) -> Result { pub async fn db_update_settings( id: i64, - s: web::Json, + settings: Settings, ) -> Result { let conn = db_connection().await?; + let query = "UPDATE settings SET channel_name = $2, preview_url = $3, config_path = $4, extra_extensions = $5 WHERE id = $1"; let result: SqliteQueryResult = sqlx::query(query) .bind(id) - .bind(s.channel_name.clone()) - .bind(s.preview_url.clone()) - .bind(s.config_path.clone()) - .bind(s.extra_extensions.clone()) + .bind(settings.channel_name.clone()) + .bind(settings.preview_url.clone()) + .bind(settings.config_path.clone()) + .bind(settings.extra_extensions.clone()) .execute(&conn) .await?; conn.close().await; @@ -158,33 +162,32 @@ pub async fn db_role(id: &i64) -> Result { Ok(result.name) } -pub async fn add_user( - mail: &str, - user: &str, - pass: &str, - salt: &str, - group: &i64, -) -> Result { +pub async fn db_login(user: &str) -> Result { let conn = db_connection().await?; - let query = - "INSERT INTO user (email, username, password, salt, role_id) VALUES($1, $2, $3, $4, $5)"; - let result = sqlx::query(query) - .bind(mail) - .bind(user) - .bind(pass) - .bind(salt) - .bind(group) - .execute(&conn) - .await?; + let query = "SELECT id, email, username, password, salt, role_id FROM user WHERE username = $1"; + let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?; conn.close().await; Ok(result) } -pub async fn db_login(user: &str) -> Result { +pub async fn db_add_user(user: User) -> Result { let conn = db_connection().await?; - let query = "SELECT id, email, username, password, salt, role_id FROM user WHERE username = $1"; - let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?; + let salt = SaltString::generate(&mut OsRng); + let password_hash = Argon2::default() + .hash_password(user.password.clone().as_bytes(), &salt) + .unwrap(); + + let query = + "INSERT INTO user (email, username, password, salt, role_id) VALUES($1, $2, $3, $4, $5)"; + let result = sqlx::query(query) + .bind(user.email) + .bind(user.username) + .bind(password_hash.to_string()) + .bind(salt.to_string()) + .bind(user.role_id) + .execute(&conn) + .await?; conn.close().await; Ok(result) diff --git a/src/api/routes.rs b/src/api/routes.rs index ed8d3992..889beccb 100644 --- a/src/api/routes.rs +++ b/src/api/routes.rs @@ -10,7 +10,9 @@ use simplelog::*; use crate::api::{ auth::{create_jwt, Claims}, errors::ServiceError, - handles::{db_get_settings, db_login, db_role, db_update_settings, db_update_user}, + handles::{ + db_add_user, db_get_settings, db_login, db_role, db_update_settings, db_update_user, + }, models::{LoginUser, Settings, User}, }; @@ -46,7 +48,7 @@ async fn patch_settings( id: web::Path, data: web::Json, ) -> Result { - if db_update_settings(*id, data).await.is_ok() { + if db_update_settings(*id, data.into_inner()).await.is_ok() { return Ok("Update Success"); }; @@ -75,9 +77,7 @@ async fn update_user( } let salt = SaltString::generate(&mut OsRng); - let argon2 = Argon2::default(); - - let password_hash = argon2 + let password_hash = Argon2::default() .hash_password(data.password.clone().as_bytes(), &salt) .unwrap(); @@ -94,17 +94,32 @@ async fn update_user( Err(ServiceError::Unauthorized) } +/// curl -X POST 'http://localhost:8080/api/user/' --header 'Content-Type: application/json' \ +/// -d '{"email": "", "username": "", "password": "", "role_id": 1}' \ +/// --header 'Authorization: Bearer ' +#[post("/user/")] +#[has_permissions("admin")] +async fn add_user(data: web::Json) -> Result { + match db_add_user(data.into_inner()).await { + Ok(_) => Ok("Add User Success"), + Err(e) => { + error!("{e}"); + Err(ServiceError::InternalServerError) + } + } +} + /// curl -X POST http://127.0.0.1:8080/auth/login/ -H "Content-Type: application/json" \ -/// -d '{"username": "USER", "password": "abc123" }' +/// -d '{"username": "", "password": "" }' #[post("/auth/login/")] pub async fn login(credentials: web::Json) -> impl Responder { match db_login(&credentials.username).await { Ok(mut user) => { let pass = user.password.clone(); + let hash = PasswordHash::new(&pass).unwrap(); user.password = "".into(); user.salt = None; - let hash = PasswordHash::new(&pass).unwrap(); if Argon2::default() .verify_password(credentials.password.as_bytes(), &hash) .is_ok() @@ -131,7 +146,7 @@ pub async fn login(credentials: web::Json) -> impl Responder { error!("Wrong password for {}!", credentials.username); web::Json(ResponseObj { message: "Wrong password!".into(), - status: 401, + status: 403, data: None, }) .customize() diff --git a/src/api/utils.rs b/src/api/utils.rs index 93e6aaca..58017868 100644 --- a/src/api/utils.rs +++ b/src/api/utils.rs @@ -1,13 +1,10 @@ -use argon2::{ - password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, - Argon2, -}; use once_cell::sync::OnceCell; use simplelog::*; use crate::api::{ args_parse::Args, - handles::{add_user, db_global, db_init}, + handles::{db_add_user, db_global, db_init}, + models::User, }; #[derive(Debug, sqlx::FromRow)] @@ -60,27 +57,17 @@ pub async fn run_args(args: Args) -> Result<(), i32> { return Err(1); } - let salt = SaltString::generate(&mut OsRng); - let argon2 = Argon2::default(); - let password = args.password.unwrap(); - - let password_hash = match argon2.hash_password(password.as_bytes(), &salt) { - Ok(hash) => hash.to_string(), - Err(e) => { - error!("{e}"); - return Err(1); - } + let user = User { + id: 0, + email: Some(args.email.unwrap()), + username: username.clone(), + password: args.password.unwrap(), + salt: None, + role_id: Some(1), + token: None, }; - if let Err(e) = add_user( - &args.email.unwrap(), - &username, - &password_hash.to_string(), - &salt.to_string(), - &1, - ) - .await - { + if let Err(e) = db_add_user(user).await { error!("{e}"); return Err(1); }; diff --git a/src/bin/ffpapi.rs b/src/bin/ffpapi.rs index aca8e375..eadbea87 100644 --- a/src/bin/ffpapi.rs +++ b/src/bin/ffpapi.rs @@ -13,7 +13,7 @@ use ffplayout_engine::{ args_parse::Args, auth, models::LoginUser, - routes::{get_settings, login, patch_settings, update_user}, + routes::{add_user, get_settings, login, patch_settings, update_user}, utils::{init_config, run_args}, }, utils::{init_logging, GlobalConfig}, @@ -53,7 +53,7 @@ async fn main() -> std::io::Result<()> { info!("running ffplayout API, listen on {conn}"); - // TODO: add allow origin + // TODO: add allow origin (or give it to the proxy) HttpServer::new(move || { let auth = HttpAuthentication::bearer(validator); App::new() @@ -62,6 +62,7 @@ async fn main() -> std::io::Result<()> { .service( web::scope("/api") .wrap(auth) + .service(add_user) .service(get_settings) .service(patch_settings) .service(update_user),