cleanup code, reponse object

This commit is contained in:
jb-alvarado 2022-06-08 18:06:40 +02:00
parent 58b0df5757
commit cca5e24cc9
7 changed files with 168 additions and 128 deletions

76
Cargo.lock generated
View File

@ -635,6 +635,7 @@ dependencies = [
"ffprobe",
"file-rotate",
"jsonrpc-http-server",
"jsonwebtoken",
"lettre",
"log",
"notify",
@ -709,7 +710,7 @@ dependencies = [
"futures-core",
"futures-sink",
"pin-project",
"spin",
"spin 0.9.3",
]
[[package]]
@ -1202,6 +1203,20 @@ dependencies = [
"unicase",
]
[[package]]
name = "jsonwebtoken"
version = "8.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc9051c17f81bae79440afa041b3a278e1de71bfb96d32454b477fd4703ccb6f"
dependencies = [
"base64",
"pem",
"ring",
"serde",
"serde_json",
"simple_asn1",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@ -1462,6 +1477,17 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
@ -1638,6 +1664,15 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc"
[[package]]
name = "pem"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9a3b09a20e374558580a4914d3b7d89bd61b954a5a5e1dcbea98753addb1947"
dependencies = [
"base64",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
@ -1837,6 +1872,21 @@ dependencies = [
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin 0.5.2",
"untrusted",
"web-sys",
"winapi 0.3.9",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
@ -1998,6 +2048,18 @@ dependencies = [
"libc",
]
[[package]]
name = "simple_asn1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085"
dependencies = [
"num-bigint",
"num-traits",
"thiserror",
"time 0.3.9",
]
[[package]]
name = "simplelog"
version = "0.12.0"
@ -2032,6 +2094,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spin"
version = "0.9.3"
@ -2424,6 +2492,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "url"
version = "2.2.2"

View File

@ -18,6 +18,7 @@ faccess = "0.2"
ffprobe = "0.3"
file-rotate = { git = "https://github.com/Ploppz/file-rotate.git", branch = "timestamp-parse-fix" }
jsonrpc-http-server = "18.0"
jsonwebtoken = "8"
lettre = "0.10.0-rc.6"
log = "0.4"
notify = "4.0"
@ -30,8 +31,11 @@ serde_json = "1.0"
serde_yaml = "0.8"
shlex = "1.1"
simplelog = { version = "^0.12", features = ["paris"] }
sqlx = { version = "0.5", features = [ "chrono", "runtime-actix-native-tls", "sqlite" ] }
sqlx = { version = "0.5", features = [
"chrono",
"runtime-actix-native-tls",
"sqlite"
] }
time = { version = "0.3", features = ["formatting", "macros"] }
walkdir = "2"

View File

@ -1,6 +1,7 @@
use std::path::Path;
use faccess::PathExt;
use rand::{distributions::Alphanumeric, Rng};
use simplelog::*;
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteQueryResult, Pool, Sqlite, SqlitePool};
@ -20,7 +21,7 @@ pub fn db_path() -> Result<String, Box<dyn std::error::Error>> {
}
async fn cretea_schema() -> Result<SqliteQueryResult, sqlx::Error> {
let pool = db_connection().await?;
let conn = db_connection().await?;
let query = "PRAGMA foreign_keys = ON;
CREATE TABLE IF NOT EXISTS groups
(
@ -35,6 +36,7 @@ async fn cretea_schema() -> Result<SqliteQueryResult, sqlx::Error> {
preview_url TEXT NOT NULL,
settings_path TEXT NOT NULL,
extra_extensions TEXT NOT NULL,
secret TEXT NOT NULL,
UNIQUE(channel_name)
);
CREATE TABLE IF NOT EXISTS user
@ -48,8 +50,8 @@ async fn cretea_schema() -> Result<SqliteQueryResult, sqlx::Error> {
FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE SET NULL ON DELETE SET NULL,
UNIQUE(email, username)
);";
let result = sqlx::query(query).execute(&pool).await;
pool.close().await;
let result = sqlx::query(query).execute(&conn).await;
conn.close().await;
result
}
@ -64,14 +66,19 @@ pub async fn db_init() -> Result<&'static str, Box<dyn std::error::Error>> {
Err(e) => panic!("{e}"),
}
}
let secret: String = rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(80)
.map(char::from)
.collect();
let instances = db_connection().await?;
let query = "INSERT INTO groups(name) VALUES('admin'), ('user');
INSERT INTO settings(channel_name, preview_url, settings_path, extra_extensions)
INSERT INTO settings(channel_name, preview_url, settings_path, extra_extensions, secret)
VALUES('Channel 1', 'http://localhost/live/preview.m3u8',
'/etc/ffplayout/ffplayout.yml', '.jpg,.jpeg,.png');";
sqlx::query(query).execute(&instances).await?;
'/etc/ffplayout/ffplayout.yml', '.jpg,.jpeg,.png', $1);";
sqlx::query(query).bind(secret).execute(&instances).await?;
instances.close().await;
Ok("Database initialized!")
@ -79,20 +86,19 @@ pub async fn db_init() -> Result<&'static str, Box<dyn std::error::Error>> {
pub async fn db_connection() -> Result<Pool<Sqlite>, sqlx::Error> {
let db_path = db_path().unwrap();
let conn = SqlitePool::connect(&db_path).await?;
let pool = SqlitePool::connect(&db_path).await?;
Ok(pool)
Ok(conn)
}
pub async fn add_user(
instances: &SqlitePool,
mail: &str,
user: &str,
pass: &str,
salt: &str,
group: &i64,
) -> Result<SqliteQueryResult, sqlx::Error> {
let conn = db_connection().await?;
let query =
"INSERT INTO user (email, username, password, salt, group_id) VALUES($1, $2, $3, $4, $5)";
let result = sqlx::query(query)
@ -101,32 +107,18 @@ pub async fn add_user(
.bind(pass)
.bind(salt)
.bind(group)
.execute(instances)
.execute(&conn)
.await?;
conn.close().await;
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, salt FROM user WHERE username = $1";
let result: Vec<User> = sqlx::query_as(query).bind(user).fetch_all(&pool).await?;
pool.close().await;
pub async fn get_login(user: &str) -> Result<User, sqlx::Error> {
let conn = db_connection().await?;
let query = "SELECT id, email, username, password, salt FROM user WHERE username = $1";
let result: User = sqlx::query_as(query).bind(user).fetch_one(&conn).await?;
conn.close().await;
Ok(result)
}

View File

@ -2,23 +2,30 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, sqlx::FromRow)]
pub struct User {
#[sqlx(default)]
pub id: Option<i64>,
#[sqlx(default)]
pub email: Option<String>,
pub username: String,
#[sqlx(default)]
#[serde(skip_serializing)]
pub password: String,
#[sqlx(default)]
#[serde(skip_serializing)]
pub salt: Option<String>,
#[sqlx(default)]
#[serde(skip_serializing)]
pub group_id: Option<i64>,
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, sqlx::FromRow)]
pub struct Settings {
pub id: i64,
pub channel_name: String,
pub preview_url: String,
pub settings_path: String,
pub extra_extensions: String,
#[sqlx(default)]
#[serde(skip_serializing)]
pub secret: String,
}

