diff --git a/Cargo.lock b/Cargo.lock index a840f695..60da8ec6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1274,6 +1274,7 @@ dependencies = [ "static-files", "sysinfo", "tokio", + "uuid", ] [[package]] diff --git a/ffplayout-api/Cargo.toml b/ffplayout-api/Cargo.toml index 50978ed2..e6658834 100644 --- a/ffplayout-api/Cargo.toml +++ b/ffplayout-api/Cargo.toml @@ -46,6 +46,7 @@ static-files = "0.2" sysinfo ={ version = "0.30", features = ["linux-netdevs"] } sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] } tokio = { version = "1.29", features = ["full"] } +uuid = "1.8" [build-dependencies] static-files = "0.2" diff --git a/ffplayout-api/examples/uuid_auth.rs b/ffplayout-api/examples/uuid_auth.rs new file mode 100644 index 00000000..13739d20 --- /dev/null +++ b/ffplayout-api/examples/uuid_auth.rs @@ -0,0 +1,80 @@ +/// Example for a simple auth mechanism in SSE. +/// +/// get new UUID: curl -X GET http://127.0.0.1:8080/generate +/// use UUID: curl --header "UUID: f2f8c29b-712a-48c5-8919-b535d3a05a3a" -X GET http://127.0.0.1:8080/check +/// +use std::{collections::HashSet, sync::Mutex, time::Duration, time::SystemTime}; + +use actix_web::{middleware::Logger, web, App, HttpRequest, HttpResponse, HttpServer}; +use simplelog::*; +use uuid::Uuid; + +use ffplayout_lib::utils::{init_logging, PlayoutConfig}; + +#[derive(Debug, Eq, Hash, PartialEq)] +struct UuidData { + uuid: Uuid, + expiration_time: SystemTime, +} + +struct AppState { + uuids: Mutex>, +} + +fn prune_uuids(uuids: &mut HashSet) { + uuids.retain(|entry| entry.expiration_time > SystemTime::now()); +} + +async fn generate_uuid(data: web::Data) -> HttpResponse { + let uuid = Uuid::new_v4(); + let expiration_time = SystemTime::now() + Duration::from_secs(30); // 24 * 3600 -> for 24 hours + let mut uuids = data.uuids.lock().unwrap(); + + prune_uuids(&mut uuids); + + uuids.insert(UuidData { + uuid, + expiration_time, + }); + + HttpResponse::Ok().body(uuid.to_string()) +} + +async fn check_uuid(data: web::Data, req: HttpRequest) -> HttpResponse { + let uuid = req.headers().get("uuid").unwrap().to_str().unwrap(); + let uuid_from_client = Uuid::parse_str(uuid).unwrap(); + let mut uuids = data.uuids.lock().unwrap(); + + prune_uuids(&mut uuids); + + match uuids.iter().find(|entry| entry.uuid == uuid_from_client) { + Some(_) => HttpResponse::Ok().body("UUID is valid"), + None => HttpResponse::Unauthorized().body("Invalid or expired UUID"), + } +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let mut config = PlayoutConfig::new(None, None); + config.mail.recipient = String::new(); + config.logging.log_to_file = false; + config.logging.timestamp = false; + + let logging = init_logging(&config, None, None); + CombinedLogger::init(logging).unwrap(); + + let state = web::Data::new(AppState { + uuids: Mutex::new(HashSet::new()), + }); + + HttpServer::new(move || { + App::new() + .app_data(state.clone()) + .wrap(Logger::default()) + .route("/generate", web::get().to(generate_uuid)) + .route("/check", web::get().to(check_uuid)) + }) + .bind("127.0.0.1:8080")? + .run() + .await +}