diff --git a/DEVELOPER_README.md b/DEVELOPER_README.md index 8a6d8269a..d4087c4d1 100644 --- a/DEVELOPER_README.md +++ b/DEVELOPER_README.md @@ -148,7 +148,7 @@ in `/tmp/kanidm.db`. 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 Success - password reset to -> Et8QRJgQkMJu3v1AQxcbxRWW44qRUZPpr6BJ9fCGapAB9cT4 diff --git a/kanidm_book/src/server_configuration.md b/kanidm_book/src/server_configuration.md index 3e1b1d149..ef84f67fe 100644 --- a/kanidm_book/src/server_configuration.md +++ b/kanidm_book/src/server_configuration.md @@ -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. 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 diff --git a/kanidm_client/Cargo.toml b/kanidm_client/Cargo.toml index 556324e5f..da0d43c36 100644 --- a/kanidm_client/Cargo.toml +++ b/kanidm_client/Cargo.toml @@ -15,7 +15,7 @@ tracing = "^0.1.35" reqwest = { version = "^0.11.11", features=["cookies", "json", "native-tls"] } kanidm_proto = { path = "../kanidm_proto", version = "1.1.0-alpha.8" } serde = { version = "^1.0.137", features = ["derive"] } -serde_json = "^1.0.80" +serde_json = "^1.0.81" toml = "^0.5.9" uuid = { version = "^1.1.2", features = ["serde", "v4"] } url = { version = "^2.2.2", features = ["serde"] } diff --git a/kanidm_proto/Cargo.toml b/kanidm_proto/Cargo.toml index 8a73ed8c9..1ff25092e 100644 --- a/kanidm_proto/Cargo.toml +++ b/kanidm_proto/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/kanidm/kanidm/" [dependencies] serde = { version = "^1.0.137", features = ["derive"] } -serde_json = "^1.0.80" +serde_json = "^1.0.81" uuid = { version = "^1.1.2", features = ["serde"] } base32 = "^0.4.0" webauthn-rs = { version = "^0.3.2", default-features = false, features = ["wasm"] } diff --git a/kanidm_tools/Cargo.toml b/kanidm_tools/Cargo.toml index 4ae509bb4..d59950c64 100644 --- a/kanidm_tools/Cargo.toml +++ b/kanidm_tools/Cargo.toml @@ -37,7 +37,7 @@ rpassword = "^6.0.1" clap = { version = "^3.2", features = ["derive", "env"] } libc = "^0.2.126" serde = { version = "^1.0.137", features = ["derive"] } -serde_json = "^1.0.80" +serde_json = "^1.0.81" shellexpand = "^2.1.0" rayon = "^1.5.3" time = { version = "=0.2.27", features = ["serde", "std"] } diff --git a/kanidm_unix_int/Cargo.toml b/kanidm_unix_int/Cargo.toml index ddb79d5bd..60288259b 100644 --- a/kanidm_unix_int/Cargo.toml +++ b/kanidm_unix_int/Cargo.toml @@ -60,7 +60,7 @@ bytes = "^1.1.0" libc = "^0.2.126" serde = { version = "^1.0.137", features = ["derive"] } -serde_json = "^1.0.80" +serde_json = "^1.0.81" clap = { version = "^3.2", features = ["derive"] } libsqlite3-sys = "0.24.2" diff --git a/kanidmd/daemon/src/main.rs b/kanidmd/daemon/src/main.rs index f9cd77966..7c8baa8fb 100644 --- a/kanidmd/daemon/src/main.rs +++ b/kanidmd/daemon/src/main.rs @@ -28,7 +28,7 @@ use std::path::PathBuf; use std::str::FromStr; use kanidm::audit::LogLevel; -use kanidm::config::{Configuration, OnlineBackup, ServerRole}; +use kanidm::config::{Configuration, ConsoleOutputMode, OnlineBackup, ServerRole}; use kanidm::tracing_tree; use kanidm::utils::file_permissions_readonly; use score::{ @@ -221,6 +221,9 @@ async fn main() { config.update_domain(&sconfig.domain.as_str()); config.update_db_arc_size(sconfig.db_arc_size); 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. if let Some(dll) = opt.commands.commonopt().debug.as_ref() { diff --git a/kanidmd/daemon/src/opt.rs b/kanidmd/daemon/src/opt.rs index 77023c17c..ed53d9eba 100644 --- a/kanidmd/daemon/src/opt.rs +++ b/kanidmd/daemon/src/opt.rs @@ -6,6 +6,10 @@ struct CommonOpt { #[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. 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)] @@ -28,7 +32,7 @@ struct RestoreOpt { #[derive(Debug, Args)] struct RecoverAccountOpt { - #[clap(short)] + #[clap(value_parser)] /// The account name to recover credentials for. name: String, #[clap(flatten)] diff --git a/kanidmd/idm/Cargo.toml b/kanidmd/idm/Cargo.toml index 2be5125bf..b9d9b5a6d 100644 --- a/kanidmd/idm/Cargo.toml +++ b/kanidmd/idm/Cargo.toml @@ -48,7 +48,7 @@ uuid = { version = "^1.1.2", features = ["serde", "v4" ] } compiled-uuid = "0.1.2" serde = { version = "^1.0.137", features = ["derive"] } serde_cbor = "^0.11.2" -serde_json = "^1.0.80" +serde_json = "^1.0.81" libsqlite3-sys = "0.24.2" rusqlite = "^0.27.0" diff --git a/kanidmd/idm/src/be/mod.rs b/kanidmd/idm/src/be/mod.rs index 2ef18814a..ff98af3db 100644 --- a/kanidmd/idm/src/be/mod.rs +++ b/kanidmd/idm/src/be/mod.rs @@ -1255,7 +1255,7 @@ impl<'a> BackendWriteTransaction<'a> { pub fn upgrade_reindex(&self, v: i64) -> Result<(), OperationError> { let dbv = self.get_db_index_version(); - admin_info!(?dbv, ?v, "upgrade_reindex"); + admin_debug!(?dbv, ?v, "upgrade_reindex"); if dbv < v { limmediate_warning!( "NOTICE: A system reindex is required. This may take a long time ...\n" @@ -1514,9 +1514,9 @@ impl Backend { idxkeys: Vec, vacuum: bool, ) -> Result { - info!("DB tickets -> {:?}", cfg.pool_size); - info!("Profile -> {}", env!("KANIDM_PROFILE_NAME")); - info!("CPU Flags -> {}", env!("KANIDM_CPU_FLAGS")); + debug!("DB tickets -> {:?}", cfg.pool_size); + debug!("Profile -> {}", env!("KANIDM_PROFILE_NAME")); + debug!("CPU Flags -> {}", env!("KANIDM_CPU_FLAGS")); // If in memory, reduce pool to 1 if cfg.path.is_empty() { diff --git a/kanidmd/idm/src/config.rs b/kanidmd/idm/src/config.rs index f6f2995e4..b97e50e57 100644 --- a/kanidmd/idm/src/config.rs +++ b/kanidmd/idm/src/config.rs @@ -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 { + match s { + "json" => Ok(ConsoleOutputMode::JSON), + "text" => Ok(ConsoleOutputMode::Text), + _ => Err("Must be one of json, text"), + } + } +} + #[derive(Serialize, Deserialize, Debug, Default)] pub struct Configuration { pub address: String, @@ -93,6 +118,7 @@ pub struct Configuration { pub domain: String, pub origin: String, pub role: ServerRole, + pub output_mode: ConsoleOutputMode, } 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, "with TLS: {}, ", self.tls_config.is_some())) .and_then(|_| match self.log_level { - Some(u) => write!(f, "with log_level: {:x}, ", u), - None => write!(f, "with log_level: default, "), + Some(u) => write!(f, "log_level: {:x}, ", u), + None => write!(f, "log_level: default, "), }) + // TODO: include the backup timings .and_then(|_| match &self.online_backup { - Some(_) => write!(f, "with online_backup: enabled, "), - None => write!(f, "with online_backup: disabled, "), + Some(_) => write!(f, "online_backup: enabled, "), + None => write!(f, "online_backup: disabled, "), }) .and_then(|_| write!(f, "role: {}, ", self.role.to_string())) .and_then(|_| { write!( f, - "integration mode: {}", + "integration mode: {}, ", 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_fs_type: None, db_arc_size: None, - maximum_request: 262_144, // 256k + maximum_request: 256 * 1024, // 256k // log type // log path // TODO #63: default true in prd @@ -157,6 +185,7 @@ impl Configuration { domain: "idm.example.com".to_string(), origin: "https://idm.example.com".to_string(), role: ServerRole::WriteReplica, + output_mode: ConsoleOutputMode::default(), }; let mut rng = StdRng::from_entropy(); rng.fill(&mut c.cookie_key); @@ -218,6 +247,11 @@ impl Configuration { 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, key: &Option) { match (chain, key) { (None, None) => {} diff --git a/kanidmd/idm/src/lib.rs b/kanidmd/idm/src/lib.rs index 98ed72cae..a85193b8d 100644 --- a/kanidmd/idm/src/lib.rs +++ b/kanidmd/idm/src/lib.rs @@ -93,9 +93,9 @@ pub mod prelude { ValueSetUuid, }; pub use crate::{ - admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace, filter_warn, - perf_trace, request_error, request_info, request_trace, request_warn, security_access, - security_critical, security_error, security_info, spanned, + admin_debug, admin_error, admin_info, admin_warn, filter_error, filter_info, filter_trace, + filter_warn, perf_trace, request_error, request_info, request_trace, request_warn, + security_access, security_critical, security_error, security_info, spanned, }; } diff --git a/kanidmd/idm/src/schema.rs b/kanidmd/idm/src/schema.rs index 9a4cb95ed..0ae8b052f 100644 --- a/kanidmd/idm/src/schema.rs +++ b/kanidmd/idm/src/schema.rs @@ -1354,10 +1354,10 @@ impl<'a> SchemaWriteTransaction<'a> { let r = self.validate(); if r.is_empty() { - admin_info!("schema validate -> passed"); + admin_debug!("schema validate -> passed"); Ok(()) } else { - admin_info!(err = ?r, "schema validate -> errors"); + admin_error!(err = ?r, "schema validate -> errors"); Err(OperationError::ConsistencyError(r)) } }) diff --git a/kanidmd/idm/src/server.rs b/kanidmd/idm/src/server.rs index 2c6dc6e55..1ddf2a75f 100644 --- a/kanidmd/idm/src/server.rs +++ b/kanidmd/idm/src/server.rs @@ -1094,7 +1094,7 @@ impl QueryServer { Err(OperationError::NoMatchingEntries) => Ok(0), Err(r) => Err(r), }?; - admin_info!(?system_info_version); + admin_debug!(?system_info_version); if system_info_version < 3 { migrate_txn.migrate_2_to_3()?; @@ -1119,8 +1119,8 @@ impl QueryServer { ts_write_3 .initialise_idm() .and_then(|_| ts_write_3.commit())?; - - admin_info!("migrations success! ☀️ "); + // TODO: work out if we've actually done any migrations before printing this + admin_debug!("Database version check and migrations success! ☀️ "); Ok(()) } @@ -2267,7 +2267,7 @@ impl<'a> QueryServerWriteTransaction<'a> { */ 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". let entries = self.schema.to_entries(); @@ -2277,9 +2277,9 @@ impl<'a> QueryServerWriteTransaction<'a> { self.internal_migrate_or_create(e) }); if r.is_ok() { - admin_info!("initialise_schema_core -> Ok!"); + admin_debug!("initialise_schema_core -> Ok!"); } 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`? debug_assert!(r.is_ok()); @@ -2287,7 +2287,7 @@ impl<'a> QueryServerWriteTransaction<'a> { } 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. let idm_schema: Vec<&str> = vec![ 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)); if r.is_ok() { - admin_info!("initialise_schema_idm -> Ok!"); + admin_debug!("initialise_schema_idm -> Ok!"); } else { admin_error!(res = ?r, "initialise_schema_idm -> Error"); } @@ -2467,7 +2467,7 @@ impl<'a> QueryServerWriteTransaction<'a> { .iter() .try_for_each(|e_str| self.internal_migrate_or_create_str(e_str)); if res.is_ok() { - admin_info!("initialise_idm -> result Ok!"); + admin_debug!("initialise_idm -> result Ok!"); } else { admin_error!(?res, "initialise_idm p3 -> result"); } diff --git a/kanidmd/idm/src/tracing_tree/event_tag.rs b/kanidmd/idm/src/tracing_tree/event_tag.rs index 3d896bfe0..9b53b087b 100644 --- a/kanidmd/idm/src/tracing_tree/event_tag.rs +++ b/kanidmd/idm/src/tracing_tree/event_tag.rs @@ -3,6 +3,7 @@ use num_enum::{IntoPrimitive, TryFromPrimitive}; #[derive(Debug, Clone, Copy, IntoPrimitive, TryFromPrimitive)] #[repr(u64)] pub enum EventTag { + AdminDebug, AdminError, AdminWarn, AdminInfo, @@ -24,6 +25,7 @@ pub enum EventTag { impl EventTag { pub fn pretty(self) -> &'static str { match self { + EventTag::AdminDebug => "admin.debug", EventTag::AdminError => "admin.error", EventTag::AdminWarn => "admin.warn", EventTag::AdminInfo => "admin.info", @@ -46,8 +48,9 @@ impl EventTag { pub fn emoji(self) -> &'static str { use EventTag::*; match self { + AdminDebug => "🐛", AdminError | FilterError | RequestError | SecurityError => "🚨", - AdminWarn | FilterWarn | RequestWarn => "⚠️ ", + AdminWarn | FilterWarn | RequestWarn => "⚠️", AdminInfo | FilterInfo | RequestInfo | SecurityInfo => " ", RequestTrace | FilterTrace | PerfTrace => "📍", SecurityCritical => "🔐", diff --git a/kanidmd/idm/src/tracing_tree/macros.rs b/kanidmd/idm/src/tracing_tree/macros.rs index ec4c3076e..380c12e34 100644 --- a/kanidmd/idm/src/tracing_tree/macros.rs +++ b/kanidmd/idm/src/tracing_tree/macros.rs @@ -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_rules! admin_error { ($($arg:tt)*) => { tagged_event!(ERROR, EventTag::AdminError, $($arg)*) } diff --git a/kanidmd/score/Cargo.toml b/kanidmd/score/Cargo.toml index 946e91ddc..56e36fc2d 100644 --- a/kanidmd/score/Cargo.toml +++ b/kanidmd/score/Cargo.toml @@ -31,6 +31,8 @@ ldap3_proto = "^0.2.3" tracing = { version = "^0.1.35", features = ["attributes"] } serde = { version = "^1.0.137", features = ["derive"] } +serde_json = "^1.0.81" + async-trait = "^0.1.53" async-std = { version = "^1.12.0", features = ["tokio1"] } compact_jwt = "^0.2.1" @@ -44,7 +46,6 @@ tracing-subscriber = "^0.3.11" # kanidm = { path = "../kanidmd" } # score = { path = "../kanidmd/score" } futures = "^0.3.21" -serde_json = "^1.0.80" # async-std = { version = "1.6", features = ["tokio1"] } webauthn-authenticator-rs = "^0.3.2" diff --git a/kanidmd/score/src/lib.rs b/kanidmd/score/src/lib.rs index d970f6f29..ee59b1def 100644 --- a/kanidmd/score/src/lib.rs +++ b/kanidmd/score/src/lib.rs @@ -27,19 +27,17 @@ extern crate kanidm; mod https; mod ldaps; -use libc::umask; // use crossbeam::channel::unbounded; +use async_std::task; +use compact_jwt::JwsSigner; 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_write::QueryServerWriteV1; use kanidm::be::{Backend, BackendConfig, BackendTransaction, FsType}; +use kanidm::config::{Configuration, ConsoleOutputMode}; use kanidm::crypto::setup_tls; use kanidm::idm::server::{IdmServer, IdmServerDelayed}; use kanidm::interval::IntervalActor; @@ -47,11 +45,12 @@ use kanidm::ldap::LdapServer; use kanidm::schema::Schema; use kanidm::status::StatusActor; use kanidm::utils::{duration_from_epoch_now, touch_file_or_quit}; - use kanidm_proto::v1::OperationError; -use async_std::task; -use compact_jwt::JwsSigner; +use serde::{Deserialize, Serialize}; +use serde_json; +use std::fmt; +use std::sync::Arc; // === internal setup helpers @@ -484,6 +483,42 @@ pub fn verify_server_core(config: &Configuration) { // 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) { let schema = match Schema::new() { Ok(s) => s, @@ -527,7 +562,26 @@ pub fn recover_account_core(config: &Configuration, name: &str) { 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<(), ()> { diff --git a/kanidmd_web_ui/Cargo.toml b/kanidmd_web_ui/Cargo.toml index 90373b83b..e09a681e6 100644 --- a/kanidmd_web_ui/Cargo.toml +++ b/kanidmd_web_ui/Cargo.toml @@ -18,7 +18,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] 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-futures = { version = "^0.4.30" } diff --git a/kanidmd_web_ui/src/credential/delete.rs b/kanidmd_web_ui/src/credential/delete.rs index 46cd5008b..0f2b97650 100644 --- a/kanidmd_web_ui/src/credential/delete.rs +++ b/kanidmd_web_ui/src/credential/delete.rs @@ -91,7 +91,7 @@ impl Component for DeleteApp { type Message = Msg; type Properties = ModalProps; - fn create(ctx: &Context) -> Self { + fn create(_ctx: &Context) -> Self { console::log!("delete modal create"); DeleteApp { state: State::Init } diff --git a/orca/Cargo.toml b/orca/Cargo.toml index d36f524a0..ba5c423b4 100644 --- a/orca/Cargo.toml +++ b/orca/Cargo.toml @@ -24,7 +24,7 @@ clap = { version = "^3.2", features = ["derive"] } uuid = { version = "^1.1.2", features = ["serde", "v4" ] } csv = "1.1.6" serde = { version = "^1.0.137", features = ["derive"] } -serde_json = "^1.0.80" +serde_json = "^1.0.81" rand = "^0.8.5" toml = "^0.5.9"