View File

@ -1,81 +1,59 @@
use crate::api::{
handles::{db_connection, get_login, get_users},
models::User,
};
use actix_web::{get, post, web, Responder};
use argon2::{password_hash::PasswordHash, Argon2, PasswordVerifier};
use serde::Serialize;
use simplelog::*;
use crate::api::{handles::get_login, models::User};
#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {
format!("Hello {name}!")
}
// /// curl -X POST -H "Content-Type: application/json" -d '{"username": "USER", "password": "abc123", "email":"user@example.org" }' http://127.0.0.1:8080/api/user/
// #[post("/api/user/")]
// pub async fn user(user: web::Json<User>) -> impl Responder {
// let params = Sha512Params::new(10_000).expect("RandomError!");
// let hashed_password = sha512_simple(&user.password, &params).expect("Should not fail");
// // // Verifying a stored password
// // assert!(sha512_check("Not so secure password", &hashed_password).is_ok());
// if let Ok(pool) = db_connection().await {
// if let Err(e) = add_user(
// &pool,
// &user.email.clone().unwrap(),
// &user.username,
// &hashed_password,
// &user.group_id.unwrap(),
// )
// .await
// {
// pool.close().await;
// return e.to_string();
// };
// pool.close().await;
// }
// format!("User {} added", user.username)
// }
/// curl -X GET http://127.0.0.1:8080/api/user/1
#[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![])
#[derive(Serialize)]
struct ResponseObj<T> {
message: String,
status: i32,
data: Option<T>,
}
/// curl -X POST -H "Content-Type: application/json" -d '{"username": "USER", "password": "abc123" }' http://127.0.0.1:8080/auth/login/
#[post("/auth/login/")]
pub async fn login(credentials: web::Json<User>) -> impl Responder {
if let Ok(u) = get_login(&credentials.username).await {
if u.is_empty() {
return "User not found";
}
let pass = u[0].password.clone();
match get_login(&credentials.username).await {
Ok(mut user) => {
let pass = user.password.clone();
user.password = "".into();
user.salt = None;
if let Ok(hash) = PasswordHash::new(&pass) {
let hash = PasswordHash::new(&pass).unwrap();
if Argon2::default()
.verify_password(credentials.password.as_bytes(), &hash)
.is_ok()
{
info!("user {} login", credentials.username);
return "login correct!";
}
};
};
error!("Login {} failed!", credentials.username);
"Login failed!"
web::Json(ResponseObj {
message: "login correct!".into(),
status: 200,
data: Some(user),
})
} else {
error!("Wrong password for {}!", credentials.username);
web::Json(ResponseObj {
message: "Wrong password!".into(),
status: 401,
data: None,
})
}
}
Err(e) => {
error!("Login {} failed! {e}", credentials.username);
return web::Json(ResponseObj {
message: format!("Login {} failed!", credentials.username),
status: 404,
data: None,
});
}
}
}

