aboutsummaryrefslogtreecommitdiff
path: root/crates/iam/src
diff options
context:
space:
mode:
authorbenj <benj@rse8.com>2022-12-24 00:43:38 -0800
committerbenj <benj@rse8.com>2022-12-24 00:43:38 -0800
commitc2268c285648ef02ece04de0d9df0813c6d70ff8 (patch)
treef84ec7ee42f97d78245f26d0c5a0c559cd35e89d /crates/iam/src
parentde6339da72af1d61ca5908b780977e2b037ce014 (diff)
downloadsecdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.gz
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.bz2
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.lz
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.xz
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.zst
secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.zip
refactor everything with more abstraction and a nicer interface
Diffstat (limited to '')
-rw-r--r--crates/iam/src/api.rs43
-rw-r--r--crates/iam/src/command.rs10
-rw-r--r--crates/iam/src/main.rs114
3 files changed, 65 insertions, 102 deletions
diff --git a/crates/iam/src/api.rs b/crates/iam/src/api.rs
index 841aa9e..ace3199 100644
--- a/crates/iam/src/api.rs
+++ b/crates/iam/src/api.rs
@@ -1,7 +1,7 @@
use crate::ISSUE_TRACKER_LOC;
use clap::{Parser, Subcommand, ValueEnum};
use colored::*;
-use secd::{IdentityId, OauthProviderName};
+use secd::IdentityId;
use serde::{Deserialize, Serialize};
use thiserror;
use url::Url;
@@ -183,7 +183,6 @@ pub enum AdminObject {
},
/// A selected Oauth2.0 provider capable of authenticating identities
Oauth2Provider {
- provider: OauthProviderName,
client_id: String,
secret: String,
redirect_url: Url,
@@ -310,7 +309,7 @@ pub enum CreateObject {
secret_code: String,
},
#[command(
- about = "An action which initiates an identity validation",
+ about = "An action which initiates an address validation",
long_about = "Validation\n\nA validation requires that the identity authenticate in some way, either by providing IAM managed credentials, an external gated mechanism (e.g. email, phone, or hardware key), or through a secondary authentication provider (oauth, saml, ldap, kerberos)."
)]
Validation {
@@ -319,7 +318,21 @@ pub enum CreateObject {
method: ValidationMethod,
/// The identity against which to associate this validation. A new identity will be created if no identity is provided.
#[arg(long, short)]
- identity: Option<Uuid>,
+ identity_id: Option<Uuid>,
+ },
+ #[command(
+ about = "An action which completes an address validation",
+ long_about = "Validation Completion\n\nA validation completion depends on an existing address validation, which is validated based on the provided validation id and secret token or secret code"
+ )]
+ ValidationCompletion {
+ /// The validation id against which to complete the validation.
+ validation_id: Uuid,
+ /// The secret token for the validation. A token or code must be provided.
+ #[arg(long, short)]
+ token: Option<String>,
+ /// The secret code for the validation. A code or token must be provided.
+ #[arg(long, short)]
+ code: Option<String>,
},
}
@@ -343,26 +356,12 @@ pub enum ValidationMethod {
/// Email address which will receive the validation
address: String,
},
- /// A hardware security key to associate with an identity
- HardwareKey,
- /// A kerberos ticket to associated with an identity
- Kerberos,
- /// An oauth2 provider to authenticate (and authorize) an identity
- Oauth2 {
- provider: OauthProviderName,
- /// An optional scope to use for authorization
- scope: Option<String>,
- /// An optional existing identity to link to this validation request
- identity: Option<IdentityId>,
- },
- /// A phone which an identity may authenticate via SMS or voice
+ /// A phone which an identity may authenticate via SMS or Voice
Phone {
/// Whether to use a voice code. Otherwise, uses SMS
#[arg(long, short, action)]
use_voice: bool,
},
- /// A saml provider to authenticate an identity
- Saml,
}
#[derive(Subcommand)]
@@ -379,8 +378,8 @@ pub enum GetObject {
id: Option<Uuid>,
},
Identity {
- /// Unique identity id
- id: Uuid,
+ /// Any session corresponding to this identity.
+ session_token: String,
},
Permission {
/// Unique permission name
@@ -497,7 +496,7 @@ pub struct ConfigProfile {
pub name: String,
pub store: secd::AuthStore,
pub store_conn: String,
- pub emailer: secd::AuthEmail,
+ pub emailer: secd::AuthEmailMessenger,
pub email_template_login: Option<String>,
pub email_template_signup: Option<String>,
}
diff --git a/crates/iam/src/command.rs b/crates/iam/src/command.rs
index 379e7fb..56734b1 100644
--- a/crates/iam/src/command.rs
+++ b/crates/iam/src/command.rs
@@ -5,7 +5,7 @@ use crate::{
};
use colored::*;
use rand::distributions::{Alphanumeric, DistString};
-use secd::{AuthEmail, AuthStore};
+use secd::{AuthEmailMessenger, AuthStore};
use std::{
fs::{self, File},
io::{self, stdin, stdout, Read, Write},
@@ -48,13 +48,13 @@ pub async fn admin_init(is_interactive: bool) -> Result<()> {
let mut cfg = api::Config {
profile: vec![api::ConfigProfile {
name: "default".to_string(),
- store: AuthStore::Sqlite,
+ store: AuthStore::Sqlite { conn: "".into() },
store_conn: format!(
"sqlite://{}/{}.sql?mode=rwc",
config_dir.clone().display().to_string(),
Alphanumeric.sample_string(&mut rand::thread_rng(), 5),
),
- emailer: secd::AuthEmail::LocalStub,
+ emailer: secd::AuthEmailMessenger::Local,
email_template_login: Some(login_template.display().to_string()),
email_template_signup: Some(signup_template.display().to_string()),
}],
@@ -104,7 +104,7 @@ pub async fn admin_init(is_interactive: bool) -> Result<()> {
write!(
stdout(),
"Email provider {:?}: ",
- AuthEmail::VARIANTS
+ AuthEmailMessenger::VARIANTS
.iter()
.map(|s| s.to_lowercase())
.collect::<Vec<String>>()
@@ -112,7 +112,7 @@ pub async fn admin_init(is_interactive: bool) -> Result<()> {
stdout().flush()?;
input.clear();
stdin().read_line(&mut input)?;
- match AuthEmail::from_str(&input.trim()) {
+ match AuthEmailMessenger::from_str(&input.trim()) {
Ok(s) => {
cfg.profile[0].emailer = s;
break;
diff --git a/crates/iam/src/main.rs b/crates/iam/src/main.rs
index 4f6316a..ce72072 100644
--- a/crates/iam/src/main.rs
+++ b/crates/iam/src/main.rs
@@ -10,7 +10,8 @@ use api::{
use clap::Parser;
use command::dev_oauth2_listen;
use env_logger::Env;
-use secd::{Secd, SecdError};
+use secd::{Secd, SecdError, ENV_AUTH_STORE_CONN_STRING};
+use std::str::FromStr;
use util::{error_detail, Result};
use uuid::Uuid;
@@ -49,16 +50,15 @@ async fn exec() -> Result<Option<String>> {
}
rest @ _ => {
- let cfg = util::read_config(args.profile).map_err(|_| CliError::InvalidProfile)?;
- let secd = Secd::init(
- cfg.store,
- Some(&cfg.store_conn),
- cfg.emailer,
- cfg.email_template_login,
- cfg.email_template_signup,
- )
- .await
- .map_err(|e| CliError::SecdInitializationFailure(e.to_string()))?;
+ // let cfg = util::read_config(args.profile).map_err(|_| CliError::InvalidProfile)?;
+ std::env::set_var(
+ ENV_AUTH_STORE_CONN_STRING,
+ "sqlite:///tmp/store.db?mode=rwc",
+ // "postgresql://secduser:p4ssw0rd@localhost:5412/secd",
+ );
+ let secd = Secd::init()
+ .await
+ .map_err(|e| CliError::SecdInitializationFailure(e.to_string()))?;
match rest {
Command::Admin { action } => admin(&secd, action).await?,
@@ -69,13 +69,13 @@ async fn exec() -> Result<Option<String>> {
"4a696b66-6231-4a2f-811c-4448a41473d2",
"Code path should be unreachable",
))),
- Command::Link { object, unlink } => link(&secd, object, unlink).await?,
+ Command::Link { object, unlink } => todo!(),
Command::Ls {
object,
name,
before,
after,
- } => list(&secd, object, name, before, after).await?,
+ } => todo!(),
Command::Repl => {
unimplemented!()
}
@@ -90,19 +90,7 @@ async fn admin(secd: &Secd, cmd: AdminAction) -> Result<Option<String>> {
println!("do backend stuff!");
None
}
- AdminAction::Create { object } => match object {
- AdminObject::Oauth2Provider {
- provider,
- client_id,
- secret,
- redirect_url,
- } => {
- secd.create_oauth_provider(&provider, client_id, secret, redirect_url)
- .await?;
- None
- }
- rest @ _ => unimplemented!(),
- },
+ AdminAction::Create { object } => todo!(),
AdminAction::Seal => {
println!("do seal");
None
@@ -148,54 +136,31 @@ async fn create(secd: &Secd, cmd: CreateObject) -> Result<Option<String>> {
CreateObject::Session {
validation_id,
secret_code,
- } => {
- let session = secd
- .exchange_code_for_session(validation_id, secret_code)
- .await
- .map_err(|e| match e {
- SecdError::InvalidCode => CliError::InvalidCode,
- _ => CliError::InternalError(error_detail(
- "17e5c226-5d7d-44a2-b3b5-be3ee958c252",
- "An unknown error while exchanging a session",
- )),
- })?;
- serde_json::to_string(&session).ok()
- }
- CreateObject::Validation { method, identity } => match method {
- ValidationMethod::Email { address } => serde_json::to_string(&Validation {
- validation_id: secd.create_validation_request_email(&address).await?,
- note: Some("<secret code> sent to client".into()),
- oauth_auth_url: None,
- })
- .ok(),
+ } => todo!(),
+ CreateObject::Validation {
+ method,
+ identity_id,
+ } => match method {
+ ValidationMethod::Email { address } => {
+ let validation = secd.validate_email(&address, identity_id).await?;
- ValidationMethod::Oauth2 {
- provider,
- scope,
- identity,
- } => {
- let redirect = secd
- .create_validation_request_oauth(&provider, scope)
- .await?
- .to_string();
- let validation_id = redirect
- .split("state=")
- .collect::<Vec<&str>>()
- .last()
- .map(|i| Uuid::parse_str(i).ok())
- .flatten()
- .unwrap();
- serde_json::to_string(&Validation {
- validation_id,
- note: Some(
- "<secret code> is retrieved by completing oauth flow in the browser".into(),
- ),
- oauth_auth_url: Some(redirect),
- })
- .ok()
+ 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())
+ }
})
}
@@ -215,10 +180,10 @@ async fn get(secd: &Secd, cmd: GetObject) -> Result<Option<String>> {
println!("get object group");
None
}
- GetObject::Identity { id } => {
- println!("get object identity");
- None
+ GetObject::Identity { session_token } => {
+ Some(serde_json::ser::to_string(&secd.get_identity(&session_token).await?)?.to_string())
}
+
GetObject::Permission { name, id } => {
println!("get object permission");
None
@@ -236,8 +201,7 @@ async fn get(secd: &Secd, cmd: GetObject) -> Result<Option<String>> {
None
}
GetObject::Session { secret } => {
- println!("get object session");
- None
+ Some(serde_json::ser::to_string(&secd.get_session(&secret).await?)?.to_string())
}
GetObject::Validation { id } => {
println!("get object validation");