aboutsummaryrefslogtreecommitdiff
path: root/crates/iam/src
diff options
context:
space:
mode:
authorbenj <benj@rse8.com>2023-05-22 15:47:06 -0700
committerbenj <benj@rse8.com>2023-05-22 15:47:06 -0700
commited34a5251f13bbded0aa15719887db4924b351eb (patch)
tree9719d805e915f4483d5db3e5e612e8b4cf5c702c /crates/iam/src
parenteb92f823c31a5e702af7005231f0d6915aad3342 (diff)
downloadsecdiam-ed34a5251f13bbded0aa15719887db4924b351eb.tar
secdiam-ed34a5251f13bbded0aa15719887db4924b351eb.tar.gz
secdiam-ed34a5251f13bbded0aa15719887db4924b351eb.tar.bz2
secdiam-ed34a5251f13bbded0aa15719887db4924b351eb.tar.lz
secdiam-ed34a5251f13bbded0aa15719887db4924b351eb.tar.xz
secdiam-ed34a5251f13bbded0aa15719887db4924b351eb.tar.zst
secdiam-ed34a5251f13bbded0aa15719887db4924b351eb.zip
update credential API to include sessions
This change updates the credential API to include sessions as just another credential type. It adds the ApiToken type and enables revocation of credentials. Updates were also made to the Identity API which now includes a list of new credentials added to an Identity. This change also migrates off the hacky ENV configuration paradigm and includes a new config.toml file specified by the SECD_CONFIG_PATH env var. No default is currently provided. Clippy updates and code cleanup.
Diffstat (limited to 'crates/iam/src')
-rw-r--r--crates/iam/src/api.rs98
-rw-r--r--crates/iam/src/main.rs143
2 files changed, 127 insertions, 114 deletions
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()))
+}