User feedback improvements, also handling a permissions issue (#424)

This commit is contained in:
James Hodgkinson 2021-04-26 11:52:13 +10:00 committed by GitHub
parent f9dd0a78dc
commit 77381c1a2a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 39 deletions

13
.clippy.toml Normal file
View file

@ -0,0 +1,13 @@
########################################################################
# complexity clippy settings
#
# because the clippy devs also acknowledge that complexity is hard to define
# https://github.com/rust-lang/rust-clippy/issues/5418#issuecomment-610054361
#
########################################################################
# default is 7, 8's ok. https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
too-many-arguments-threshold = 8
# default's 250
type-complexity-threshold = 300

View file

@ -15,6 +15,7 @@ use serde_derive::Deserialize;
use serde_json::error::Error as SerdeJsonError; use serde_json::error::Error as SerdeJsonError;
use std::collections::BTreeSet as Set; use std::collections::BTreeSet as Set;
use std::fs::{metadata, File, Metadata}; use std::fs::{metadata, File, Metadata};
use std::io::ErrorKind;
use std::io::Read; use std::io::Read;
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::path::Path; use std::path::Path;
@ -151,10 +152,26 @@ impl KanidmClientBuilder {
let mut f = match File::open(&config_path) { let mut f = match File::open(&config_path) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
match e.kind() {
ErrorKind::NotFound => {
debug!(
"Configuration file {:#?} not found, skipping ...",
&config_path
);
}
ErrorKind::PermissionDenied => {
warn!(
"Permission denied loading configuration file {:#?}, skipping ...",
&config_path
);
}
_ => {
debug!( debug!(
"Unable to open config file {:#?} [{:?}], skipping ...", "Unable to open config file {:#?} [{:?}], skipping ...",
&config_path, e &config_path, e
); );
}
};
return Ok(self); return Ok(self);
} }
}; };

View file

@ -81,7 +81,7 @@ impl CommonOpt {
#[allow(clippy::expect_used)] #[allow(clippy::expect_used)]
let (f_uname, f_token) = tokens.iter().next().expect("Memory Corruption"); let (f_uname, f_token) = tokens.iter().next().expect("Memory Corruption");
// else pick the first token // else pick the first token
info!("Authenticated as {}", f_uname); info!("Using cached token for name {}", f_uname);
f_token.clone() f_token.clone()
} else { } else {
// Unable to select // Unable to select

View file

@ -36,13 +36,18 @@ impl SelfOpt {
let client = copt.to_client(); let client = copt.to_client();
match client.whoami() { match client.whoami() {
Ok(o_ent) => match o_ent { Ok(o_ent) => {
match o_ent {
Some((ent, uat)) => { Some((ent, uat)) => {
debug!("{:?}", ent); debug!("{:?}", ent);
println!("{}", uat); println!("{}", uat);
} }
None => println!("Unauthenticated"), None => {
}, error!("Authentication with cached token failed, can't query information.");
// TODO: remove token when we know it's not valid
}
}
}
Err(e) => println!("Error: {:?}", e), Err(e) => println!("Error: {:?}", e),
} }
} }
@ -53,13 +58,13 @@ impl SelfOpt {
let password = match rpassword::prompt_password_stderr("Enter new password: ") { let password = match rpassword::prompt_password_stderr("Enter new password: ") {
Ok(p) => p, Ok(p) => p,
Err(e) => { Err(e) => {
eprintln!("Error -> {:?}", e); error!("Error -> {:?}", e);
return; return;
} }
}; };
if let Err(e) = client.idm_account_set_password(password) { if let Err(e) = client.idm_account_set_password(password) {
eprintln!("Error -> {:?}", e); error!("Error -> {:?}", e);
} }
} }
} }
@ -106,7 +111,7 @@ pub(crate) fn password_prompt(prompt: &str) -> Option<String> {
if password == password_confirm { if password == password_confirm {
return Some(password); return Some(password);
} else { } else {
eprintln!("Passwords do not match"); error!("Passwords do not match");
} }
} }
None None

View file

