mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
bleep bloop what was I doing again (#870)
* human-facing message generator thingie * doctests for new code
This commit is contained in:
parent
b380c4b4ba
commit
664e2e4df0
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1010,6 +1010,7 @@ dependencies = [
|
|||
"clap 3.2.6",
|
||||
"clap_complete",
|
||||
"kanidm",
|
||||
"kanidm_proto",
|
||||
"profiles",
|
||||
"score",
|
||||
"serde",
|
||||
|
|
|
@ -8,5 +8,6 @@
|
|||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
|
||||
pub mod messages;
|
||||
pub mod oauth2;
|
||||
pub mod v1;
|
||||
|
|
129
kanidm_proto/src/messages.rs
Normal file
129
kanidm_proto/src/messages.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// User-facing output things
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]
|
||||
#[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;
|
||||
/// This can be safely unwrap'd because it'll always return a default
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"json" => Ok(ConsoleOutputMode::JSON),
|
||||
"text" => Ok(ConsoleOutputMode::Text),
|
||||
_ => {
|
||||
eprintln!(
|
||||
"Supplied output mode ({:?}) was invalid, defaulting to text",
|
||||
s
|
||||
);
|
||||
Ok(ConsoleOutputMode::Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This will take any string, if it's 'text' or 'json' then you'll get
|
||||
/// what you asked for, else you'll get a text version.
|
||||
///
|
||||
/// ```
|
||||
/// use kanidm_proto::messages::ConsoleOutputMode;
|
||||
/// let bork = "text";
|
||||
/// let com: ConsoleOutputMode = bork.into();
|
||||
/// matches!(ConsoleOutputMode::Text, com);
|
||||
/// ```
|
||||
impl From<&str> for ConsoleOutputMode {
|
||||
fn from(input: &str) -> Self {
|
||||
match ConsoleOutputMode::from_str(input) {
|
||||
Ok(val) => val,
|
||||
Err(_) => Self::Text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This will take any string, if it's 'text' or 'json' then you'll get
|
||||
/// what you asked for, else you'll get a text version.
|
||||
///
|
||||
/// ```
|
||||
/// use kanidm_proto::messages::ConsoleOutputMode;
|
||||
/// let bork = String::from("cr4bz");
|
||||
/// let com: ConsoleOutputMode = bork.into();
|
||||
/// matches!(ConsoleOutputMode::Text, com);
|
||||
/// ```
|
||||
impl From<String> for ConsoleOutputMode {
|
||||
fn from(input: String) -> Self {
|
||||
match ConsoleOutputMode::from_str(input.as_str()) {
|
||||
Ok(val) => val,
|
||||
Err(_) => Self::Text,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub 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("success"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AccountChangeMessage {
|
||||
#[serde(skip_serializing)]
|
||||
pub output_mode: ConsoleOutputMode,
|
||||
pub action: String,
|
||||
pub result: String,
|
||||
pub status: MessageStatus,
|
||||
pub src_user: String,
|
||||
pub dest_user: String,
|
||||
}
|
||||
|
||||
impl Default for AccountChangeMessage {
|
||||
fn default() -> Self {
|
||||
AccountChangeMessage {
|
||||
output_mode: ConsoleOutputMode::Text,
|
||||
action: String::from(""),
|
||||
result: String::from(""),
|
||||
status: MessageStatus::Success,
|
||||
src_user: String::from(""),
|
||||
dest_user: String::from(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This outputs in either JSON or Text depending on the output_mode setting
|
||||
impl fmt::Display for AccountChangeMessage {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.output_mode {
|
||||
ConsoleOutputMode::JSON => write!(
|
||||
f,
|
||||
"{}",
|
||||
serde_json::to_string(self).unwrap_or(format!("{:?}", self)) // if it fails to JSON serialize, just debug-dump it
|
||||
),
|
||||
ConsoleOutputMode::Text => write!(
|
||||
f,
|
||||
"{} - {} for user {}: {}",
|
||||
self.status, self.action, self.dest_user, self.result,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,22 +3,17 @@ use crate::{
|
|||
AccountCredential, AccountOpt, AccountPerson, AccountPosix, AccountRadius, AccountSsh,
|
||||
AccountValidity,
|
||||
};
|
||||
use qrcode::{render::unicode, QrCode};
|
||||
// use std::io;
|
||||
use kanidm_client::KanidmClient;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use dialoguer::{theme::ColorfulTheme, Select};
|
||||
use dialoguer::{Confirm, Input, Password};
|
||||
|
||||
// use webauthn_authenticator_rs::{u2fhid::U2FHid, WebauthnAuthenticator};
|
||||
|
||||
// use kanidm_client::ClientError;
|
||||
use kanidm_client::ClientError::Http as ClientErrorHttp;
|
||||
use kanidm_proto::v1::OperationError::PasswordQuality;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::messages::{AccountChangeMessage, MessageStatus};
|
||||
use kanidm_proto::v1::OperationError::{InvalidAttribute, PasswordQuality};
|
||||
use kanidm_proto::v1::{CUIntentToken, CURegState, CUSessionToken, CUStatus};
|
||||
use std::fmt;
|
||||
use qrcode::{render::unicode, QrCode};
|
||||
use std::fmt::{self, Debug};
|
||||
use std::str::FromStr;
|
||||
use time::OffsetDateTime;
|
||||
use url::Url;
|
||||
|
||||
impl AccountOpt {
|
||||
|
@ -149,10 +144,23 @@ impl AccountOpt {
|
|||
}
|
||||
}
|
||||
}, // end AccountOpt::Posix
|
||||
|
||||
AccountOpt::Person { commands } => match commands {
|
||||
AccountPerson::Extend(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
if let Err(e) = client
|
||||
let mut result_output = kanidm_proto::messages::AccountChangeMessage {
|
||||
output_mode: aopt.copt.output_mode.to_owned().into(),
|
||||
action: String::from("account_person_extend"),
|
||||
result: String::from("This is a filler message"),
|
||||
src_user: aopt
|
||||
.copt
|
||||
.username
|
||||
.to_owned()
|
||||
.unwrap_or(format!("{:?}", client.whoami().await)),
|
||||
dest_user: aopt.aopts.account_id.to_string(),
|
||||
status: kanidm_proto::messages::MessageStatus::Failure,
|
||||
};
|
||||
match client
|
||||
.idm_account_person_extend(
|
||||
aopt.aopts.account_id.as_str(),
|
||||
aopt.mail.as_deref(),
|
||||
|
@ -160,12 +168,71 @@ impl AccountOpt {
|
|||
)
|
||||
.await
|
||||
{
|
||||
error!("Error -> {:?}", e);
|
||||
// TODO: JSON output for account person extend.
|
||||
Ok(_) => {
|
||||
result_output.result = format!(
|
||||
"Successfully extended the user {}.",
|
||||
aopt.aopts.account_id
|
||||
);
|
||||
match &aopt.legalname {
|
||||
Some(legalname) => {
|
||||
result_output.result +=
|
||||
format!(" Set legalname to {}.", legalname).as_str()
|
||||
}
|
||||
_ => debug!("Didn't change legalname field."),
|
||||
};
|
||||
match &aopt.mail {
|
||||
Some(mail) => {
|
||||
result_output.result +=
|
||||
format!(" Set mail to {:?}.", mail.join(", ")).as_str()
|
||||
}
|
||||
_ => debug!("Didn't change mail field."),
|
||||
};
|
||||
println!("{}", result_output);
|
||||
}
|
||||
// TODO: consider a macro to unpack the KanidmClient result object
|
||||
Err(e) => {
|
||||
match e {
|
||||
ClientErrorHttp(_, result, _) => {
|
||||
if let Some(ia_error) = result {
|
||||
match ia_error {
|
||||
InvalidAttribute(msg) => {
|
||||
result_output.result =
|
||||
format!("Failed to set value: {}", msg)
|
||||
}
|
||||
_ => {
|
||||
result_output.result =
|
||||
format!("Operation Error - {:?}", ia_error)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
result_output.result =
|
||||
format!("ClientError - {:?}", result);
|
||||
}
|
||||
}
|
||||
_ => result_output.result = format!("Error -> {:?}", e),
|
||||
};
|
||||
eprintln!("{:?}", result_output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: there should probably be an 'unset' variant of this
|
||||
AccountPerson::Set(aopt) => {
|
||||
let client = aopt.copt.to_client().await;
|
||||
if let Err(e) = client
|
||||
let mut result_output = AccountChangeMessage {
|
||||
output_mode: aopt.copt.output_mode.to_owned().into(),
|
||||
action: String::from("account_person set"),
|
||||
result: String::from(""),
|
||||
src_user: aopt
|
||||
.copt
|
||||
.username
|
||||
.to_owned()
|
||||
.unwrap_or(format!("{:?}", client.whoami().await)),
|
||||
dest_user: aopt.aopts.account_id.to_string(),
|
||||
status: MessageStatus::Failure,
|
||||
};
|
||||
match client
|
||||
.idm_account_person_set(
|
||||
aopt.aopts.account_id.as_str(),
|
||||
aopt.mail.as_deref(),
|
||||
|
@ -173,7 +240,16 @@ impl AccountOpt {
|
|||
)
|
||||
.await
|
||||
{
|
||||
error!("Error -> {:?}", e);
|
||||
Ok(_) => {
|
||||
result_output.status = kanidm_proto::messages::MessageStatus::Success;
|
||||
result_output.result = "set 'person' status on user".to_string();
|
||||
println!("{}", result_output)
|
||||
}
|
||||
// TODO: ponder macro'ing handling ClientError
|
||||
Err(e) => {
|
||||
result_output.result = format!("Error -> {:?}", e);
|
||||
eprintln!("{}", result_output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, // end AccountOpt::Person
|
||||
|
@ -460,13 +536,13 @@ impl AccountCredential {
|
|||
.build();
|
||||
println!("{}", image);
|
||||
|
||||
println!("");
|
||||
println!();
|
||||
println!("link: {}", url.as_str());
|
||||
println!(
|
||||
"command: kanidm account credential reset {}",
|
||||
cuintent_token.token
|
||||
);
|
||||
println!("");
|
||||
println!();
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error starting credential reset -> {:?}", e);
|
||||
|
@ -826,22 +902,20 @@ async fn credential_update_exec(
|
|||
|
||||
if password_a != password_b {
|
||||
eprintln!("Passwords do not match");
|
||||
} else {
|
||||
if let Err(e) = client
|
||||
.idm_account_credential_update_set_password(&session_token, &password_a)
|
||||
.await
|
||||
{
|
||||
match e {
|
||||
ClientErrorHttp(_, Some(PasswordQuality(feedback)), _) => {
|
||||
for fb_item in feedback.iter() {
|
||||
eprintln!("{:?}", fb_item)
|
||||
}
|
||||
} else if let Err(e) = client
|
||||
.idm_account_credential_update_set_password(&session_token, &password_a)
|
||||
.await
|
||||
{
|
||||
match e {
|
||||
ClientErrorHttp(_, Some(PasswordQuality(feedback)), _) => {
|
||||
for fb_item in feedback.iter() {
|
||||
eprintln!("{:?}", fb_item)
|
||||
}
|
||||
_ => eprintln!("An error occured -> {:?}", e),
|
||||
}
|
||||
} else {
|
||||
println!("success");
|
||||
_ => eprintln!("An error occured -> {:?}", e),
|
||||
}
|
||||
} else {
|
||||
println!("Successfully reset password.");
|
||||
}
|
||||
}
|
||||
CUAction::Totp => totp_enroll_prompt(&session_token, &client).await,
|
||||
|
|
|
@ -155,7 +155,7 @@ pub fn prompt_for_username_get_values() -> Result<(String, String), String> {
|
|||
options.push(String::from(option.0));
|
||||
}
|
||||
let user_select = Select::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Authentication tokens exist. Please select one")
|
||||
.with_prompt("Multiple authentication tokens exist. Please select one")
|
||||
.default(0)
|
||||
.items(&options)
|
||||
.interact();
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
|
||||
use clap::Parser;
|
||||
use kanidm_cli::KanidmClientParser;
|
||||
|
||||
use tracing_subscriber::prelude::*;
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
|
|
|
@ -15,14 +15,20 @@ pub struct DebugOpt {
|
|||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct CommonOpt {
|
||||
// TODO: this should probably be a flag, or renamed to log level if it's a level
|
||||
#[clap(short, long, env = "KANIDM_DEBUG")]
|
||||
pub debug: bool,
|
||||
#[clap(short = 'H', long = "url", env = "KANIDM_URL")]
|
||||
pub addr: Option<String>,
|
||||
/// User which will initiate requests
|
||||
#[clap(short = 'D', long = "name", env = "KANIDM_NAME")]
|
||||
pub username: Option<String>,
|
||||
/// Path to a CA certificate file
|
||||
#[clap(parse(from_os_str), short = 'C', long = "ca", env = "KANIDM_CA_PATH")]
|
||||
pub ca_path: Option<PathBuf>,
|
||||
/// Log format (still in very early development)
|
||||
#[clap(short, long = "output", env = "KANIDM_OUTPUT", default_value="text")]
|
||||
pub output_mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
|
@ -183,9 +189,9 @@ pub enum AccountRadius {
|
|||
pub struct AccountPosixOpt {
|
||||
#[clap(flatten)]
|
||||
aopts: AccountCommonOpt,
|
||||
#[clap(long = "gidnumber")]
|
||||
#[clap(long)]
|
||||
gidnumber: Option<u32>,
|
||||
#[clap(long = "shell")]
|
||||
#[clap(long)]
|
||||
shell: Option<String>,
|
||||
#[clap(flatten)]
|
||||
copt: CommonOpt,
|
||||
|
@ -205,9 +211,9 @@ pub enum AccountPosix {
|
|||
pub struct AccountPersonOpt {
|
||||
#[clap(flatten)]
|
||||
aopts: AccountCommonOpt,
|
||||
#[clap(long = "mail")]
|
||||
#[clap(long, short, help="Set the mail address, can be set multiple times for multiple addresses.")]
|
||||
mail: Option<Vec<String>>,
|
||||
#[clap(long = "legalname")]
|
||||
#[clap(long, short, help="Set the legal name for the person.")]
|
||||
legalname: Option<String>,
|
||||
#[clap(flatten)]
|
||||
copt: CommonOpt,
|
||||
|
|
|
@ -18,6 +18,7 @@ path = "src/main.rs"
|
|||
|
||||
[dependencies]
|
||||
kanidm = { path = "../idm" }
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
score = { path = "../score" }
|
||||
clap = { version = "^3.2", features = ["derive", "env"] }
|
||||
users = "^0.11.0"
|
||||
|
|
|
@ -28,7 +28,7 @@ use std::path::PathBuf;
|
|||
use std::str::FromStr;
|
||||
|
||||
use kanidm::audit::LogLevel;
|
||||
use kanidm::config::{Configuration, ConsoleOutputMode, OnlineBackup, ServerRole};
|
||||
use kanidm::config::{Configuration, OnlineBackup, ServerRole};
|
||||
use kanidm::tracing_tree;
|
||||
use kanidm::utils::file_permissions_readonly;
|
||||
use score::{
|
||||
|
@ -221,9 +221,7 @@ 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(),
|
||||
);
|
||||
config.update_output_mode(opt.commands.commonopt().output_mode.to_owned().into());
|
||||
|
||||
// Apply any cli overrides, normally debug level.
|
||||
if let Some(dll) = opt.commands.commonopt().debug.as_ref() {
|
||||
|
|
|
@ -6,7 +6,6 @@ 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,
|
||||
|
|
|
@ -15,74 +15,54 @@ name = "kanidm"
|
|||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
tracing = { version = "^0.1.35", features = ["attributes"] }
|
||||
tracing-subscriber = { version = "^0.3.11", features = ["env-filter"] }
|
||||
tracing-serde = "^0.1.3"
|
||||
|
||||
dyn-clone = "^1.0.6"
|
||||
|
||||
url = { version = "^2.2.2", features = ["serde"] }
|
||||
tide = "^0.16.0"
|
||||
async-trait = "^0.1.53"
|
||||
fernet = { version = "^0.1.4", features = ["fernet_danger_timestamps"] }
|
||||
compact_jwt = "^0.2.1"
|
||||
|
||||
async-std = { version = "^1.12.0", features = ["tokio1"] }
|
||||
|
||||
rand = "^0.8.5"
|
||||
toml = "^0.5.9"
|
||||
|
||||
async-trait = "^0.1.53"
|
||||
base64 = "^0.13.0"
|
||||
chrono = "^0.4.19"
|
||||
saffron = "^0.1.0"
|
||||
regex = "^1.5.6"
|
||||
lazy_static = "^1.4.0"
|
||||
|
||||
compact_jwt = "^0.2.1"
|
||||
compiled-uuid = "0.1.2"
|
||||
concread = "^0.3.4"
|
||||
dyn-clone = "^1.0.6"
|
||||
fernet = { version = "^0.1.4", features = ["fernet_danger_timestamps"] }
|
||||
filetime = "^0.2.16"
|
||||
futures = "^0.3.21"
|
||||
futures-util = "^0.3.21"
|
||||
tokio = { version = "^1.19.1", features = ["net", "sync", "time"] }
|
||||
tokio-util = { version = "^0.7.3", features = ["codec"] }
|
||||
hashbrown = { version = "0.12.0", features = ["serde", "inline-more", "ahash"] }
|
||||
idlset = { version = "^0.2.3" }
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
lazy_static = "^1.4.0"
|
||||
ldap3_proto = "^0.2.3"
|
||||
libc = "^0.2.126"
|
||||
libsqlite3-sys = "0.24.2"
|
||||
num_enum = "^0.5.7"
|
||||
openssl = "^0.10.38"
|
||||
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
|
||||
compiled-uuid = "0.1.2"
|
||||
r2d2 = "^0.8.9"
|
||||
r2d2_sqlite = "^0.20.0"
|
||||
rand = "^0.8.5"
|
||||
regex = "^1.5.6"
|
||||
rusqlite = "^0.27.0"
|
||||
saffron = "^0.1.0"
|
||||
serde = { version = "^1.0.137", features = ["derive"] }
|
||||
serde_cbor = "^0.11.2"
|
||||
serde_json = "^1.0.81"
|
||||
|
||||
libsqlite3-sys = "0.24.2"
|
||||
rusqlite = "^0.27.0"
|
||||
r2d2 = "^0.8.10"
|
||||
r2d2_sqlite = "^0.20.0"
|
||||
|
||||
time = { version = "=0.2.27", features = ["serde", "std"] }
|
||||
|
||||
hashbrown = { version = "0.12.0", features = ["serde", "inline-more", "ahash"] }
|
||||
concread = "^0.3.4"
|
||||
smolset = "^1.3.1"
|
||||
|
||||
sshkeys = "^0.3.1"
|
||||
|
||||
zxcvbn = "^2.2.1"
|
||||
base64 = "^0.13.0"
|
||||
|
||||
idlset = { version = "^0.2.3" }
|
||||
|
||||
ldap3_proto = "^0.2.3"
|
||||
|
||||
webauthn-rs = "^0.3.2"
|
||||
|
||||
libc = "^0.2.126"
|
||||
users = "^0.11.0"
|
||||
|
||||
smartstring = { version = "^1.0.1", features = ["serde"] }
|
||||
|
||||
validator = { version = "^0.15.0", features = ["phone"] }
|
||||
|
||||
smolset = "^1.3.1"
|
||||
sshkeys = "^0.3.1"
|
||||
tide = "^0.16.0"
|
||||
time = { version = "=0.2.27", features = ["serde", "std"] }
|
||||
tokio = { version = "^1.19.1", features = ["net", "sync", "time"] }
|
||||
tokio-util = { version = "^0.7.3", features = ["codec"] }
|
||||
toml = "^0.5.9"
|
||||
touch = "^0.0.1"
|
||||
filetime = "^0.2.16"
|
||||
|
||||
num_enum = "^0.5.7"
|
||||
tracing = { version = "^0.1.35", features = ["attributes"] }
|
||||
tracing-serde = "^0.1.3"
|
||||
tracing-subscriber = { version = "^0.3.11", features = ["env-filter"] }
|
||||
url = { version = "^2.2.2", features = ["serde"] }
|
||||
users = "^0.11.0"
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
|
||||
validator = { version = "^0.15.0", features = ["phone"] }
|
||||
webauthn-rs = "^0.3.2"
|
||||
zxcvbn = "^2.2.1"
|
||||
|
||||
[features]
|
||||
# default = [ "libsqlite3-sys/bundled", "openssl/vendored" ]
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//! These components should be "per server". Any "per domain" config should be in the system
|
||||
//! or domain entries that are able to be replicated.
|
||||
|
||||
use kanidm_proto::messages::ConsoleOutputMode;
|
||||
use rand::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
|
@ -74,31 +75,6 @@ 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)]
|
||||
pub struct Configuration {
|
||||
pub address: String,
|
||||
|
|
|
@ -203,7 +203,7 @@ impl IdmServer {
|
|||
let valid = url.domain().map(|effective_domain| {
|
||||
// We need to prepend the '.' here to ensure that myexample.com != example.com,
|
||||
// rather than just ends with.
|
||||
effective_domain.ends_with(&format!(".{}", rp_id))
|
||||
effective_domain.ends_with(&format!(".{}", rp_id))
|
||||
|| effective_domain == rp_id
|
||||
}).unwrap_or(false);
|
||||
|
||||
|
@ -370,7 +370,7 @@ impl IdmServer {
|
|||
}
|
||||
|
||||
impl IdmServerDelayed {
|
||||
pub(crate) fn is_empty_or_panic(&mut self) {
|
||||
pub(crate) fn check_is_empty_or_panic(&mut self) {
|
||||
let waker = futures_task::noop_waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
match self.async_rx.poll_recv(&mut cx) {
|
||||
|
@ -3107,7 +3107,7 @@ mod tests {
|
|||
run_idm_test!(
|
||||
|qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| {
|
||||
// Assert the delayed action queue is empty
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
// Setup the admin w_ an imported password.
|
||||
{
|
||||
let qs_write = qs.write(duration_from_epoch_now());
|
||||
|
@ -3126,7 +3126,7 @@ mod tests {
|
|||
qs_write.commit().expect("failed to commit");
|
||||
}
|
||||
// Still empty
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
// Do an auth, this will trigger the action to send.
|
||||
check_admin_password(idms, "password");
|
||||
// process it.
|
||||
|
@ -3136,7 +3136,7 @@ mod tests {
|
|||
// Check the admin pw still matches
|
||||
check_admin_password(idms, "password");
|
||||
// No delayed action was queued.
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -3146,7 +3146,7 @@ mod tests {
|
|||
run_idm_test!(
|
||||
|_qs: &QueryServer, idms: &IdmServer, idms_delayed: &mut IdmServerDelayed| {
|
||||
// Assert the delayed action queue is empty
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
// Setup the admin with an imported unix pw.
|
||||
let idms_prox_write = idms.proxy_write(duration_from_epoch_now());
|
||||
|
||||
|
@ -3170,7 +3170,7 @@ mod tests {
|
|||
};
|
||||
assert!(idms_prox_write.qs_write.modify(&me_posix).is_ok());
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
// Get the auth ready.
|
||||
let uuae = UnixUserAuthEvent::new_internal(&UUID_ADMIN, "password");
|
||||
let mut idms_auth = idms.auth();
|
||||
|
@ -3197,7 +3197,7 @@ mod tests {
|
|||
};
|
||||
idms_auth.commit().expect("Must not fail");
|
||||
// No delayed action was queued.
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -3749,7 +3749,7 @@ mod tests {
|
|||
// Assert we can increment the counter if needed.
|
||||
|
||||
// Assert the delayed action queue is empty
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
|
||||
// Generate a fake counter increment
|
||||
let da = DelayedAction::WebauthnCounterIncrement(WebauthnCounterIncrement {
|
||||
|
@ -3841,7 +3841,7 @@ mod tests {
|
|||
assert!(idms_prox_write.commit().is_ok());
|
||||
|
||||
// Assert the delayed action queue is empty
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
|
||||
// Generate a fake action to remove one backup code
|
||||
let da = DelayedAction::BackupCodeRemoval(BackupCodeRemoval {
|
||||
|
|
|
@ -191,7 +191,7 @@ macro_rules! run_idm_test_inner {
|
|||
// Any needed teardown?
|
||||
// Make sure there are no errors.
|
||||
assert!(test_server.verify().len() == 0);
|
||||
idms_delayed.is_empty_or_panic();
|
||||
idms_delayed.check_is_empty_or_panic();
|
||||
}};
|
||||
}
|
||||
|
||||
|
|
|
@ -1579,7 +1579,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// Validate input.
|
||||
|
||||
// Is the modlist non zero?
|
||||
if me.modlist.len() == 0 {
|
||||
if me.modlist.is_empty() {
|
||||
request_error!("modify: empty modify request");
|
||||
return Err(OperationError::EmptyRequest);
|
||||
}
|
||||
|
|
|
@ -336,7 +336,8 @@ impl TreeEvent {
|
|||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
if field.name() == "event_tag_id" {
|
||||
let tag = EventTag::try_from(value).unwrap_or_else(|_| {
|
||||
panic!("Invalid `event_tag_id`: {}, this is a bug", value)
|
||||
error!("Invalid `event_tag_id`: {}, this is a bug", value);
|
||||
std::process::exit(1)
|
||||
});
|
||||
self.tag = Some(tag);
|
||||
} else {
|
||||
|
|
|
@ -13,29 +13,25 @@ repository = "https://github.com/kanidm/kanidm/"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
kanidm = { path = "../idm" }
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
libc = "^0.2.126"
|
||||
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
|
||||
|
||||
tide = "^0.16.0"
|
||||
tide-openssl = "^0.1.1"
|
||||
|
||||
async-std = { version = "^1.12.0", features = ["tokio1"] }
|
||||
async-trait = "^0.1.53"
|
||||
compact_jwt = "^0.2.1"
|
||||
futures-util = "^0.3.21"
|
||||
tokio = { version = "^1.19.1", features = ["net", "sync", "io-util", "macros"] }
|
||||
tokio-util = { version = "^0.7.3", features = ["codec"] }
|
||||
tokio-openssl = "^0.6.3"
|
||||
openssl = "^0.10.38"
|
||||
kanidm = { path = "../idm" }
|
||||
kanidm_client = { path = "../../kanidm_client" }
|
||||
kanidm_proto = { path = "../../kanidm_proto" }
|
||||
ldap3_proto = "^0.2.3"
|
||||
|
||||
tracing = { version = "^0.1.35", features = ["attributes"] }
|
||||
libc = "^0.2.126"
|
||||
openssl = "^0.10.38"
|
||||
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"
|
||||
tide = "^0.16.0"
|
||||
tide-openssl = "^0.1.1"
|
||||
tokio = { version = "^1.19.1", features = ["net", "sync", "io-util", "macros"] }
|
||||
tokio-openssl = "^0.6.3"
|
||||
tokio-util = { version = "^0.7.3", features = ["codec"] }
|
||||
tracing = { version = "^0.1.35", features = ["attributes"] }
|
||||
uuid = { version = "^1.1.2", features = ["serde", "v4" ] }
|
||||
|
||||
[build-dependencies]
|
||||
profiles = { path = "../../profiles" }
|
||||
|
@ -52,6 +48,5 @@ webauthn-authenticator-rs = "^0.3.2"
|
|||
oauth2_ext = { package = "oauth2", version = "^4.1.0", default-features = false }
|
||||
base64 = "^0.13.0"
|
||||
|
||||
kanidm_client = { path = "../../kanidm_client" }
|
||||
url = { version = "^2.2.2", features = ["serde"] }
|
||||
reqwest = { version = "0.11.11", features=["cookies", "json", "native-tls"] }
|
||||
|
|
|
@ -37,7 +37,7 @@ use libc::umask;
|
|||
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::config::Configuration;
|
||||
use kanidm::crypto::setup_tls;
|
||||
use kanidm::idm::server::{IdmServer, IdmServerDelayed};
|
||||
use kanidm::interval::IntervalActor;
|
||||
|
@ -45,11 +45,9 @@ 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::messages::{AccountChangeMessage, MessageStatus};
|
||||
use kanidm_proto::v1::OperationError;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
// === internal setup helpers
|
||||
|
@ -483,42 +481,6 @@ 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,
|
||||
|
@ -562,26 +524,17 @@ pub fn recover_account_core(config: &Configuration, name: &str) {
|
|||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
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"),
|
||||
}
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
AccountChangeMessage {
|
||||
output_mode: config.output_mode,
|
||||
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<(), ()> {
|
||||
|
|
|
@ -113,7 +113,7 @@ impl Component for PwModalApp {
|
|||
type Message = Msg;
|
||||
type Properties = ModalProps;
|
||||
|
||||
fn create(ctx: &Context<Self>) -> Self {
|
||||
fn create(_ctx: &Context<Self>) -> Self {
|
||||
console::log!("pw modal create");
|
||||
|
||||
PwModalApp {
|
||||
|
|
|
@ -339,10 +339,11 @@ impl CredentialResetApp {
|
|||
uuid: _,
|
||||
claims: _,
|
||||
type_:
|
||||
// TODO: review this and find out why we aren't using these variables
|
||||
CredentialDetailType::PasswordMfa(
|
||||
totp_set,
|
||||
security_key_labels,
|
||||
backup_codes_remaining,
|
||||
_totp_set,
|
||||
_security_key_labels,
|
||||
_backup_codes_remaining,
|
||||
),
|
||||
}) => {
|
||||
html! {
|
||||
|
|
|
@ -28,6 +28,7 @@ enum State {
|
|||
Consent {
|
||||
token: String,
|
||||
client_name: String,
|
||||
#[allow(dead_code)]
|
||||
scopes: Vec<String>,
|
||||
pii_scopes: Vec<String>,
|
||||
consent_token: String,
|
||||
|
|
|
@ -2,7 +2,7 @@ use gloo::console;
|
|||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
pub use web_sys::InputEvent;
|
||||
use web_sys::{Document, Event, HtmlButtonElement, HtmlElement, HtmlInputElement, Window};
|
||||
use web_sys::{Document, Event, /*HtmlButtonElement,*/ HtmlElement, HtmlInputElement, Window};
|
||||
|
||||
pub fn window() -> Window {
|
||||
web_sys::window().expect_throw("Unable to retrieve window")
|
||||
|
@ -37,23 +37,23 @@ pub fn get_value_from_input_event(e: InputEvent) -> String {
|
|||
target.value()
|
||||
}
|
||||
|
||||
pub fn get_element_by_id(id: &str) -> Option<HtmlElement> {
|
||||
document()
|
||||
.get_element_by_id(id)
|
||||
.and_then(|element| element.dyn_into::<web_sys::HtmlElement>().ok())
|
||||
}
|
||||
// pub fn get_element_by_id(id: &str) -> Option<HtmlElement> {
|
||||
// document()
|
||||
// .get_element_by_id(id)
|
||||
// .and_then(|element| element.dyn_into::<web_sys::HtmlElement>().ok())
|
||||
// }
|
||||
|
||||
pub fn get_buttonelement_by_id(id: &str) -> Option<HtmlButtonElement> {
|
||||
document()
|
||||
.get_element_by_id(id)
|
||||
.and_then(|element| element.dyn_into::<web_sys::HtmlButtonElement>().ok())
|
||||
}
|
||||
// pub fn get_buttonelement_by_id(id: &str) -> Option<HtmlButtonElement> {
|
||||
// document()
|
||||
// .get_element_by_id(id)
|
||||
// .and_then(|element| element.dyn_into::<web_sys::HtmlButtonElement>().ok())
|
||||
// }
|
||||
|
||||
pub fn get_inputelement_by_id(id: &str) -> Option<HtmlInputElement> {
|
||||
document()
|
||||
.get_element_by_id(id)
|
||||
.and_then(|element| element.dyn_into::<web_sys::HtmlInputElement>().ok())
|
||||
}
|
||||
// pub fn get_inputelement_by_id(id: &str) -> Option<HtmlInputElement> {
|
||||
// document()
|
||||
// .get_element_by_id(id)
|
||||
// .and_then(|element| element.dyn_into::<web_sys::HtmlInputElement>().ok())
|
||||
// }
|
||||
|
||||
pub fn get_value_from_element_id(id: &str) -> Option<String> {
|
||||
document()
|
||||
|
|
|
@ -147,7 +147,8 @@ impl Component for SecurityApp {
|
|||
<button type="button" class="btn btn-primary"
|
||||
disabled={ !submit_enabled }
|
||||
onclick={
|
||||
ctx.link().callback(|e| {
|
||||
// TODO: figure out if we need the e here? :)
|
||||
ctx.link().callback(|_e| {
|
||||
Msg::RequestCredentialUpdate
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue