Update to account recovery UX (#859)

* JSON-formatted output for recover_account, moved a bunch of logs to debug instead of info
* updated documentation
This commit is contained in:
James Hodgkinson 2022-06-26 18:02:16 +10:00 committed by GitHub
parent 4b1989ee22
commit 57f8fa9d2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 152 additions and 48 deletions

View file

@ -148,7 +148,7 @@ in `/tmp/kanidm.db`.
Create the initial database and generate an `admin` username: Create the initial database and generate an `admin` username:
cargo run --bin kanidmd recover_account -c ./examples/insecure_server.toml -n admin cargo run --bin kanidmd recover_account -c ./examples/insecure_server.toml admin
<snip> <snip>
Success - password reset to -> Et8QRJgQkMJu3v1AQxcbxRWW44qRUZPpr6BJ9fCGapAB9cT4 Success - password reset to -> Et8QRJgQkMJu3v1AQxcbxRWW44qRUZPpr6BJ9fCGapAB9cT4

View file

@ -119,7 +119,7 @@ You should test your configuration is valid before you proceed.
Then you can setup the initial admin account and initialise the database into your volume. Then you can setup the initial admin account and initialise the database into your volume.
docker run --rm -i -t -v kanidmd:/data \ docker run --rm -i -t -v kanidmd:/data \
kanidm/server:latest /sbin/kanidmd recover_account -c /data/server.toml -n admin kanidm/server:latest /sbin/kanidmd recover_account -c /data/server.toml admin
### Run the Server ### Run the Server

View file

@ -15,7 +15,7 @@ tracing = "^0.1.35"
reqwest = { version = "^0.11.11", features=["cookies", "json", "native-tls"] } reqwest = { version = "^0.11.11", features=["cookies", "json", "native-tls"] }
kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" } kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" }
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.80" serde_json = "^1.0.81"
toml = "^0.5.9" toml = "^0.5.9"
uuid = { version = "^1.1.2", features = ["serde", "v4"] } uuid = { version = "^1.1.2", features = ["serde", "v4"] }
url = { version = "^2.2.2", features = ["serde"] } url = { version = "^2.2.2", features = ["serde"] }

View file

@ -12,7 +12,7 @@ repository = "https://github.com/kanidm/kanidm/"
[dependencies] [dependencies]
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.80" serde_json = "^1.0.81"
uuid = { version = "^1.1.2", features = ["serde"] } uuid = { version = "^1.1.2", features = ["serde"] }
base32 = "^0.4.0" base32 = "^0.4.0"
webauthn-rs = { version = "^0.3.2", default-features = false, features = ["wasm"] } webauthn-rs = { version = "^0.3.2", default-features = false, features = ["wasm"] }

View file

@ -37,7 +37,7 @@ rpassword = "^6.0.1"
clap = { version = "^3.2", features = ["derive", "env"] } clap = { version = "^3.2", features = ["derive", "env"] }
libc = "^0.2.126" libc = "^0.2.126"
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.80" serde_json = "^1.0.81"
shellexpand = "^2.1.0" shellexpand = "^2.1.0"
rayon = "^1.5.3" rayon = "^1.5.3"
time = { version = "=0.2.27", features = ["serde", "std"] } time = { version = "=0.2.27", features = ["serde", "std"] }

View file

@ -60,7 +60,7 @@ bytes = "^1.1.0"
libc = "^0.2.126" libc = "^0.2.126"
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.80" serde_json = "^1.0.81"
clap = { version = "^3.2", features = ["derive"] } clap = { version = "^3.2", features = ["derive"] }
libsqlite3-sys = "0.24.2" libsqlite3-sys = "0.24.2"

View file

