diff options
Diffstat (limited to '')
| -rw-r--r-- | crates/iam/Cargo.toml | 2 | ||||
| -rw-r--r-- | crates/iam/src/api.rs | 98 | ||||
| -rw-r--r-- | crates/iam/src/main.rs | 143 |
3 files changed, 129 insertions, 114 deletions
diff --git a/crates/iam/Cargo.toml b/crates/iam/Cargo.toml index 966f341..2ee6fc6 100644 --- a/crates/iam/Cargo.toml +++ b/crates/iam/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] anyhow = "1.0" async-std = { version = "1.12.0", features = [ "attributes" ] } +base64 = "0.21.0" clap = { version = "4.0.29", features = ["derive"] } colored = "2.0.0" email_address = "0.2" @@ -18,6 +19,7 @@ serde = "1" serde_json = { version = "1.0", features = ["raw_value"] } strum = "0.24.1" strum_macros = "0.24" +time = { version = "0.3", features = [ "serde" ] } tiny_http = "0.12" tokio = { version = "1.23.0", features = ["full"] } toml = "0.5.9" diff --git a/crates/iam/src/api.rs b/crates/iam/src/api.rs index af175a7..c662e0c 100644 --- a/crates/iam/src/api.rs +++ b/crates/iam/src/api.rs @@ -97,6 +97,14 @@ pub enum Command { #[command(subcommand)] object: UpdateObject, }, + #[command( + about = "Validate an IAM credential, optionally returning a session from the validation", + long_about = "Validate\n\nCredentials which have been created for identities may be validated and optionally exchanged for sessions." + )] + Validate { + #[command(subcommand)] + object: ValidateObject, + }, } #[derive(Subcommand)] @@ -248,11 +256,15 @@ pub enum DevObject { #[derive(Subcommand)] pub enum CredentialMethod { - /// A + ApiToken { + #[arg(long)] + expires_at: Option<i64>, + }, + /// Unique username and passphrase credential. Each username may have at most one Passprhase credential. Passphrase { - /// B + /// The username associated with this credential username: String, - /// C + /// The secret passphrase for this credential passphrase: String, }, } @@ -274,60 +286,13 @@ pub enum ValidationMethod { #[derive(Subcommand)] pub enum GetObject { - ApiKey { - /// Public key associated with this api key set - public_key: String, - }, - Group { - /// Unique group name - name: String, - /// Unique group id - #[arg(long, short)] - id: Option<Uuid>, - }, Identity { /// The unique id corresponding to this identity. #[arg(long, short)] identity_id: Option<Uuid>, - /// Any session corresponding to this identity. - #[arg(long, short)] - session_token: Option<String>, - }, - Permission { - /// Unique permission name - name: String, - /// Unique permission id - #[arg(long, short)] - id: Option<Uuid>, - }, - Role { - /// Unique role name - name: String, - /// Unique role id - #[arg(long, short)] - id: Option<Uuid>, - }, - Session { - /// The plaintext token which uniquely identifies the session - secret: String, - }, - Service { - /// Unique service name - name: String, - /// Unique service id - #[arg(long, short)] - id: Option<Uuid>, - }, - ServiceAction { - /// Unique service action name - name: String, - /// Unique service action id - #[arg(long, short)] - id: Option<Uuid>, - }, - Validation { - /// Unique validation request id - id: Uuid, + /// The credential corresponding to this identity. + #[command(subcommand)] + credential: Option<ValidateObject>, }, } @@ -340,6 +305,33 @@ pub enum UpdateObject { #[arg(long, short)] metadata: Option<String>, }, + Credential { + /// Unique identifier for this credential. + /// Note: You can validate the credential to find it's id. + id: Uuid, + /// Whether to revoke this credential. Once revoked, the credential may no longer be used + /// and may not be un-revoked. + #[arg(long, short, action)] + revoke: bool, + }, +} + +#[derive(Subcommand)] +pub enum ValidateObject { + ApiToken { + /// Api token to validate + token: String, + }, + Passphrase { + /// The username associated with this credential + username: String, + /// The secret passphrase for this credential + passphrase: String, + }, + Session { + /// The secret token associated with this session. + token: String, + }, } #[derive(Serialize, Deserialize)] diff --git a/crates/iam/src/main.rs b/crates/iam/src/main.rs index c679af6..28f4e4c 100644 --- a/crates/iam/src/main.rs +++ b/crates/iam/src/main.rs @@ -2,19 +2,19 @@ mod api; mod command; mod util; +use crate::api::ValidationMethod; use anyhow::bail; -use api::{AdminAction, Args, CliError, Command, CreateObject, DevObject, GetObject, UpdateObject}; +use api::{ + AdminAction, Args, CliError, Command, CreateObject, DevObject, GetObject, UpdateObject, + ValidateObject, +}; + 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; +use secd::{CredentialType, Secd}; +use time::OffsetDateTime; +use util::Result; const CONFIG_DIR_NAME: &str = "secdiam"; const CONFIG_PROFILE_FILE: &str = "profiles.toml"; @@ -49,15 +49,7 @@ async fn exec() -> Result<Option<String>> { 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) + let secd = Secd::init(None, None) .await .map_err(|e| CliError::SecdInitializationFailure(e.to_string()))?; @@ -70,6 +62,7 @@ async fn exec() -> Result<Option<String>> { unimplemented!() } Command::Update { object } => update(&secd, object).await?, + Command::Validate { object } => validate(&secd, object).await?, } } }) @@ -101,18 +94,27 @@ async fn create(secd: &Secd, cmd: CreateObject) -> Result<Option<String>> { method, identity_id, } => { - let t = match method { + let t = match &method { api::CredentialMethod::Passphrase { username, passphrase, } => CredentialType::Passphrase { - key: username, - value: passphrase, + key: username.clone(), + value: passphrase.clone(), }, + api::CredentialMethod::ApiToken { .. } => CredentialType::new_api_token()?, + }; + + let expires_at = match method { + api::CredentialMethod::ApiToken { expires_at, .. } => expires_at.map(|t| { + OffsetDateTime::from_unix_timestamp(t) + .expect("The provided value is an invalid unix timestamp") + }), + api::CredentialMethod::Passphrase { .. } => None, }; - let credential = secd.create_credential(t, identity_id).await?; - Some(serde_json::ser::to_string(&credential)?.to_string()) + let credential = secd.create_credential(t, identity_id, expires_at).await?; + Some(serde_json::ser::to_string_pretty(&credential)?.to_string()) } CreateObject::Validation { method, @@ -121,7 +123,7 @@ async fn create(secd: &Secd, cmd: CreateObject) -> Result<Option<String>> { ValidationMethod::Email { address } => { let validation = secd.validate_email(&address, identity_id).await?; - Some(serde_json::ser::to_string(&validation)?.to_string()) + Some(serde_json::ser::to_string_pretty(&validation)?.to_string()) } _ => unimplemented!(), }, @@ -136,7 +138,7 @@ async fn create(secd: &Secd, cmd: CreateObject) -> Result<Option<String>> { let session = secd .complete_address_validation(&validation_id, token, code) .await?; - Some(serde_json::ser::to_string(&session)?.to_string()) + Some(serde_json::ser::to_string_pretty(&session)?.to_string()) } }) } @@ -149,44 +151,31 @@ async fn dev(cmd: DevObject) -> Result<Option<String>> { async fn get(secd: &Secd, cmd: GetObject) -> Result<Option<String>> { 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 + + credential, + } => { + let t = credential.map(|cred| match cred { + ValidateObject::ApiToken { token } => { + CredentialType::api_token_from_str(&token).expect("failed to build api token") + } + ValidateObject::Passphrase { + username, + passphrase, + } => CredentialType::Passphrase { + key: username, + value: passphrase, + }, + ValidateObject::Session { token } => { + CredentialType::session_from_str(&token).expect("failed to build session") + } + }); + + Some( + serde_json::ser::to_string_pretty(&secd.get_identity(identity_id, t).await?)? + .to_string(), + ) } }) } @@ -202,5 +191,37 @@ async fn update(secd: &Secd, cmd: UpdateObject) -> Result<Option<String>> { Some(serde_json::to_string(&identity)?.to_string()) } + UpdateObject::Credential { id, revoke } => { + if revoke { + secd.revoke_credential(id).await?; + } + + Some("Ok".to_string()) + } }) } + +async fn validate(secd: &Secd, cmd: ValidateObject) -> Result<Option<String>> { + let credential = match cmd { + ValidateObject::ApiToken { token } => { + secd.validate_credential(CredentialType::api_token_from_str(&token)?) + .await? + } + ValidateObject::Passphrase { + username, + passphrase, + } => { + secd.validate_credential(CredentialType::Passphrase { + key: username, + value: passphrase, + }) + .await? + } + ValidateObject::Session { token } => { + secd.validate_credential(CredentialType::session_from_str(&token)?) + .await? + } + }; + + Ok(Some(serde_json::to_string_pretty(&credential)?.to_string())) +} |
