diff --git a/services/identity/Cargo.toml b/services/identity/Cargo.toml --- a/services/identity/Cargo.toml +++ b/services/identity/Cargo.toml @@ -9,7 +9,7 @@ prost = "0.11" tokio = { version = "1.24", features = ["macros", "rt-multi-thread"] } ed25519-dalek = "1" -clap = { version = "3.1.12", features = ["derive"] } +clap = { version = "3.1.12", features = ["derive", "env"] } derive_more = "0.99" aws-config = "0.54.0" aws-sdk-dynamodb = "0.24.0" diff --git a/services/identity/src/config.rs b/services/identity/src/config.rs --- a/services/identity/src/config.rs +++ b/services/identity/src/config.rs @@ -1,5 +1,7 @@ use base64::{engine::general_purpose, DecodeError, Engine as _}; +use clap::{Parser, Subcommand}; use once_cell::sync::Lazy; +use path::Path; use std::{collections::HashSet, env, fmt, fs, io, path}; use tracing::{error, info}; @@ -9,13 +11,54 @@ TUNNELBROKER_GRPC_ENDPOINT, }; -pub static CONFIG: Lazy = - Lazy::new(|| Config::load().expect("failed to load config")); +pub static CONFIG: Lazy = Lazy::new(|| { + let args = Cli::parse(); + Config::load(args.command).expect("failed to load config") +}); pub(super) fn load_config() { Lazy::force(&CONFIG); } +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +#[clap(propagate_version = true)] +pub struct Cli { + #[clap(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// Runs the server + Server { + #[clap(short, long)] + #[clap(env = LOCALSTACK_ENDPOINT)] + localstack_endpoint: Option, + // Opaque 2.0 server secrets + #[clap(short, long)] + #[clap(env = OPAQUE_SERVER_SETUP)] + #[clap(default_value_t = format!("{}/{}", SECRETS_DIRECTORY, SECRETS_SETUP_FILE))] + server_setup_path: String, + #[clap(short, long)] + #[clap(env = KEYSERVER_PUBLIC_KEY)] + keyserver_public_key: Option, + #[clap(short, long)] + #[clap(env = TUNNELBROKER_GRPC_ENDPOINT)] + #[clap(default_value_t = DEFAULT_TUNNELBROKER_ENDPOINT.to_string())] + tunnelbroker_endpoint: String, + }, + /// Generates and persists a keypair to use for PAKE registration and login + Keygen { + #[clap(short, long)] + #[clap(default_value_t = String::from(SECRETS_DIRECTORY))] + dir: String, + }, + /// Populates the `identity-users` table in DynamoDB from MySQL + PopulateDB, +} + +// This config is the result of loading the values pass from the cli #[derive(Clone)] pub struct Config { pub localstack_endpoint: Option, @@ -28,37 +71,19 @@ } impl Config { - fn load() -> Result { - let localstack_endpoint = env::var(LOCALSTACK_ENDPOINT).ok(); - let tunnelbroker_endpoint = match env::var(TUNNELBROKER_GRPC_ENDPOINT) { - Ok(val) => { - info!("Using tunnelbroker endpoint from env var: {}", val); - val - } - Err(std::env::VarError::NotPresent) => { - let val = DEFAULT_TUNNELBROKER_ENDPOINT; - info!("Falling back to default tunnelbroker endpoint: {}", val); - val.to_string() - } - Err(e) => { - error!( - "Failed to read environment variable {}: {:?}", - TUNNELBROKER_GRPC_ENDPOINT, e - ); - return Err(Error::Env(e)); - } + fn load(args: Commands) -> Result { + let Commands::Server { + localstack_endpoint, + server_setup_path, + keyserver_public_key, + tunnelbroker_endpoint + } = args else { + return Err(Error::InvalidCommand); }; - let mut path_buf = path::PathBuf::new(); - path_buf.push(SECRETS_DIRECTORY); - path_buf.push(SECRETS_SETUP_FILE); - - let server_setup = get_server_setup(path_buf.as_path())?; - + let server_setup = get_server_setup(&Path::new(&server_setup_path))?; let reserved_usernames = get_reserved_usernames_set()?; - let keyserver_public_key = env::var(KEYSERVER_PUBLIC_KEY).ok(); - Ok(Self { localstack_endpoint, server_setup, @@ -91,6 +116,8 @@ Json(serde_json::Error), #[display(...)] Decode(DecodeError), + #[display(...)] + InvalidCommand, } fn get_server_setup( diff --git a/services/identity/src/main.rs b/services/identity/src/main.rs --- a/services/identity/src/main.rs +++ b/services/identity/src/main.rs @@ -1,6 +1,5 @@ use std::time::Duration; -use clap::{Parser, Subcommand}; use database::DatabaseClient; use moka::future::Cache; use tonic::transport::Server; @@ -21,38 +20,17 @@ mod token; mod tunnelbroker; -use config::load_config; -use constants::{IDENTITY_SERVICE_SOCKET_ADDR, SECRETS_DIRECTORY}; +use config::{load_config, Cli, Commands}; +use constants::IDENTITY_SERVICE_SOCKET_ADDR; use keygen::generate_and_persist_keypair; use tracing::{self, info, Level}; use tracing_subscriber::EnvFilter; +use clap::Parser; use client_service::{ClientService, IdentityClientServiceServer}; use grpc_services::authenticated::auth_proto::identity_client_service_server::IdentityClientServiceServer as AuthServer; use grpc_services::authenticated::AuthenticatedService; -#[derive(Parser)] -#[clap(author, version, about, long_about = None)] -#[clap(propagate_version = true)] -struct Cli { - #[clap(subcommand)] - command: Commands, -} - -#[derive(Subcommand)] -enum Commands { - /// Runs the server - Server, - /// Generates and persists a keypair to use for PAKE registration and login - Keygen { - #[clap(short, long)] - #[clap(default_value_t = String::from(SECRETS_DIRECTORY))] - dir: String, - }, - /// Populates the `identity-users` table in DynamoDB from MySQL - PopulateDB, -} - #[tokio::main] async fn main() -> Result<(), Box> { let filter = EnvFilter::builder() @@ -66,9 +44,9 @@ let cli = Cli::parse(); match &cli.command { Commands::Keygen { dir } => { - generate_and_persist_keypair(dir)?; + generate_and_persist_keypair(&dir)?; } - Commands::Server => { + Commands::Server { .. } => { load_config(); let addr = IDENTITY_SERVICE_SOCKET_ADDR.parse()?; let aws_config = aws_config::from_env().region("us-east-2").load().await;