diff --git a/Cargo.lock b/Cargo.lock index b67160c..dbffde4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1619,7 +1619,7 @@ dependencies = [ [[package]] name = "tapo-rest" -version = "0.1.1" +version = "0.1.2" dependencies = [ "anyhow", "axum", diff --git a/Cargo.toml b/Cargo.toml index 78f08d5..a74e8da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tapo-rest" -version = "0.1.1" +version = "0.1.2" edition = "2021" [dependencies] diff --git a/src/cmd.rs b/src/cmd.rs index 5e92aa9..15b74b5 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -16,6 +16,16 @@ pub struct ServerConfig { #[clap(short, long, help = "Port to serve on")] pub port: u16, + #[clap(flatten)] + pub password: PasswordArgGroup, +} + +#[derive(Parser)] +#[group(required = true, multiple = false)] +pub struct PasswordArgGroup { #[clap(short, long, help = "Login password")] - pub auth_password: String, + pub auth_password: Option, + + #[clap(short = 'f', long, help = "Read the login password from a file")] + pub password_from_file: Option, } diff --git a/src/server/mod.rs b/src/server/mod.rs index 3260747..6c930f1 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,6 +1,6 @@ -use std::{path::PathBuf, sync::Arc}; +use std::{fs, path::PathBuf, sync::Arc}; -use anyhow::Result; +use anyhow::{bail, Context, Result}; use axum::{ routing::{get, post}, Router, @@ -9,7 +9,7 @@ use tokio::{net::TcpListener, sync::RwLock}; use tower_http::cors::{AllowHeaders, AllowMethods, AllowOrigin, CorsLayer}; use crate::{ - cmd::ServerConfig, + cmd::{PasswordArgGroup, ServerConfig}, devices::TapoDevice, server::{actions::make_router, state::StateInit}, }; @@ -34,10 +34,31 @@ pub async fn serve( devices: Vec, sessions_file: PathBuf, ) -> Result<()> { - let ServerConfig { - port, + let ServerConfig { port, password } = config; + + let PasswordArgGroup { auth_password, - } = config; + password_from_file, + } = password; + + let auth_password = match (auth_password, password_from_file) { + (Some(auth_password), None) => auth_password, + + (None, Some(file)) => { + if !file.is_file() { + bail!( + "Provided file password path does not exist: {}", + file.display() + ); + } + + fs::read_to_string(&file).with_context(|| { + format!("Failed to read file password at path: {}", file.display()) + })? + } + + (Some(_), Some(_)) | (None, None) => unreachable!(), + }; let cors = CorsLayer::new() .allow_methods(AllowMethods::any())