View File

@ -6,7 +6,7 @@ use simplelog::*;
use crate::api::{
args_parse::Args,
handles::{add_user, db_connection, db_init},
handles::{add_user, db_init},
};
pub async fn run_args(args: Args) -> Result<(), i32> {
@ -42,33 +42,22 @@ pub async fn run_args(args: Args) -> Result<(), i32> {
}
};
match db_connection().await {
Ok(pool) => {
if let Err(e) = add_user(
&pool,
&args.email.unwrap(),
&username,
&password_hash.to_string(),
&salt.to_string(),
&1,
)
.await
{
pool.close().await;
error!("{e}");
return Err(1);
};
if let Err(e) = add_user(
&args.email.unwrap(),
&username,
&password_hash.to_string(),
&salt.to_string(),
&1,
)
.await
{
error!("{e}");
return Err(1);
};
pool.close().await;
info!("Create admin user \"{username}\" done...");
info!("Create admin user \"{username}\" done...");
return Err(0);
}
Err(e) => {
error!("Add admin user failed! Did you init the database?");
panic!("{e}")
}
}
return Err(0);
}
Ok(())

View File

@ -5,11 +5,7 @@ use clap::Parser;
use simplelog::*;
use ffplayout_engine::{
api::{
args_parse::Args,
routes::{get_user, login},
utils::run_args,
},
api::{args_parse::Args, routes::login, utils::run_args},
utils::{init_logging, GlobalConfig},
};
@ -35,7 +31,7 @@ async fn main() -> std::io::Result<()> {
let port = ip_port[1].parse::<u16>().unwrap();
info!("running ffplayout API, listen on {conn}");
HttpServer::new(|| App::new().service(get_user).service(login))
HttpServer::new(|| App::new().service(login))
.bind((addr, port))?
.run()
.await