aboutsummaryrefslogtreecommitdiff
path: root/crates/iam/src/command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/iam/src/command.rs')
-rw-r--r--crates/iam/src/command.rs164
1 files changed, 164 insertions, 0 deletions
diff --git a/crates/iam/src/command.rs b/crates/iam/src/command.rs
new file mode 100644
index 0000000..e9e0f23
--- /dev/null
+++ b/crates/iam/src/command.rs
@@ -0,0 +1,164 @@
+use crate::{
+ api,
+ util::{self, get_config_profile, Result},
+ CONFIG_LOGIN_TEMPLATE, CONFIG_SIGNUP_TEMPLATE,
+};
+use async_std::fs;
+use colored::*;
+use rand::distributions::{Alphanumeric, DistString};
+use secd::{AuthEmail, AuthStore};
+use std::{
+ fs::File,
+ io::{self, stdin, stdout, Write},
+ str::FromStr,
+};
+use strum::VariantNames;
+
+const DEFAULT_LOGIN_EMAIL: &str = "<!doctype html><html><body><p>You requested a login link for %secd_email_address%. Please click the following link<br/><br/>http://localhost:5500/myapp/iam/exchange/%secd_link%<br/><br/>or use code: %secd_code%</p></body></html>";
+const DEFAULT_SIGNUP_EMAIL: &str = "<!doctype html><html><body><h1>Welcome to SecD IAM</h1></h1><p>If you did not request this sign up, you can safely ignore this email. Otherwise, please click the following link to validate your account<br/><br/>http://localhost:5500/myapp/iam/exchange/%secd_link%<br/><br/>or use code: %secd_code%</p></body></html>";
+
+pub async fn admin_init(is_interactive: bool) -> Result<()> {
+ let config_dir = util::get_config_dir();
+ let config_profile = get_config_profile();
+ fs::create_dir_all(config_dir.clone()).await?;
+
+ if config_profile.try_exists()? {
+ writeln!(
+ io::stdout(),
+ "{} {}",
+ config_profile.clone().display(),
+ "already exists and there is nothing to initialize. To create a new IAM store use `iam admin create store` or modify the configuration profile directly"
+ .yellow()
+ )?;
+ } else {
+ writeln!(stdout(), "{}", "creating default profile".green())?;
+
+ let mut login_template = config_dir.clone();
+ login_template.push(CONFIG_LOGIN_TEMPLATE);
+ let mut f = File::create(login_template.clone())?;
+ f.write_all(DEFAULT_LOGIN_EMAIL.as_bytes())?;
+
+ let mut signup_template = config_dir.clone();
+ signup_template.push(CONFIG_SIGNUP_TEMPLATE);
+ f = File::create(signup_template.clone())?;
+ f.write_all(DEFAULT_SIGNUP_EMAIL.as_bytes())?;
+
+ let mut cfg = api::Config {
+ profile: vec![api::ConfigProfile {
+ name: "default".to_string(),
+ store: AuthStore::Sqlite,
+ 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,
+ email_template_login: Some(login_template.display().to_string()),
+ email_template_signup: Some(signup_template.display().to_string()),
+ }],
+ };
+
+ let mut input: String = String::new();
+ if is_interactive {
+ writeln!(stdout(), "{}", "For a complete overview of configuration options, cancel the initialization and explore `iam help`")?;
+ write!(stdout(), "Would you like to create a default local store with local stubs for external services?[(y)es/(n)o]: ")?;
+ stdout().flush()?;
+ 'outer: loop {
+ input.clear();
+ stdin().read_line(&mut input)?;
+ match input.as_str().trim() {
+ "Y" | "y" | "Yes" | "yes" => break,
+ "N" | "n" | "No" | "no" => {
+ loop {
+ write!(
+ stdout(),
+ "Persistence store {:?}: ",
+ AuthStore::VARIANTS
+ .iter()
+ .map(|s| s.to_lowercase())
+ .collect::<Vec<String>>()
+ )?;
+ stdout().flush()?;
+ input.clear();
+ stdin().read_line(&mut input)?;
+ match AuthStore::from_str(&input.trim()) {
+ Ok(s) => {
+ cfg.profile[0].store = s;
+ break;
+ }
+ Err(_) => {
+ writeln!(stdout(), "{}", "Invalid store type".red())?;
+ }
+ }
+ }
+
+ write!(stdout(), "Store connection string: ")?;
+ stdout().flush()?;
+ input.clear();
+ stdin().read_line(&mut input)?;
+ cfg.profile[0].store_conn = input.trim().to_string().clone();
+
+ loop {
+ write!(
+ stdout(),
+ "Email provider {:?}: ",
+ AuthEmail::VARIANTS
+ .iter()
+ .map(|s| s.to_lowercase())
+ .collect::<Vec<String>>()
+ )?;
+ stdout().flush()?;
+ input.clear();
+ stdin().read_line(&mut input)?;
+ match AuthEmail::from_str(&input.trim()) {
+ Ok(s) => {
+ cfg.profile[0].emailer = s;
+ break;
+ }
+ Err(_) => {
+ writeln!(stdout(), "{}", "Invalid email provider".red())?;
+ }
+ }
+ }
+
+ write!(
+ stdout(),
+ "Email template for login validation:[FilePath or Enter for default]: "
+ )?;
+ stdout().flush()?;
+ input.clear();
+ stdin().read_line(&mut input)?;
+ cfg.profile[0].email_template_login =
+ Some(input.trim().to_string().clone());
+
+ write!(
+ stdout(),
+ "Email template for signup validation:[FilePath or Enter for default]: "
+ )?;
+ stdout().flush()?;
+ input.clear();
+ stdin().read_line(&mut input)?;
+ cfg.profile[0].email_template_login =
+ Some(input.trim().to_string().clone());
+
+ break 'outer;
+ }
+ _ => {}
+ }
+ }
+ }
+
+ let mut f = File::create(config_profile.clone())?;
+ f.write_all(toml::to_string(&cfg)?.as_bytes())?;
+ writeln!(
+ stdout(),
+ "{} {} {} {} {}",
+ "created iam config".green(),
+ "default",
+ "at".green(),
+ config_dir.display().to_string(),
+ "to hold secD iam configurations".green()
+ )?;
+ }
+ Ok(())
+}