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 std::collections::BTreeSet as Set;
use std::fs::{metadata, File, Metadata};
use std::io::ErrorKind;
use std::io::Read;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
@ -151,10 +152,26 @@ impl KanidmClientBuilder {
let mut f = match File::open(&config_path) {
Ok(f) => f,
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!(
"Unable to open config file {:#?} [{:?}], skipping ...",
&config_path, e
);
}
};
return Ok(self);
}
};

View file

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

View file

@ -36,13 +36,18 @@ impl SelfOpt {
let client = copt.to_client();
match client.whoami() {
Ok(o_ent) => match o_ent {
Ok(o_ent) => {
match o_ent {
Some((ent, uat)) => {
debug!("{:?}", ent);
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),
}
}
@ -53,13 +58,13 @@ impl SelfOpt {
let password = match rpassword::prompt_password_stderr("Enter new password: ") {
Ok(p) => p,
Err(e) => {
eprintln!("Error -> {:?}", e);
error!("Error -> {:?}", e);
return;
}
};
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 {
return Some(password);
} else {
eprintln!("Passwords do not match");
error!("Passwords do not match");
}
}
None

View file

@ -4,6 +4,7 @@ use kanidm_proto::v1::{AuthAllowed, AuthResponse, AuthState};
use libc::umask;
use std::collections::BTreeMap;
use std::fs::{create_dir, File};
use std::io::ErrorKind;
use std::io::{self, BufReader, BufWriter};
use std::path::PathBuf;
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());
if !token_path.exists() {
debug!(
"Token path {} does not exist, assuming empty ... ",
"Token cache file path {:?} does not exist, returning an empty token store.",
TOKEN_PATH
);
return Ok(BTreeMap::new());
@ -26,6 +27,17 @@ pub fn read_tokens() -> Result<BTreeMap<String, String>, ()> {
let file = match File::open(&token_path) {
Ok(f) => f,
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!(
"Cannot read tokens from {} due to error: {:?} ... continuing.",
TOKEN_PATH, e
@ -33,6 +45,8 @@ pub fn read_tokens() -> Result<BTreeMap<String, String>, ()> {
return Ok(BTreeMap::new());
}
};
}
};
let reader = BufReader::new(file);
// Else try to read

View file

@ -153,6 +153,12 @@ pub(crate) async fn create_ldap_server(
opt_tls_params: Option<SslAcceptorBuilder>,
qe_r_ref: &'static QueryServerReadV1,
) -> 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| {
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.
//!
//! 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
//! with no ordering. An entry has many avas. A pseudo example, minus schema and typing:
//! 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:
//!
//! ```
//! /*
//! ```text
//! Entry {
//! "name": ["william"],
//! "uuid": ["..."],
//! "mail": ["maila@example.com", "mailb@example.com"],
//! }
//! */
//! };
//! ```
//!
//! There are three rules for entries:
//! * Must have an ava for UUID containing a single value.
//! * Any ava with zero values will be removed.
//! * Avas are stored with no sorting.
//! * Must have an AVA for UUID containing a single value.
//! * Any AVA with zero values will be removed.
//! * AVAs are stored with no sorting.
//!
//! For more, see the [`Entry`] type.
//!
@ -56,7 +54,7 @@ use uuid::Uuid;
// use std::str::FromStr;
// make a trait entry for everything to adhere to?
// * How to get indexs out?
// * How to get indexes out?
// * How to track pending diffs?
// 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
/// 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.
/// 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
/// `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
/// 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`,
/// 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
/// 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.
//! 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
//! avas must uphold to be considered true.
//! A filter is a logical statement of properties that an [`Entry`] and its
//! AVAs must uphold to be considered true.
//!
//! [`Filter`]: struct.Filter.html
//! [`Entry`]: ../entry/struct.Entry.html

View file

@ -431,8 +431,6 @@ fn ldap_domain_to_dc(input: &str) -> String {
output.push_str(dc);
#[allow(clippy::single_char_add_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 ','
output.pop();