@ -4,6 +4,7 @@ use kanidm_proto::v1::{AuthAllowed, AuthResponse, AuthState};
use libc::umask; use libc::umask;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fs::{create_dir, File}; use std::fs::{create_dir, File};
use std::io::ErrorKind;
use std::io::{self, BufReader, BufWriter}; use std::io::{self, BufReader, BufWriter};
use std::path::PathBuf; use std::path::PathBuf;
use webauthn_authenticator_rs::{u2fhid::U2FHid, RequestChallengeResponse, WebauthnAuthenticator}; use webauthn_authenticator_rs::{u2fhid::U2FHid, RequestChallengeResponse, WebauthnAuthenticator};
@ -15,7 +16,7 @@ pub fn read_tokens() -> Result<BTreeMap<String, String>, ()> {
let token_path = PathBuf::from(shellexpand::tilde(TOKEN_PATH).into_owned()); let token_path = PathBuf::from(shellexpand::tilde(TOKEN_PATH).into_owned());
if !token_path.exists() { if !token_path.exists() {
debug!( debug!(
"Token path {} does not exist, assuming empty ... ", "Token cache file path {:?} does not exist, returning an empty token store.",
TOKEN_PATH TOKEN_PATH
); );
return Ok(BTreeMap::new()); return Ok(BTreeMap::new());
@ -26,6 +27,17 @@ pub fn read_tokens() -> Result<BTreeMap<String, String>, ()> {
let file = match File::open(&token_path) { let file = match File::open(&token_path) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
match e.kind() {
ErrorKind::PermissionDenied => {
// we bail here because you won't be able to write them back...
error!(
"Permission denied reading token store file {:?}",
&token_path
);
return Err(());
}
// other errors are OK to continue past
_ => {
warn!( warn!(
"Cannot read tokens from {} due to error: {:?} ... continuing.", "Cannot read tokens from {} due to error: {:?} ... continuing.",
TOKEN_PATH, e TOKEN_PATH, e
@ -33,6 +45,8 @@ pub fn read_tokens() -> Result<BTreeMap<String, String>, ()> {
return Ok(BTreeMap::new()); return Ok(BTreeMap::new());
} }
}; };
}
};
let reader = BufReader::new(file); let reader = BufReader::new(file);
// Else try to read // Else try to read

View file

@ -153,6 +153,12 @@ pub(crate) async fn create_ldap_server(
opt_tls_params: Option<SslAcceptorBuilder>, opt_tls_params: Option<SslAcceptorBuilder>,
qe_r_ref: &'static QueryServerReadV1, qe_r_ref: &'static QueryServerReadV1,
) -> Result<(), ()> { ) -> Result<(), ()> {
if address.starts_with(":::") {
// takes :::xxxx to xxxx
let port = address.replacen(":::", "", 1);
eprintln!("Address '{}' looks like an attempt to wildcard bind with IPv6 on port {} - please try using ldapbindaddress = '[::]:{}'", address, port, port);
};
let addr = net::SocketAddr::from_str(address).map_err(|e| { let addr = net::SocketAddr::from_str(address).map_err(|e| {
eprintln!("Could not parse ldap server address {} -> {:?}", address, e); eprintln!("Could not parse ldap server address {} -> {:?}", address, e);
})?; })?;

View file

@ -2,23 +2,21 @@
//! concepts along with [`filter`]s and [`schema`] that everything else builds upon. //! concepts along with [`filter`]s and [`schema`] that everything else builds upon.
//! //!
//! An [`Entry`] is a collection of attribute-value sets. There are sometimes called attribute value //! An [`Entry`] is a collection of attribute-value sets. There are sometimes called attribute value
//! assertions, or avas. The attribute is a "key" and it holds 1 to infinitite associtade values //! assertions, or AVAs. The attribute is a "key" and it holds 1 to infinite associated values
//! with no ordering. An entry has many avas. A pseudo example, minus schema and typing: //! with no ordering. An entry has many AVAs. A pseudo example, minus schema and typing:
//! //!
//! ``` //! ```text
//! /*
//! Entry { //! Entry {
//! "name": ["william"], //! "name": ["william"],
//! "uuid": ["..."], //! "uuid": ["..."],
//! "mail": ["maila@example.com", "mailb@example.com"], //! "mail": ["maila@example.com", "mailb@example.com"],
//! } //! };
//! */
//! ``` //! ```
//! //!
//! There are three rules for entries: //! There are three rules for entries:
//! * Must have an ava for UUID containing a single value. //! * Must have an AVA for UUID containing a single value.
//! * Any ava with zero values will be removed. //! * Any AVA with zero values will be removed.
//! * Avas are stored with no sorting. //! * AVAs are stored with no sorting.
//! //!
//! For more, see the [`Entry`] type. //! For more, see the [`Entry`] type.
//! //!
@ -56,7 +54,7 @@ use uuid::Uuid;
// use std::str::FromStr; // use std::str::FromStr;
// make a trait entry for everything to adhere to? // make a trait entry for everything to adhere to?
// * How to get indexs out? // * How to get indexes out?
// * How to track pending diffs? // * How to track pending diffs?
// Entry is really similar to serde Value, but limits the possibility // Entry is really similar to serde Value, but limits the possibility
@ -172,17 +170,17 @@ fn compare_attrs(left: &Map<AttrString, Set<Value>>, right: &Map<AttrString, Set
/// Entry is the core data storage type of the server. Almost every aspect of the server is /// Entry is the core data storage type of the server. Almost every aspect of the server is
/// designed to read, handle and manipulate entries. /// designed to read, handle and manipulate entries.
/// ///
/// Entries store attribute value assertions, or ava. These are sets of key-values. /// Entries store attribute value assertions, or AVA. These are sets of key-values.
/// ///
/// Entries have a lifecycle within a single operation, and as part of replication. /// Entries have a lifecycle within a single operation, and as part of replication.
/// The lifecycle for operations is defined through state and valid types. Each entry has a pair /// The lifecycle for operations is defined through state and valid types. Each entry has a pair
/// Of these types at anytime. The first is the ava [`schema`] and [`access`] control assertion /// Of these types at anytime. The first is the AVA [`schema`] and [`access`] control assertion
/// state. This is represented by the type `VALID` as one of `EntryValid`, `EntryInvalid` or /// state. This is represented by the type `VALID` as one of `EntryValid`, `EntryInvalid` or
/// `EntryReduced`. Every entry starts as `EntryInvalid`, and when checked by the schema for /// `EntryReduced`. Every entry starts as `EntryInvalid`, and when checked by the schema for
/// correctness, transitions to `EntryValid`. While an entry is `EntryValid` it can not be /// correctness, transitions to `EntryValid`. While an entry is `EntryValid` it can not be
/// altered - you must invalidate it to `EntryInvalid`, then modify, then check again. /// altered - you must invalidate it to `EntryInvalid`, then modify, then check again.
/// An entry that has had access controls applied moves from `EntryValid` to `EntryReduced`, /// An entry that has had access controls applied moves from `EntryValid` to `EntryReduced`,
/// to show that the avas have reduced to the valid read set of the current [`event`] user. /// to show that the AVAs have reduced to the valid read set of the current [`event`] user.
/// ///
/// The second type of `STATE` represents the database commit state and internal db ID's. A /// The second type of `STATE` represents the database commit state and internal db ID's. A
/// new entry that has never been committed is `EntryNew`, but an entry that has been retrieved /// new entry that has never been committed is `EntryNew`, but an entry that has been retrieved

View file

@ -1,9 +1,9 @@
//! [`Filter`]s are one of the three foundational concepts of the design in kanidm. //! [`Filter`]s are one of the three foundational concepts of the design in kanidm.
//! They are used in nearly every aspect ofthe server to provide searching of //! They are used in nearly every aspect of the server to provide searching of
//! datasets, and assertion of entry properties. //! datasets and assertion of entry properties.
//! //!
//! A filter is a logical statement of properties that an [`Entry`] and it's //! A filter is a logical statement of properties that an [`Entry`] and its
//! avas must uphold to be considered true. //! AVAs must uphold to be considered true.
//! //!
//! [`Filter`]: struct.Filter.html //! [`Filter`]: struct.Filter.html
//! [`Entry`]: ../entry/struct.Entry.html //! [`Entry`]: ../entry/struct.Entry.html

View file

@ -431,8 +431,6 @@ fn ldap_domain_to_dc(input: &str) -> String {
output.push_str(dc); output.push_str(dc);
#[allow(clippy::single_char_add_str)] #[allow(clippy::single_char_add_str)]
output.push_str(","); output.push_str(",");
// Can't use concat as it's evalled at compile, not run time.
// output.push_str(concat!("dc=", dc, ","));
}); });
// Remove the last ',' // Remove the last ','
output.pop(); output.pop();