@ -28,7 +28,7 @@ use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use kanidm::audit::LogLevel; use kanidm::audit::LogLevel;
use kanidm::config::{Configuration, OnlineBackup, ServerRole}; use kanidm::config::{Configuration, ConsoleOutputMode, OnlineBackup, ServerRole};
use kanidm::tracing_tree; use kanidm::tracing_tree;
use kanidm::utils::file_permissions_readonly; use kanidm::utils::file_permissions_readonly;
use score::{ use score::{
@ -221,6 +221,9 @@ async fn main() {
config.update_domain(&sconfig.domain.as_str()); config.update_domain(&sconfig.domain.as_str());
config.update_db_arc_size(sconfig.db_arc_size); config.update_db_arc_size(sconfig.db_arc_size);
config.update_role(sconfig.role); config.update_role(sconfig.role);
config.update_output_mode(
ConsoleOutputMode::from_str(opt.commands.commonopt().output_mode.as_str()).unwrap(),
);
// Apply any cli overrides, normally debug level. // Apply any cli overrides, normally debug level.
if let Some(dll) = opt.commands.commonopt().debug.as_ref() { if let Some(dll) = opt.commands.commonopt().debug.as_ref() {

View file

@ -6,6 +6,10 @@ struct CommonOpt {
#[clap(parse(from_os_str), short, long = "config", env = "KANIDM_CONFIG")] #[clap(parse(from_os_str), short, long = "config", env = "KANIDM_CONFIG")]
/// Path to the server's configuration file. If it does not exist, it will be created. /// Path to the server's configuration file. If it does not exist, it will be created.
config_path: PathBuf, config_path: PathBuf,
//TODO: remove this once we work out the format
/// Log format (still in very early development)
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value="text")]
output_mode: String,
} }
#[derive(Debug, Args)] #[derive(Debug, Args)]
@ -28,7 +32,7 @@ struct RestoreOpt {
#[derive(Debug, Args)] #[derive(Debug, Args)]
struct RecoverAccountOpt { struct RecoverAccountOpt {
#[clap(short)] #[clap(value_parser)]
/// The account name to recover credentials for. /// The account name to recover credentials for.
name: String, name: String,
#[clap(flatten)] #[clap(flatten)]

View file

@ -48,7 +48,7 @@ uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
compiled-uuid = "0.1.2" compiled-uuid = "0.1.2"
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_cbor = "^0.11.2" serde_cbor = "^0.11.2"
serde_json = "^1.0.80" serde_json = "^1.0.81"
libsqlite3-sys = "0.24.2" libsqlite3-sys = "0.24.2"
rusqlite = "^0.27.0" rusqlite = "^0.27.0"

View file

@ -1255,7 +1255,7 @@ impl<'a> BackendWriteTransaction<'a> {
pub fn upgrade_reindex(&self, v: i64) -> Result<(), OperationError> { pub fn upgrade_reindex(&self, v: i64) -> Result<(), OperationError> {
let dbv = self.get_db_index_version(); let dbv = self.get_db_index_version();
admin_info!(?dbv, ?v, "upgrade_reindex"); admin_debug!(?dbv, ?v, "upgrade_reindex");
if dbv < v { if dbv < v {
limmediate_warning!( limmediate_warning!(
"NOTICE: A system reindex is required. This may take a long time ...\n" "NOTICE: A system reindex is required. This may take a long time ...\n"
@ -1514,9 +1514,9 @@ impl Backend {
idxkeys: Vec<IdxKey>, idxkeys: Vec<IdxKey>,
vacuum: bool, vacuum: bool,
) -> Result<Self, OperationError> { ) -> Result<Self, OperationError> {
info!("DB tickets -> {:?}", cfg.pool_size); debug!("DB tickets -> {:?}", cfg.pool_size);
info!("Profile -> {}", env!("KANIDM_PROFILE_NAME")); debug!("Profile -> {}", env!("KANIDM_PROFILE_NAME"));
info!("CPU Flags -> {}", env!("KANIDM_CPU_FLAGS")); debug!("CPU Flags -> {}", env!("KANIDM_CPU_FLAGS"));
// If in memory, reduce pool to 1 // If in memory, reduce pool to 1
if cfg.path.is_empty() { if cfg.path.is_empty() {

View file

@ -74,6 +74,31 @@ impl FromStr for ServerRole {
} }
} }
// TODO: this should probably be in the kanidm crate
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum ConsoleOutputMode {
Text,
JSON,
}
impl Default for ConsoleOutputMode {
fn default() -> Self {
ConsoleOutputMode::Text
}
}
impl FromStr for ConsoleOutputMode {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"json" => Ok(ConsoleOutputMode::JSON),
"text" => Ok(ConsoleOutputMode::Text),
_ => Err("Must be one of json, text"),
}
}
}
#[derive(Serialize, Deserialize, Debug, Default)] #[derive(Serialize, Deserialize, Debug, Default)]
pub struct Configuration { pub struct Configuration {
pub address: String, pub address: String,
@ -93,6 +118,7 @@ pub struct Configuration {
pub domain: String, pub domain: String,
pub origin: String, pub origin: String,
pub role: ServerRole, pub role: ServerRole,
pub output_mode: ConsoleOutputMode,
} }
impl fmt::Display for Configuration { impl fmt::Display for Configuration {
@ -112,21 +138,23 @@ impl fmt::Display for Configuration {
.and_then(|_| write!(f, "secure cookies: {}, ", self.secure_cookies)) .and_then(|_| write!(f, "secure cookies: {}, ", self.secure_cookies))
.and_then(|_| write!(f, "with TLS: {}, ", self.tls_config.is_some())) .and_then(|_| write!(f, "with TLS: {}, ", self.tls_config.is_some()))
.and_then(|_| match self.log_level { .and_then(|_| match self.log_level {
Some(u) => write!(f, "with log_level: {:x}, ", u), Some(u) => write!(f, "log_level: {:x}, ", u),
None => write!(f, "with log_level: default, "), None => write!(f, "log_level: default, "),
}) })
// TODO: include the backup timings
.and_then(|_| match &self.online_backup { .and_then(|_| match &self.online_backup {
Some(_) => write!(f, "with online_backup: enabled, "), Some(_) => write!(f, "online_backup: enabled, "),
None => write!(f, "with online_backup: disabled, "), None => write!(f, "online_backup: disabled, "),
}) })
.and_then(|_| write!(f, "role: {}, ", self.role.to_string())) .and_then(|_| write!(f, "role: {}, ", self.role.to_string()))
.and_then(|_| { .and_then(|_| {
write!( write!(
f, f,
"integration mode: {}", "integration mode: {}, ",
self.integration_test_config.is_some() self.integration_test_config.is_some()
) )
}) })
.and_then(|_| write!(f, "console output format: {:?} ", self.output_mode))
} }
} }
@ -144,7 +172,7 @@ impl Configuration {
db_path: String::from(""), db_path: String::from(""),
db_fs_type: None, db_fs_type: None,
db_arc_size: None, db_arc_size: None,
maximum_request: 262_144, // 256k maximum_request: 256 * 1024, // 256k
// log type // log type
// log path // log path
// TODO #63: default true in prd // TODO #63: default true in prd
@ -157,6 +185,7 @@ impl Configuration {
domain: "idm.example.com".to_string(), domain: "idm.example.com".to_string(),
origin: "https://idm.example.com".to_string(), origin: "https://idm.example.com".to_string(),
role: ServerRole::WriteReplica, role: ServerRole::WriteReplica,
output_mode: ConsoleOutputMode::default(),
}; };
let mut rng = StdRng::from_entropy(); let mut rng = StdRng::from_entropy();
rng.fill(&mut c.cookie_key); rng.fill(&mut c.cookie_key);
@ -218,6 +247,11 @@ impl Configuration {
self.role = r; self.role = r;
} }
/// Sets the output mode for writing to the console
pub fn update_output_mode(&mut self, om: ConsoleOutputMode) {
self.output_mode = om;
}
pub fn update_tls(&mut self, chain: &Option<String>, key: &Option<String>) { pub fn update_tls(&mut self, chain: &Option<String>, key: &Option<String>) {
match (chain, key) { match (chain, key) {
(None, None) => {} (None, None) => {}

View file

@ -93,9 +93,9 @@ pub mod prelude {
ValueSetUuid, ValueSetUuid,
}; };
pub use crate::{ pub use crate::{
admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace, filter_warn, admin_debug, admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace,
perf_trace, request_error, request_info, request_trace, request_warn, security_access, filter_warn, perf_trace, request_error, request_info, request_trace, request_warn,
security_critical, security_error, security_info, spanned, security_access, security_critical, security_error, security_info, spanned,
}; };
} }

View file

@ -1354,10 +1354,10 @@ impl<'a> SchemaWriteTransaction<'a> {
let r = self.validate(); let r = self.validate();
if r.is_empty() { if r.is_empty() {
admin_info!("schema validate -> passed"); admin_debug!("schema validate -> passed");
Ok(()) Ok(())
} else { } else {
admin_info!(err = ?r, "schema validate -> errors"); admin_error!(err = ?r, "schema validate -> errors");
Err(OperationError::ConsistencyError(r)) Err(OperationError::ConsistencyError(r))
} }
}) })

View file

@ -1094,7 +1094,7 @@ impl QueryServer {
Err(OperationError::NoMatchingEntries) => Ok(0), Err(OperationError::NoMatchingEntries) => Ok(0),
Err(r) => Err(r), Err(r) => Err(r),
}?; }?;
admin_info!(?system_info_version); admin_debug!(?system_info_version);
if system_info_version < 3 { if system_info_version < 3 {
migrate_txn.migrate_2_to_3()?; migrate_txn.migrate_2_to_3()?;
@ -1119,8 +1119,8 @@ impl QueryServer {
ts_write_3 ts_write_3
.initialise_idm() .initialise_idm()
.and_then(|_| ts_write_3.commit())?; .and_then(|_| ts_write_3.commit())?;
// TODO: work out if we've actually done any migrations before printing this
admin_info!("migrations success! ☀️ "); admin_debug!("Database version check and migrations success! ☀️ ");
Ok(()) Ok(())
} }
@ -2267,7 +2267,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
*/ */
pub fn initialise_schema_core(&self) -> Result<(), OperationError> { pub fn initialise_schema_core(&self) -> Result<(), OperationError> {
admin_info!("initialise_schema_core -> start ..."); admin_debug!("initialise_schema_core -> start ...");
// Load in all the "core" schema, that we already have in "memory". // Load in all the "core" schema, that we already have in "memory".
let entries = self.schema.to_entries(); let entries = self.schema.to_entries();
@ -2277,9 +2277,9 @@ impl<'a> QueryServerWriteTransaction<'a> {
self.internal_migrate_or_create(e) self.internal_migrate_or_create(e)
}); });
if r.is_ok() { if r.is_ok() {
admin_info!("initialise_schema_core -> Ok!"); admin_debug!("initialise_schema_core -> Ok!");
} else { } else {
admin_info!(?r, "initialise_schema_core -> Error"); admin_error!(?r, "initialise_schema_core -> Error");
} }
// why do we have error handling if it's always supposed to be `Ok`? // why do we have error handling if it's always supposed to be `Ok`?
debug_assert!(r.is_ok()); debug_assert!(r.is_ok());
@ -2287,7 +2287,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
} }
pub fn initialise_schema_idm(&self) -> Result<(), OperationError> { pub fn initialise_schema_idm(&self) -> Result<(), OperationError> {
admin_info!("initialise_schema_idm -> start ..."); admin_debug!("initialise_schema_idm -> start ...");
// List of IDM schemas to init. // List of IDM schemas to init.
let idm_schema: Vec<&str> = vec![ let idm_schema: Vec<&str> = vec![
JSON_SCHEMA_ATTR_DISPLAYNAME, JSON_SCHEMA_ATTR_DISPLAYNAME,
@ -2338,7 +2338,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
.try_for_each(|e_str| self.internal_migrate_or_create_str(e_str)); .try_for_each(|e_str| self.internal_migrate_or_create_str(e_str));
if r.is_ok() { if r.is_ok() {
admin_info!("initialise_schema_idm -> Ok!"); admin_debug!("initialise_schema_idm -> Ok!");
} else { } else {
admin_error!(res = ?r, "initialise_schema_idm -> Error"); admin_error!(res = ?r, "initialise_schema_idm -> Error");
} }
@ -2467,7 +2467,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
.iter() .iter()
.try_for_each(|e_str| self.internal_migrate_or_create_str(e_str)); .try_for_each(|e_str| self.internal_migrate_or_create_str(e_str));
if res.is_ok() { if res.is_ok() {
admin_info!("initialise_idm -> result Ok!"); admin_debug!("initialise_idm -> result Ok!");
} else { } else {
admin_error!(?res, "initialise_idm p3 -> result"); admin_error!(?res, "initialise_idm p3 -> result");
} }

View file

@ -3,6 +3,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
#[derive(Debug, Clone, Copy, IntoPrimitive, TryFromPrimitive)] #[derive(Debug, Clone, Copy, IntoPrimitive, TryFromPrimitive)]
#[repr(u64)] #[repr(u64)]
pub enum EventTag { pub enum EventTag {
AdminDebug,
AdminError, AdminError,
AdminWarn, AdminWarn,
AdminInfo, AdminInfo,
@ -24,6 +25,7 @@ pub enum EventTag {
impl EventTag { impl EventTag {
pub fn pretty(self) -> &'static str { pub fn pretty(self) -> &'static str {
match self { match self {
EventTag::AdminDebug => "admin.debug",
EventTag::AdminError => "admin.error", EventTag::AdminError => "admin.error",
EventTag::AdminWarn => "admin.warn", EventTag::AdminWarn => "admin.warn",
EventTag::AdminInfo => "admin.info", EventTag::AdminInfo => "admin.info",
@ -46,8 +48,9 @@ impl EventTag {
pub fn emoji(self) -> &'static str { pub fn emoji(self) -> &'static str {
use EventTag::*; use EventTag::*;
match self { match self {
AdminDebug => "🐛",
AdminError | FilterError | RequestError | SecurityError => "🚨", AdminError | FilterError | RequestError | SecurityError => "🚨",
AdminWarn | FilterWarn | RequestWarn => "⚠️ ", AdminWarn | FilterWarn | RequestWarn => "⚠️",
AdminInfo | FilterInfo | RequestInfo | SecurityInfo => " ", AdminInfo | FilterInfo | RequestInfo | SecurityInfo => " ",
RequestTrace | FilterTrace | PerfTrace => "📍", RequestTrace | FilterTrace | PerfTrace => "📍",
SecurityCritical => "🔐", SecurityCritical => "🔐",

View file

@ -25,6 +25,11 @@ macro_rules! tagged_event {
}} }}
} }
#[macro_export]
macro_rules! admin_debug {
($($arg:tt)*) => { tagged_event!(DEBUG, EventTag::AdminDebug, $($arg)*) }
}
#[macro_export] #[macro_export]
macro_rules! admin_error { macro_rules! admin_error {
($($arg:tt)*) => { tagged_event!(ERROR, EventTag::AdminError, $($arg)*) } ($($arg:tt)*) => { tagged_event!(ERROR, EventTag::AdminError, $($arg)*) }

View file

@ -31,6 +31,8 @@ ldap3_proto = "^0.2.3"
tracing = { version = "^0.1.35", features = ["attributes"] } tracing = { version = "^0.1.35", features = ["attributes"] }
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.81"
async-trait = "^0.1.53" async-trait = "^0.1.53"
async-std = { version = "^1.12.0", features = ["tokio1"] } async-std = { version = "^1.12.0", features = ["tokio1"] }
compact_jwt = "^0.2.1" compact_jwt = "^0.2.1"
@ -44,7 +46,6 @@ tracing-subscriber = "^0.3.11"
# kanidm = { path = "../kanidmd" } # kanidm = { path = "../kanidmd" }
# score = { path = "../kanidmd/score" } # score = { path = "../kanidmd/score" }
futures = "^0.3.21" futures = "^0.3.21"
serde_json = "^1.0.80"
# async-std = { version = "1.6", features = ["tokio1"] } # async-std = { version = "1.6", features = ["tokio1"] }
webauthn-authenticator-rs = "^0.3.2" webauthn-authenticator-rs = "^0.3.2"

View file

@ -27,19 +27,17 @@ extern crate kanidm;
mod https; mod https;
mod ldaps; mod ldaps;
use libc::umask;
// use crossbeam::channel::unbounded; // use crossbeam::channel::unbounded;
use async_std::task;
use compact_jwt::JwsSigner;
use kanidm::prelude::*; use kanidm::prelude::*;
use std::sync::Arc; use libc::umask;
use kanidm::config::Configuration;
// SearchResult
// use self::ctx::ServerCtx;
use kanidm::actors::v1_read::QueryServerReadV1; use kanidm::actors::v1_read::QueryServerReadV1;
use kanidm::actors::v1_write::QueryServerWriteV1; use kanidm::actors::v1_write::QueryServerWriteV1;
use kanidm::be::{Backend, BackendConfig, BackendTransaction, FsType}; use kanidm::be::{Backend, BackendConfig, BackendTransaction, FsType};
use kanidm::config::{Configuration, ConsoleOutputMode};
use kanidm::crypto::setup_tls; use kanidm::crypto::setup_tls;
use kanidm::idm::server::{IdmServer, IdmServerDelayed}; use kanidm::idm::server::{IdmServer, IdmServerDelayed};
use kanidm::interval::IntervalActor; use kanidm::interval::IntervalActor;
@ -47,11 +45,12 @@ use kanidm::ldap::LdapServer;
use kanidm::schema::Schema; use kanidm::schema::Schema;
use kanidm::status::StatusActor; use kanidm::status::StatusActor;
use kanidm::utils::{duration_from_epoch_now, touch_file_or_quit}; use kanidm::utils::{duration_from_epoch_now, touch_file_or_quit};
use kanidm_proto::v1::OperationError; use kanidm_proto::v1::OperationError;
use async_std::task; use serde::{Deserialize, Serialize};
use compact_jwt::JwsSigner; use serde_json;
use std::fmt;
use std::sync::Arc;
// === internal setup helpers // === internal setup helpers
@ -484,6 +483,42 @@ pub fn verify_server_core(config: &Configuration) {
// Now add IDM server verifications? // Now add IDM server verifications?
} }
// TODO: should this go somewhere else
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum MessageStatus {
Failure,
Success,
}
impl fmt::Display for MessageStatus {
fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
match *self {
MessageStatus::Failure => f.write_str("failure"),
MessageStatus::Success => f.write_str("status"),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
struct AccountChangeMessage {
action: String,
result: String,
status: MessageStatus,
src_user: String,
dest_user: String,
}
impl fmt::Display for AccountChangeMessage {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
serde_json::to_string(self).unwrap_or(format!("{:?}", self))
)
}
}
pub fn recover_account_core(config: &Configuration, name: &str) { pub fn recover_account_core(config: &Configuration, name: &str) {
let schema = match Schema::new() { let schema = match Schema::new() {
Ok(s) => s, Ok(s) => s,
@ -527,7 +562,26 @@ pub fn recover_account_core(config: &Configuration, name: &str) {
std::process::exit(1); std::process::exit(1);
} }
}; };
eprintln!("Success - password reset to -> {}", new_pw); match config.output_mode {
ConsoleOutputMode::JSON => {
println!(
"{}",
AccountChangeMessage {
status: MessageStatus::Success,
src_user: String::from("command-line invocation"),
dest_user: name.to_string(),
result: new_pw,
action: String::from("recover_account"),
}
);
}
ConsoleOutputMode::Text => {
println!(
"Successfully recovered account '{}' - password reset to -> {}",
name, new_pw
);
}
}
} }
pub async fn create_server_core(config: Configuration, config_test: bool) -> Result<(), ()> { pub async fn create_server_core(config: Configuration, config_test: bool) -> Result<(), ()> {

View file

@ -18,7 +18,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies] [dependencies]
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.80" serde_json = "^1.0.81"
wasm-bindgen = { version = "^0.2.81", features = ["serde-serialize"] } wasm-bindgen = { version = "^0.2.81", features = ["serde-serialize"] }
wasm-bindgen-futures = { version = "^0.4.30" } wasm-bindgen-futures = { version = "^0.4.30" }

View file

@ -91,7 +91,7 @@ impl Component for DeleteApp {
type Message = Msg; type Message = Msg;
type Properties = ModalProps; type Properties = ModalProps;
fn create(ctx: &Context<Self>) -> Self { fn create(_ctx: &Context<Self>) -> Self {
console::log!("delete modal create"); console::log!("delete modal create");
DeleteApp { state: State::Init } DeleteApp { state: State::Init }

View file

@ -24,7 +24,7 @@ clap = { version = "^3.2", features = ["derive"] }
uuid = { version = "^1.1.2", features = ["serde", "v4" ] } uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
csv = "1.1.6" csv = "1.1.6"
serde = { version = "^1.0.137", features = ["derive"] } serde = { version = "^1.0.137", features = ["derive"] }
serde_json = "^1.0.80" serde_json = "^1.0.81"
rand = "^0.8.5" rand = "^0.8.5"
toml = "^0.5.9" toml = "^0.5.9"