mod api; mod command; mod util; use anyhow::bail; use api::{AdminAction, Args, CliError, Command, CreateObject, DevObject, GetObject, UpdateObject}; use clap::Parser; use command::dev_oauth2_listen; use env_logger::Env; use secd::{ auth::z, Credential, CredentialType, Secd, ENV_AUTH_STORE_CONN_STRING, ENV_SPICE_SECRET, ENV_SPICE_SERVER, }; use util::{error_detail, Result}; use uuid::Uuid; use crate::api::ValidationMethod; const CONFIG_DIR_NAME: &str = "secdiam"; const CONFIG_PROFILE_FILE: &str = "profiles.toml"; const CONFIG_LOGIN_TEMPLATE: &str = "default_login.html"; const CONFIG_SIGNUP_TEMPLATE: &str = "default_signup.html"; const ISSUE_TRACKER_LOC: &str = "https://www.github.com/secdiam/iam"; #[tokio::main] async fn main() { env_logger::init_from_env(Env::default().default_filter_or("debug")); match exec().await { Ok(Some(s)) => println!("{}", s), Err(e) => { println!("{}", e); std::process::exit(1); } _ => {} } } async fn exec() -> Result> { let args = Args::parse(); Ok(match args.command { Command::Admin { action: AdminAction::Init { interactive }, } => { command::admin_init(interactive) .await .map_err(|_| CliError::AdminInitializationError)?; None } rest @ _ => { // let cfg = util::read_config(args.profile).map_err(|_| CliError::InvalidProfile)?; std::env::set_var( ENV_AUTH_STORE_CONN_STRING, // "sqlite:///home/benj/.config/secdiam/34wxC.sql?mode=rwc", "postgresql://secduser:p4ssw0rd@localhost:5412/secd", ); std::env::set_var(ENV_SPICE_SECRET, "sup3rs3cr3tk3y"); std::env::set_var(ENV_SPICE_SERVER, "http://[::1]:50051"); let secd = Secd::init(None) .await .map_err(|e| CliError::SecdInitializationFailure(e.to_string()))?; match rest { Command::Admin { action } => admin(&secd, action).await?, Command::Create { object } => create(&secd, object).await?, Command::Dev { object } => dev(object).await?, Command::Get { object } => get(&secd, object).await?, Command::Repl => { unimplemented!() } Command::Update { object } => update(&secd, object).await?, } } }) } async fn admin(secd: &Secd, cmd: AdminAction) -> Result> { Ok(match cmd { AdminAction::Backend { action } => { println!("do backend stuff!"); None } AdminAction::Create { object } => todo!(), AdminAction::Seal => { println!("do seal"); None } AdminAction::Unseal { secret_key } => { println!("do unseal: {}", secret_key); None } AdminAction::Init { .. } => { panic!("Invariant violation: this path should be impossible") } }) } async fn create(secd: &Secd, cmd: CreateObject) -> Result> { Ok(match cmd { CreateObject::Credential { method, identity_id, } => { let t = match method { api::CredentialMethod::Passphrase { username, passphrase, } => CredentialType::Passphrase { key: username, value: passphrase, }, }; let credential = secd.create_credential(t, identity_id).await?; Some(serde_json::ser::to_string(&credential)?.to_string()) } CreateObject::Validation { method, identity_id, } => match method { ValidationMethod::Email { address } => { let validation = secd.validate_email(&address, identity_id).await?; Some(serde_json::ser::to_string(&validation)?.to_string()) } _ => unimplemented!(), }, CreateObject::ValidationCompletion { validation_id, token, code, } => { if token.is_none() && code.is_none() { bail!("A token or code must be specified") } let session = secd .complete_address_validation(&validation_id, token, code) .await?; Some(serde_json::ser::to_string(&session)?.to_string()) } }) } async fn dev(cmd: DevObject) -> Result> { Ok(match cmd { DevObject::Oauth2Server { port } => serde_json::to_string(&dev_oauth2_listen(port)?).ok(), }) } async fn get(secd: &Secd, cmd: GetObject) -> Result> { Ok(match cmd { GetObject::ApiKey { public_key } => { println!("get object api key"); None } GetObject::Group { name, id } => { println!("get object group"); None } GetObject::Identity { identity_id, session_token, } => Some( serde_json::ser::to_string(&secd.get_identity(identity_id, session_token).await?)? .to_string(), ), GetObject::Permission { name, id } => { println!("get object permission"); None } GetObject::Role { name, id } => { println!("get object role"); None } GetObject::Service { name, id } => { println!("get object service"); None } GetObject::ServiceAction { name, id } => { println!("get object service action"); None } GetObject::Session { secret } => { Some(serde_json::ser::to_string(&secd.get_session(&secret).await?)?.to_string()) } GetObject::Validation { id } => { println!("get object validation"); None } }) } async fn update(secd: &Secd, cmd: UpdateObject) -> Result> { Ok(match cmd { UpdateObject::Identity { id, metadata } => { let identity = if metadata.is_some() { secd.update_identity_metadata(id, metadata.unwrap()).await? } else { secd.get_identity(Some(id), None).await? }; Some(serde_json::to_string(&identity)?.to_string()) } }) }