work on login route

This commit is contained in:
jb-alvarado 2022-06-07 18:11:46 +02:00
parent 227d09bce7
commit 49af829221
7 changed files with 166 additions and 80 deletions

24
src/api/args_parse.rs Normal file
View File

@ -0,0 +1,24 @@
use clap::Parser;
#[derive(Parser, Debug, Clone)]
#[clap(version,
name = "ffpapi",
version = "0.1.0",
about = "ffplayout REST API",
long_about = None)]
pub struct Args {
#[clap(short, long, help = "Listen on IP:PORT, like: 127.0.0.1:8080")]
pub listen: Option<String>,
#[clap(short, long, help = "Initialize Database")]
pub init: bool,
#[clap(short, long, help = "Create admin user")]
pub username: Option<String>,
#[clap(short, long, help = "Admin email")]
pub email: Option<String>,
#[clap(short, long, help = "Admin password")]
pub password: Option<String>,
}

View File

@ -4,6 +4,8 @@ use faccess::PathExt;
use simplelog::*; use simplelog::*;
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool}; use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool};
use crate::api::models::User;
pub fn db_path() -> Result<String, Box<dyn std::error::Error>> { pub fn db_path() -> Result<String, Box<dyn std::error::Error>> {
let sys_path = Path::new("/usr/share/ffplayout"); let sys_path = Path::new("/usr/share/ffplayout");
let mut db_path = String::from("./ffplayout.db"); let mut db_path = String::from("./ffplayout.db");
@ -100,3 +102,27 @@ pub async fn add_user(
Ok(result) Ok(result)
} }
pub async fn get_users(
instances: &SqlitePool,
index: Option<i64>,
) -> Result<Vec<User>, sqlx::Error> {
let query = match index {
Some(i) => format!("SELECT id, email, username FROM user WHERE id = {i}"),
None => "SELECT id, email, username FROM user".to_string(),
};
let result: Vec<User> = sqlx::query_as(&query).fetch_all(instances).await?;
instances.close().await;
Ok(result)
}
pub async fn get_login(user: &str) -> Result<Vec<User>, sqlx::Error> {
let pool = db_connection().await?;
let query = "SELECT id, username, password FROM user WHERE username = $1";
let result: Vec<User> = sqlx::query_as(query).bind(user).fetch_all(&pool).await?;
pool.close().await;
Ok(result)
}

View File

@ -1,3 +1,5 @@
pub mod args_parse;
pub mod handles; pub mod handles;
pub mod models; pub mod models;
pub mod routes; pub mod routes;
pub mod utils;

View File

@ -1,15 +1,20 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize, sqlx::FromRow)]
pub struct User { pub struct User {
pub email: String, pub id: Option<i64>,
#[sqlx(default)]
pub email: Option<String>,
pub username: String, pub username: String,
#[sqlx(default)]
pub password: String, pub password: String,
pub group_id: i64, #[sqlx(default)]
pub group_id: Option<i64>,
} }
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub struct Settings { pub struct Settings {
pub id: i64,
pub channel_name: String, pub channel_name: String,
pub preview_url: String, pub preview_url: String,
pub settings_path: String, pub settings_path: String,

View File

@ -1,9 +1,9 @@
use crate::api::{ use crate::api::{
handles::{add_user, db_connection}, handles::{add_user, db_connection, get_login, get_users},
models::User, models::User,
}; };
use actix_web::{get, post, web, Responder}; use actix_web::{get, post, web, Responder};
use sha_crypt::{sha512_simple, Sha512Params}; use sha_crypt::{sha512_check, sha512_simple, Sha512Params};
#[get("/hello/{name}")] #[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder { async fn greet(name: web::Path<String>) -> impl Responder {
@ -14,7 +14,6 @@ async fn greet(name: web::Path<String>) -> impl Responder {
#[post("/api/user/")] #[post("/api/user/")]
pub async fn user(user: web::Json<User>) -> impl Responder { pub async fn user(user: web::Json<User>) -> impl Responder {
let params = Sha512Params::new(10_000).expect("RandomError!"); let params = Sha512Params::new(10_000).expect("RandomError!");
let hashed_password = sha512_simple(&user.password, &params).expect("Should not fail"); let hashed_password = sha512_simple(&user.password, &params).expect("Should not fail");
// // Verifying a stored password // // Verifying a stored password
@ -23,10 +22,10 @@ pub async fn user(user: web::Json<User>) -> impl Responder {
if let Ok(pool) = db_connection().await { if let Ok(pool) = db_connection().await {
if let Err(e) = add_user( if let Err(e) = add_user(
&pool, &pool,
&user.email, &user.email.clone().unwrap(),
&user.username, &user.username,
&hashed_password, &hashed_password,
&user.group_id, &user.group_id.unwrap(),
) )
.await .await
{ {
@ -39,3 +38,37 @@ pub async fn user(user: web::Json<User>) -> impl Responder {
format!("User {} added", user.username) format!("User {} added", user.username)
} }
#[get("/api/user/{id}")]
pub async fn get_user(id: web::Path<i64>) -> impl Responder {
if let Ok(pool) = db_connection().await {
match get_users(&pool, Some(*id)).await {
Ok(r) => {
return web::Json(r);
}
Err(_) => {
return web::Json(vec![]);
}
};
}
web::Json(vec![])
}
#[post("/auth/login/")]
pub async fn login(credentials: web::Json<User>) -> impl Responder {
let params = Sha512Params::new(10_000).expect("RandomError!");
let hashed_password = sha512_simple(&credentials.password, &params).expect("Should not fail");
println!("{hashed_password}");
if let Ok(u) = get_login(&credentials.username).await {
println!("{}", &u[0].password);
println!("{:?}", sha512_check(&u[0].password, &hashed_password));
if !u.is_empty() && sha512_check(&u[0].password, &hashed_password).is_ok() {
return "login correct!";
}
};
"Login failed!"
}

58
src/api/utils.rs Normal file
View File

@ -0,0 +1,58 @@
use sha_crypt::{sha512_simple, Sha512Params};
use simplelog::*;
use crate::api::{
args_parse::Args,
handles::{add_user, db_connection, db_init},
};
pub async fn run_args(args: Args) -> Result<(), i32> {
if !args.init && args.listen.is_none() && args.username.is_none() {
error!("Wrong number of arguments! Run ffpapi --help for more information.");
return Err(0);
}
if args.init {
if let Err(e) = db_init().await {
panic!("{e}");
};
return Err(0);
}
if let Some(username) = args.username {
if args.email.is_none() || args.password.is_none() {
error!("Email/password missing!");
return Err(1);
}
let params = Sha512Params::new(10_000).expect("RandomError!");
let hashed_password =
sha512_simple(&args.password.unwrap(), &params).expect("Should not fail");
match db_connection().await {
Ok(pool) => {
if let Err(e) =
add_user(&pool, &args.email.unwrap(), &username, &hashed_password, &1).await
{
pool.close().await;
error!("{e}");
return Err(1);
};
pool.close().await;
info!("Create admin user \"{username}\" done...");
return Err(0);
}
Err(e) => {
error!("Add admin user failed! Did you init the database?");
panic!("{e}")
}
}
}
Ok(())
}

View File

@ -2,95 +2,31 @@ use std::process::exit;
use actix_web::{App, HttpServer}; use actix_web::{App, HttpServer};
use clap::Parser; use clap::Parser;
use sha_crypt::{sha512_simple, Sha512Params};
use simplelog::*; use simplelog::*;
use ffplayout_engine::{ use ffplayout_engine::{
api::{ api::{
handles::{add_user, db_connection, db_init}, args_parse::Args,
routes::user, routes::{get_user, login, user},
utils::run_args,
}, },
utils::{init_logging, GlobalConfig}, utils::{init_logging, GlobalConfig},
}; };
#[derive(Parser, Debug)]
#[clap(version,
name = "ffpapi",
version = "0.1.0",
about = "ffplayout REST API",
long_about = None)]
pub struct Args {
#[clap(short, long, help = "Listen on IP:PORT, like: 127.0.0.1:8080")]
pub listen: Option<String>,
#[clap(short, long, help = "Initialize Database")]
pub init: bool,
#[clap(short, long, help = "Create admin user")]
pub username: Option<String>,
#[clap(short, long, help = "Admin email")]
pub email: Option<String>,
#[clap(short, long, help = "Admin password")]
pub password: Option<String>,
}
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
let args = Args::parse(); let args = Args::parse();
if !args.init && args.listen.is_none() && args.username.is_none() {
error!("Wrong number of arguments! Run ffpapi --help for more information.");
exit(1);
}
let mut config = GlobalConfig::new(None); let mut config = GlobalConfig::new(None);
config.mail.recipient = String::new(); config.mail.recipient = String::new();
config.logging.log_to_file = false; config.logging.log_to_file = false;
config.logging.timestamp = false;
let logging = init_logging(&config, None, None); let logging = init_logging(&config, None, None);
CombinedLogger::init(logging).unwrap(); CombinedLogger::init(logging).unwrap();
if args.init { if let Err(c) = run_args(args.clone()).await {
if let Err(e) = db_init().await { exit(c);
panic!("{e}");
};
exit(0);
}
if let Some(username) = args.username {
if args.email.is_none() || args.password.is_none() {
error!("Email/password missing!");
exit(1);
}
let params = Sha512Params::new(10_000).expect("RandomError!");
let hashed_password =
sha512_simple(&args.password.unwrap(), &params).expect("Should not fail");
match db_connection().await {
Ok(pool) => {
if let Err(e) =
add_user(&pool, &args.email.unwrap(), &username, &hashed_password, &1).await
{
pool.close().await;
error!("{e}");
exit(1);
};
pool.close().await;
info!("Create admin user \"{username}\" done...");
exit(0);
}
Err(e) => {
panic!("{e}")
}
}
} }
if let Some(conn) = args.listen { if let Some(conn) = args.listen {
@ -99,11 +35,13 @@ async fn main() -> std::io::Result<()> {
let port = ip_port[1].parse::<u16>().unwrap(); let port = ip_port[1].parse::<u16>().unwrap();
info!("running ffplayout API, listen on {conn}"); info!("running ffplayout API, listen on {conn}");
HttpServer::new(|| App::new().service(user)) HttpServer::new(|| App::new().service(get_user).service(login).service(user))
.bind((addr, port))? .bind((addr, port))?
.run() .run()
.await .await
} else { } else {
panic!("Run ffpapi with listen parameter!") error!("Run ffpapi with listen parameter!");
Ok(())
} }
} }