mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
Making clippy happy (#420)
This commit is contained in:
parent
01e9aa982d
commit
de431451f4
|
@ -101,7 +101,7 @@ An improved design will have Credential become an enum representing the valid au
|
|||
Anonymous,
|
||||
Password(),
|
||||
GeneratedPassword(),
|
||||
PasswordMFA(),
|
||||
PasswordMfa(),
|
||||
PasswordWebauthn(),
|
||||
Webauthn(),
|
||||
WebauthnVerified(),
|
||||
|
@ -160,7 +160,7 @@ the correct way to handle these).
|
|||
Anonymous,
|
||||
Password,
|
||||
// This covers PasswordWebauthn as well.
|
||||
PasswordMFA,
|
||||
PasswordMfa,
|
||||
Webauthn,
|
||||
WebauthnVerified,
|
||||
PasswordWebauthnVerified,
|
||||
|
|
|
@ -24,7 +24,7 @@ An example of this display for the CLI:
|
|||
expire_at: <date>
|
||||
|
||||
- <credential_id>
|
||||
type: Password|APIKey|PasswordMFA
|
||||
type: Password|APIKey|PasswordMfa
|
||||
locked: true|false
|
||||
valid_from: <date>
|
||||
expire_at: <date>
|
||||
|
|
|
@ -368,7 +368,7 @@ impl KanidmAsyncClient {
|
|||
|
||||
pub async fn auth_step_totp(&mut self, totp: u32) -> Result<AuthResponse, ClientError> {
|
||||
let auth_req = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::TOTP(totp)),
|
||||
step: AuthStep::Cred(AuthCredential::Totp(totp)),
|
||||
};
|
||||
let r: Result<AuthResponse, _> = self.perform_auth_post_request("/v1/auth", auth_req).await;
|
||||
|
||||
|
@ -463,17 +463,17 @@ impl KanidmAsyncClient {
|
|||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !mechs.contains(&AuthMech::PasswordMFA) {
|
||||
debug!("PasswordMFA mech not presented");
|
||||
if !mechs.contains(&AuthMech::PasswordMfa) {
|
||||
debug!("PasswordMfa mech not presented");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let state = match self.auth_step_begin(AuthMech::PasswordMFA).await {
|
||||
let state = match self.auth_step_begin(AuthMech::PasswordMfa).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !state.contains(&AuthAllowed::TOTP) {
|
||||
if !state.contains(&AuthAllowed::Totp) {
|
||||
debug!("TOTP step not offered.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
@ -818,8 +818,8 @@ impl KanidmAsyncClient {
|
|||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<(Uuid, TOTPSecret), ClientError> {
|
||||
let r = SetCredentialRequest::TOTPGenerate(label.to_string());
|
||||
) -> Result<(Uuid, TotpSecret), ClientError> {
|
||||
let r = SetCredentialRequest::TotpGenerate(label.to_string());
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
|
@ -827,7 +827,7 @@ impl KanidmAsyncClient {
|
|||
)
|
||||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::TOTPCheck(u, s)) => Ok((u, s)),
|
||||
Ok(SetCredentialResponse::TotpCheck(u, s)) => Ok((u, s)),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
@ -840,7 +840,7 @@ impl KanidmAsyncClient {
|
|||
otp: u32,
|
||||
session: Uuid,
|
||||
) -> Result<bool, ClientError> {
|
||||
let r = SetCredentialRequest::TOTPVerify(session, otp);
|
||||
let r = SetCredentialRequest::TotpVerify(session, otp);
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
|
@ -849,7 +849,7 @@ impl KanidmAsyncClient {
|
|||
.await;
|
||||
match res {
|
||||
Ok(SetCredentialResponse::Success) => Ok(true),
|
||||
Ok(SetCredentialResponse::TOTPCheck(u, s)) => Err(ClientError::TOTPVerifyFailed(u, s)),
|
||||
Ok(SetCredentialResponse::TotpCheck(u, s)) => Err(ClientError::TotpVerifyFailed(u, s)),
|
||||
Ok(_) => Err(ClientError::EmptyResponse),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
@ -859,7 +859,7 @@ impl KanidmAsyncClient {
|
|||
&self,
|
||||
id: &str,
|
||||
) -> Result<bool, ClientError> {
|
||||
let r = SetCredentialRequest::TOTPRemove;
|
||||
let r = SetCredentialRequest::TotpRemove;
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
|
|
|
@ -45,7 +45,7 @@ pub enum ClientError {
|
|||
Transport(reqwest::Error),
|
||||
AuthenticationFailed,
|
||||
EmptyResponse,
|
||||
TOTPVerifyFailed(Uuid, TOTPSecret),
|
||||
TotpVerifyFailed(Uuid, TotpSecret),
|
||||
JSONDecode(reqwest::Error, String),
|
||||
JSONEncode(SerdeJsonError),
|
||||
SystemError,
|
||||
|
@ -555,7 +555,7 @@ impl KanidmClient {
|
|||
&self,
|
||||
id: &str,
|
||||
label: &str,
|
||||
) -> Result<(Uuid, TOTPSecret), ClientError> {
|
||||
) -> Result<(Uuid, TotpSecret), ClientError> {
|
||||
tokio_block_on(
|
||||
self.asclient
|
||||
.idm_account_primary_credential_generate_totp(id, label),
|
||||
|
|
|
@ -10,6 +10,7 @@ use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
|||
use async_std::task;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
pub const ADMIN_TEST_USER: &str = "admin";
|
||||
pub const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
||||
static PORT_ALLOC: AtomicU16 = AtomicU16::new(18080);
|
||||
|
||||
|
@ -47,6 +48,7 @@ pub fn run_test(test_fn: fn(KanidmClient) -> ()) {
|
|||
};
|
||||
|
||||
let int_config = Box::new(IntegrationTestConfig {
|
||||
admin_user: ADMIN_TEST_USER.to_string(),
|
||||
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
||||
});
|
||||
|
||||
|
@ -57,9 +59,9 @@ pub fn run_test(test_fn: fn(KanidmClient) -> ()) {
|
|||
config.integration_test_config = Some(int_config);
|
||||
config.log_level = Some(LogLevel::Quiet as u32);
|
||||
// config.log_level = Some(LogLevel::Verbose as u32);
|
||||
// config.log_level = Some(LogLevel::FullTrace as u32);
|
||||
config.threads = 1;
|
||||
|
||||
// config.log_level = Some(LogLevel::FullTrace as u32);
|
||||
let t_handle = thread::spawn(move || {
|
||||
// Spawn a thread for the test runner, this should have a unique
|
||||
// port....
|
||||
|
|
|
@ -5,7 +5,7 @@ use kanidm_client::KanidmClient;
|
|||
use kanidm_proto::v1::{Filter, Modify, ModifyList};
|
||||
|
||||
mod common;
|
||||
use crate::common::{run_test, ADMIN_TEST_PASSWORD};
|
||||
use crate::common::{run_test, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
|
||||
|
||||
static USER_READABLE_ATTRS: [&str; 9] = [
|
||||
"name",
|
||||
|
@ -113,7 +113,7 @@ fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: &str) -> Option<boo
|
|||
fn add_all_attrs(mut rsclient: &mut KanidmClient, id: &str, group_name: &str) {
|
||||
// Extend with posix attrs to test read attr: gidnumber and loginshell
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
||||
.unwrap();
|
||||
rsclient
|
||||
.idm_account_unix_extend(id, None, Some(&"/bin/bash"))
|
||||
|
@ -136,7 +136,7 @@ fn add_all_attrs(mut rsclient: &mut KanidmClient, id: &str, group_name: &str) {
|
|||
.idm_account_radius_credential_regenerate(id)
|
||||
.unwrap();
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -155,10 +155,13 @@ fn create_user_with_all_attrs(
|
|||
|
||||
fn login_account(rsclient: &mut KanidmClient, id: &str) -> () {
|
||||
rsclient
|
||||
.idm_group_add_members("idm_people_account_password_import_priv", &["admin"])
|
||||
.idm_group_add_members(
|
||||
"idm_people_account_password_import_priv",
|
||||
&[ADMIN_TEST_USER],
|
||||
)
|
||||
.unwrap();
|
||||
rsclient
|
||||
.idm_group_add_members("idm_people_extend_priv", &["admin"])
|
||||
.idm_group_add_members("idm_people_extend_priv", &[ADMIN_TEST_USER])
|
||||
.unwrap();
|
||||
|
||||
rsclient
|
||||
|
@ -225,7 +228,7 @@ fn test_modify_group(rsclient: &KanidmClient, group_names: &[&str], is_modificab
|
|||
fn test_default_entries_rbac_users() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
|
||||
create_user_with_all_attrs(&mut rsclient, "self_account", Some("self_group"));
|
||||
|
@ -263,7 +266,7 @@ fn test_default_entries_rbac_users() {
|
|||
fn test_default_entries_rbac_account_managers() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
|
||||
create_user(&rsclient, "account_manager", "idm_account_manage_priv");
|
||||
|
@ -295,7 +298,7 @@ fn test_default_entries_rbac_account_managers() {
|
|||
fn test_default_entries_rbac_group_managers() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
|
||||
create_user(&rsclient, "group_manager", "idm_group_manage_priv");
|
||||
|
@ -336,7 +339,7 @@ fn test_default_entries_rbac_group_managers() {
|
|||
fn test_default_entries_rbac_admins_access_control_entries() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
static ACP_COMMON_ATTRS: [&str; 4] =
|
||||
["name", "description", "acp_receiver", "acp_targetscope"];
|
||||
|
@ -384,7 +387,7 @@ fn test_default_entries_rbac_admins_access_control_entries() {
|
|||
fn test_default_entries_rbac_admins_schema_entries() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
let default_classnames: HashSet<String> = [
|
||||
"access_control_create",
|
||||
|
@ -495,7 +498,7 @@ fn test_default_entries_rbac_admins_schema_entries() {
|
|||
fn test_default_entries_rbac_admins_group_entries() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
create_user(&rsclient, "test", "test_group");
|
||||
|
||||
|
@ -511,7 +514,7 @@ fn test_default_entries_rbac_admins_group_entries() {
|
|||
fn test_default_entries_rbac_admins_ha_accounts() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
|
||||
static MAIN_ATTRS: [&str; 3] = ["name", "displayname", "primary_credential"];
|
||||
|
@ -524,7 +527,7 @@ fn test_default_entries_rbac_admins_ha_accounts() {
|
|||
fn test_default_entries_rbac_admins_recycle_accounts() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
create_user(&rsclient, "test", "test_group");
|
||||
|
||||
|
@ -543,7 +546,7 @@ fn test_default_entries_rbac_admins_recycle_accounts() {
|
|||
fn test_default_entries_rbac_people_managers() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
|
||||
create_user(&rsclient, "read_people_manager", "idm_people_read_priv");
|
||||
|
@ -564,7 +567,7 @@ fn test_default_entries_rbac_people_managers() {
|
|||
|
||||
let _ = rsclient.logout();
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
create_user(&rsclient, "write_people_manager", "idm_people_write_priv");
|
||||
login_account(&mut rsclient, "write_people_manager");
|
||||
|
@ -582,7 +585,7 @@ fn test_default_entries_rbac_people_managers() {
|
|||
fn test_default_entries_rbac_anonymous_entry() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
create_user_with_all_attrs(&mut rsclient, "test", Some("test_group"));
|
||||
rsclient
|
||||
|
@ -607,7 +610,7 @@ fn test_default_entries_rbac_anonymous_entry() {
|
|||
fn test_default_entries_rbac_radius_servers() {
|
||||
run_test(|mut rsclient: KanidmClient| {
|
||||
rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.unwrap();
|
||||
create_user(&rsclient, "radius_server", "idm_radius_servers");
|
||||
create_user_with_all_attrs(&mut rsclient, "test", Some("test_group"));
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::time::SystemTime;
|
|||
|
||||
use log::debug;
|
||||
|
||||
use kanidm::credential::totp::TOTP;
|
||||
use kanidm::credential::totp::Totp;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::v1::{CredentialDetailType, Entry, Filter, Modify, ModifyList};
|
||||
|
||||
|
@ -46,7 +46,7 @@ fn test_server_modify() {
|
|||
run_test(|mut rsclient: KanidmClient| {
|
||||
// Build a self mod.
|
||||
|
||||
let f = Filter::SelfUUID;
|
||||
let f = Filter::SelfUuid;
|
||||
let m = ModifyList::new_list(vec![
|
||||
Modify::Purged("displayname".to_string()),
|
||||
Modify::Present("displayname".to_string(), "test".to_string()),
|
||||
|
@ -761,7 +761,7 @@ fn test_server_rest_totp_auth_lifecycle() {
|
|||
.idm_account_primary_credential_generate_totp("demo_account", "demo")
|
||||
.unwrap();
|
||||
|
||||
let r_tok: TOTP = tok.into();
|
||||
let r_tok: Totp = tok.into();
|
||||
let totp = r_tok
|
||||
.do_totp_duration_from_epoch(
|
||||
&SystemTime::now()
|
||||
|
@ -960,7 +960,7 @@ fn test_server_rest_webauthn_mfa_auth_lifecycle() {
|
|||
.auth_webauthn_complete(auth)
|
||||
.expect("Failed to authenticate");
|
||||
|
||||
// Set a password to cause the state to change to PasswordMFA
|
||||
// Set a password to cause the state to change to PasswordMfa
|
||||
assert!(rsclient
|
||||
.idm_account_primary_credential_set_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
||||
.is_ok());
|
||||
|
|
|
@ -49,7 +49,7 @@ pub enum ConsistencyError {
|
|||
MemberOfInvalid(u64),
|
||||
InvalidAttributeType(String),
|
||||
DuplicateUniqueAttribute(String),
|
||||
InvalidSPN(u64),
|
||||
InvalidSpn(u64),
|
||||
SqliteIntegrityFailure,
|
||||
}
|
||||
|
||||
|
@ -66,23 +66,23 @@ pub enum OperationError {
|
|||
SchemaViolation(SchemaError),
|
||||
Plugin(PluginError),
|
||||
FilterGeneration,
|
||||
FilterUUIDResolution,
|
||||
FilterUuidResolution,
|
||||
InvalidAttributeName(String),
|
||||
InvalidAttribute(String),
|
||||
InvalidDBState,
|
||||
InvalidDbState,
|
||||
InvalidCacheState,
|
||||
InvalidValueState,
|
||||
InvalidEntryID,
|
||||
InvalidEntryId,
|
||||
InvalidRequestState,
|
||||
InvalidState,
|
||||
InvalidEntryState,
|
||||
InvalidUuid,
|
||||
InvalidReplCID,
|
||||
InvalidACPState(String),
|
||||
InvalidAcpState(String),
|
||||
InvalidSchemaState(String),
|
||||
InvalidAccountState(String),
|
||||
BackendEngine,
|
||||
SQLiteError, //(RusqliteError)
|
||||
SqliteError, //(RusqliteError)
|
||||
FsError,
|
||||
SerdeJsonError,
|
||||
SerdeCborError,
|
||||
|
@ -286,7 +286,7 @@ pub enum CredentialDetailType {
|
|||
GeneratedPassword,
|
||||
Webauthn(Vec<String>),
|
||||
/// totp, webauthn
|
||||
PasswordMFA(bool, Vec<String>),
|
||||
PasswordMfa(bool, Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
@ -317,7 +317,7 @@ impl fmt::Display for CredentialDetail {
|
|||
write!(f, "")
|
||||
}
|
||||
}
|
||||
CredentialDetailType::PasswordMFA(totp, labels) => {
|
||||
CredentialDetailType::PasswordMfa(totp, labels) => {
|
||||
writeln!(f, "password: set")?;
|
||||
if *totp {
|
||||
writeln!(f, "totp: enabled")?;
|
||||
|
@ -391,7 +391,7 @@ pub enum Filter {
|
|||
#[serde(alias = "AndNot")]
|
||||
AndNot(Box<Filter>),
|
||||
#[serde(rename = "self", alias = "Self")]
|
||||
SelfUUID,
|
||||
SelfUuid,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
|
@ -496,7 +496,7 @@ impl ModifyRequest {
|
|||
pub enum AuthCredential {
|
||||
Anonymous,
|
||||
Password(String),
|
||||
TOTP(u32),
|
||||
Totp(u32),
|
||||
Webauthn(PublicKeyCredential),
|
||||
}
|
||||
|
||||
|
@ -505,7 +505,7 @@ impl fmt::Debug for AuthCredential {
|
|||
match self {
|
||||
AuthCredential::Anonymous => write!(fmt, "Anonymous"),
|
||||
AuthCredential::Password(_) => write!(fmt, "Password(_)"),
|
||||
AuthCredential::TOTP(_) => write!(fmt, "TOTP(_)"),
|
||||
AuthCredential::Totp(_) => write!(fmt, "TOTP(_)"),
|
||||
AuthCredential::Webauthn(_) => write!(fmt, "Webauthn(_)"),
|
||||
}
|
||||
}
|
||||
|
@ -516,7 +516,7 @@ impl fmt::Debug for AuthCredential {
|
|||
pub enum AuthMech {
|
||||
Anonymous,
|
||||
Password,
|
||||
PasswordMFA,
|
||||
PasswordMfa,
|
||||
Webauthn,
|
||||
// WebauthnVerified,
|
||||
// PasswordWebauthnVerified
|
||||
|
@ -533,7 +533,7 @@ impl fmt::Display for AuthMech {
|
|||
match self {
|
||||
AuthMech::Anonymous => write!(f, "Anonymous (no credentials)"),
|
||||
AuthMech::Password => write!(f, "Passwold Only"),
|
||||
AuthMech::PasswordMFA => write!(f, "TOTP or Token, and Password"),
|
||||
AuthMech::PasswordMfa => write!(f, "TOTP or Token, and Password"),
|
||||
AuthMech::Webauthn => write!(f, "Webauthn Token"),
|
||||
}
|
||||
}
|
||||
|
@ -565,7 +565,7 @@ pub struct AuthRequest {
|
|||
pub enum AuthAllowed {
|
||||
Anonymous,
|
||||
Password,
|
||||
TOTP,
|
||||
Totp,
|
||||
Webauthn(RequestChallengeResponse),
|
||||
}
|
||||
|
||||
|
@ -588,8 +588,8 @@ impl Ord for AuthAllowed {
|
|||
(_, AuthAllowed::Anonymous) => Ordering::Greater,
|
||||
(AuthAllowed::Password, _) => Ordering::Less,
|
||||
(_, AuthAllowed::Password) => Ordering::Greater,
|
||||
(AuthAllowed::TOTP, _) => Ordering::Less,
|
||||
(_, AuthAllowed::TOTP) => Ordering::Greater,
|
||||
(AuthAllowed::Totp, _) => Ordering::Less,
|
||||
(_, AuthAllowed::Totp) => Ordering::Greater,
|
||||
(AuthAllowed::Webauthn(_), _) => Ordering::Less,
|
||||
// Unreachable
|
||||
// (_, AuthAllowed::Webauthn(_)) => Ordering::Greater,
|
||||
|
@ -609,7 +609,7 @@ impl fmt::Display for AuthAllowed {
|
|||
match self {
|
||||
AuthAllowed::Anonymous => write!(f, "Anonymous (no credentials)"),
|
||||
AuthAllowed::Password => write!(f, "Password"),
|
||||
AuthAllowed::TOTP => write!(f, "TOTP"),
|
||||
AuthAllowed::Totp => write!(f, "TOTP"),
|
||||
AuthAllowed::Webauthn(_) => write!(f, "Webauthn Token"),
|
||||
}
|
||||
}
|
||||
|
@ -641,9 +641,9 @@ pub struct AuthResponse {
|
|||
pub enum SetCredentialRequest {
|
||||
Password(String),
|
||||
GeneratePassword,
|
||||
TOTPGenerate(String),
|
||||
TOTPVerify(Uuid, u32),
|
||||
TOTPRemove,
|
||||
TotpGenerate(String),
|
||||
TotpVerify(Uuid, u32),
|
||||
TotpRemove,
|
||||
// Start the rego.
|
||||
WebauthnBegin(String),
|
||||
// Finish it.
|
||||
|
@ -654,32 +654,32 @@ pub enum SetCredentialRequest {
|
|||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum TOTPAlgo {
|
||||
pub enum TotpAlgo {
|
||||
Sha1,
|
||||
Sha256,
|
||||
Sha512,
|
||||
}
|
||||
|
||||
impl fmt::Display for TOTPAlgo {
|
||||
impl fmt::Display for TotpAlgo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
TOTPAlgo::Sha1 => write!(f, "SHA1"),
|
||||
TOTPAlgo::Sha256 => write!(f, "SHA256"),
|
||||
TOTPAlgo::Sha512 => write!(f, "SHA512"),
|
||||
TotpAlgo::Sha1 => write!(f, "SHA1"),
|
||||
TotpAlgo::Sha256 => write!(f, "SHA256"),
|
||||
TotpAlgo::Sha512 => write!(f, "SHA512"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TOTPSecret {
|
||||
pub struct TotpSecret {
|
||||
pub accountname: String,
|
||||
pub issuer: String,
|
||||
pub secret: Vec<u8>,
|
||||
pub algo: TOTPAlgo,
|
||||
pub algo: TotpAlgo,
|
||||
pub step: u64,
|
||||
}
|
||||
|
||||
impl TOTPSecret {
|
||||
impl TotpSecret {
|
||||
/// https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
pub fn to_uri(&self) -> String {
|
||||
// label = accountname / issuer (“:” / “%3A”) *”%20” accountname
|
||||
|
@ -714,7 +714,7 @@ impl TOTPSecret {
|
|||
pub enum SetCredentialResponse {
|
||||
Success,
|
||||
Token(String),
|
||||
TOTPCheck(Uuid, TOTPSecret),
|
||||
TotpCheck(Uuid, TotpSecret),
|
||||
WebauthnCreateChallenge(Uuid, CreationChallengeResponse),
|
||||
}
|
||||
|
||||
|
@ -789,7 +789,7 @@ impl SingleStringRequest {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::v1::Filter as ProtoFilter;
|
||||
use crate::v1::{TOTPAlgo, TOTPSecret};
|
||||
use crate::v1::{TotpAlgo, TotpSecret};
|
||||
|
||||
#[test]
|
||||
fn test_protofilter_simple() {
|
||||
|
@ -800,23 +800,23 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn totp_to_string() {
|
||||
let totp = TOTPSecret {
|
||||
let totp = TotpSecret {
|
||||
accountname: "william".to_string(),
|
||||
issuer: "blackhats".to_string(),
|
||||
secret: vec![0xaa, 0xbb, 0xcc, 0xdd],
|
||||
step: 30,
|
||||
algo: TOTPAlgo::Sha256,
|
||||
algo: TotpAlgo::Sha256,
|
||||
};
|
||||
let s = totp.to_uri();
|
||||
assert!(s == "otpauth://totp/blackhats:william?secret=VK54ZXI&issuer=blackhats&algorithm=SHA256&digits=6&period=30");
|
||||
|
||||
// check that invalid issuer/accounts are cleaned up.
|
||||
let totp = TOTPSecret {
|
||||
let totp = TotpSecret {
|
||||
accountname: "william:%3A".to_string(),
|
||||
issuer: "blackhats australia".to_string(),
|
||||
secret: vec![0xaa, 0xbb, 0xcc, 0xdd],
|
||||
step: 30,
|
||||
algo: TOTPAlgo::Sha256,
|
||||
algo: TotpAlgo::Sha256,
|
||||
};
|
||||
let s = totp.to_uri();
|
||||
assert!(s == "otpauth://totp/blackhats%20australia:william?secret=VK54ZXI&issuer=blackhats%20australia&algorithm=SHA256&digits=6&period=30");
|
||||
|
|
|
@ -18,7 +18,7 @@ impl AccountOpt {
|
|||
AccountCredential::RegisterWebauthn(acs) => acs.copt.debug,
|
||||
AccountCredential::RemoveWebauthn(acs) => acs.copt.debug,
|
||||
AccountCredential::RegisterTotp(acs) => acs.copt.debug,
|
||||
AccountCredential::RemoveTOTP(acs) => acs.copt.debug,
|
||||
AccountCredential::RemoveTotp(acs) => acs.copt.debug,
|
||||
AccountCredential::Status(acs) => acs.copt.debug,
|
||||
},
|
||||
AccountOpt::Radius(acopt) => match acopt {
|
||||
|
@ -213,7 +213,7 @@ impl AccountOpt {
|
|||
}
|
||||
}
|
||||
}
|
||||
AccountCredential::RemoveTOTP(acsopt) => {
|
||||
AccountCredential::RemoveTotp(acsopt) => {
|
||||
let client = acsopt.copt.to_client();
|
||||
match client.idm_account_primary_credential_remove_totp(
|
||||
acsopt.aopts.account_id.as_str(),
|
||||
|
|
|
@ -256,7 +256,7 @@ impl LoginOpt {
|
|||
let res = match choice {
|
||||
AuthAllowed::Anonymous => client.auth_step_anonymous(),
|
||||
AuthAllowed::Password => self.do_password(&mut client),
|
||||
AuthAllowed::TOTP => self.do_totp(&mut client),
|
||||
AuthAllowed::Totp => self.do_totp(&mut client),
|
||||
AuthAllowed::Webauthn(chal) => self.do_webauthn(&mut client, chal.clone()),
|
||||
};
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ pub enum AccountCredential {
|
|||
RegisterTotp(AccountNamedTagOpt),
|
||||
/// Remove TOTP from the account. If no TOTP exists, no action is taken.
|
||||
#[structopt(name = "remove_totp")]
|
||||
RemoveTOTP(AccountNamedOpt),
|
||||
RemoveTotp(AccountNamedOpt),
|
||||
/// Show the status of the accounts credentials.
|
||||
#[structopt(name = "status")]
|
||||
Status(AccountNamedOpt),
|
||||
|
|
|
@ -21,6 +21,7 @@ use async_std::task;
|
|||
use tokio::sync::mpsc;
|
||||
|
||||
static PORT_ALLOC: AtomicU16 = AtomicU16::new(28080);
|
||||
const ADMIN_TEST_USER: &str = "admin";
|
||||
const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
||||
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
|
||||
const TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test";
|
||||
|
@ -55,6 +56,7 @@ fn run_test(fix_fn: fn(&mut KanidmClient) -> (), test_fn: fn(CacheLayer, KanidmA
|
|||
};
|
||||
|
||||
let int_config = Box::new(IntegrationTestConfig {
|
||||
admin_user: ADMIN_TEST_USER.to_string(),
|
||||
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
||||
});
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ impl AccessControlSearch {
|
|||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_value_pres("class", &CLASS_ACS) {
|
||||
ladmin_error!(audit, "class access_control_search not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_search".to_string(),
|
||||
));
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ impl AccessControlSearch {
|
|||
.get_ava_as_str("acp_search_attr")
|
||||
.ok_or_else(|| {
|
||||
ladmin_error!(audit, "Missing acp_search_attr");
|
||||
OperationError::InvalidACPState("Missing acp_search_attr".to_string())
|
||||
OperationError::InvalidAcpState("Missing acp_search_attr".to_string())
|
||||
})?
|
||||
.map(AttrString::from)
|
||||
.collect();
|
||||
|
@ -123,7 +123,7 @@ impl AccessControlDelete {
|
|||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_value_pres("class", &CLASS_ACD) {
|
||||
ladmin_error!(audit, "class access_control_delete not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_delete".to_string(),
|
||||
));
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ impl AccessControlCreate {
|
|||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_value_pres("class", &CLASS_ACC) {
|
||||
ladmin_error!(audit, "class access_control_create not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_create".to_string(),
|
||||
));
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ impl AccessControlModify {
|
|||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_value_pres("class", &CLASS_ACM) {
|
||||
ladmin_error!(audit, "class access_control_modify not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_modify".to_string(),
|
||||
));
|
||||
}
|
||||
|
@ -304,7 +304,7 @@ impl AccessControlProfile {
|
|||
// Assert we have class access_control_profile
|
||||
if !value.attribute_value_pres("class", &CLASS_ACP) {
|
||||
ladmin_error!(audit, "class access_control_profile not present.");
|
||||
return Err(OperationError::InvalidACPState(
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_profile".to_string(),
|
||||
));
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ impl AccessControlProfile {
|
|||
.get_ava_single_str("name")
|
||||
.ok_or_else(|| {
|
||||
ladmin_error!(audit, "Missing name");
|
||||
OperationError::InvalidACPState("Missing name".to_string())
|
||||
OperationError::InvalidAcpState("Missing name".to_string())
|
||||
})?
|
||||
.to_string();
|
||||
// copy uuid
|
||||
|
@ -326,7 +326,7 @@ impl AccessControlProfile {
|
|||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
ladmin_error!(audit, "Missing acp_receiver");
|
||||
OperationError::InvalidACPState("Missing acp_receiver".to_string())
|
||||
OperationError::InvalidAcpState("Missing acp_receiver".to_string())
|
||||
})?;
|
||||
// targetscope, and turn to real filter
|
||||
let targetscope_f: ProtoFilter = value
|
||||
|
@ -335,7 +335,7 @@ impl AccessControlProfile {
|
|||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
ladmin_error!(audit, "Missing acp_targetscope");
|
||||
OperationError::InvalidACPState("Missing acp_targetscope".to_string())
|
||||
OperationError::InvalidAcpState("Missing acp_targetscope".to_string())
|
||||
})?;
|
||||
|
||||
let event = Event::from_internal();
|
||||
|
|
|
@ -9,8 +9,8 @@ use crate::event::{
|
|||
ReviveRecycledEvent,
|
||||
};
|
||||
use crate::idm::event::{
|
||||
GeneratePasswordEvent, GenerateTOTPEvent, PasswordChangeEvent, RegenerateRadiusSecretEvent,
|
||||
RemoveTOTPEvent, RemoveWebauthnEvent, UnixPasswordChangeEvent, VerifyTOTPEvent,
|
||||
GeneratePasswordEvent, GenerateTotpEvent, PasswordChangeEvent, RegenerateRadiusSecretEvent,
|
||||
RemoveTotpEvent, RemoveWebauthnEvent, UnixPasswordChangeEvent, VerifyTotpEvent,
|
||||
WebauthnDoRegisterEvent, WebauthnInitRegisterEvent,
|
||||
};
|
||||
use crate::modify::{Modify, ModifyInvalid, ModifyList};
|
||||
|
@ -622,8 +622,8 @@ impl QueryServerWriteV1 {
|
|||
.and_then(|r| idms_prox_write.commit(&mut audit).map(|_| r))
|
||||
.map(SetCredentialResponse::Token)
|
||||
}
|
||||
SetCredentialRequest::TOTPGenerate(label) => {
|
||||
let gte = GenerateTOTPEvent::from_parts(
|
||||
SetCredentialRequest::TotpGenerate(label) => {
|
||||
let gte = GenerateTotpEvent::from_parts(
|
||||
&mut audit,
|
||||
&idms_prox_write.qs_write,
|
||||
msg.uat.as_ref(),
|
||||
|
@ -642,8 +642,8 @@ impl QueryServerWriteV1 {
|
|||
.generate_account_totp(&mut audit, >e, ct)
|
||||
.and_then(|r| idms_prox_write.commit(&mut audit).map(|_| r))
|
||||
}
|
||||
SetCredentialRequest::TOTPVerify(uuid, chal) => {
|
||||
let vte = VerifyTOTPEvent::from_parts(
|
||||
SetCredentialRequest::TotpVerify(uuid, chal) => {
|
||||
let vte = VerifyTotpEvent::from_parts(
|
||||
&mut audit,
|
||||
&idms_prox_write.qs_write,
|
||||
msg.uat.as_ref(),
|
||||
|
@ -663,8 +663,8 @@ impl QueryServerWriteV1 {
|
|||
.verify_account_totp(&mut audit, &vte, ct)
|
||||
.and_then(|r| idms_prox_write.commit(&mut audit).map(|_| r))
|
||||
}
|
||||
SetCredentialRequest::TOTPRemove => {
|
||||
let rte = RemoveTOTPEvent::from_parts(
|
||||
SetCredentialRequest::TotpRemove => {
|
||||
let rte = RemoveTotpEvent::from_parts(
|
||||
&mut audit,
|
||||
&idms_prox_write.qs_write,
|
||||
msg.uat.as_ref(),
|
||||
|
|
|
@ -590,7 +590,7 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
entries.try_for_each(|e| {
|
||||
ltrace!(au, "Inserting {:?} to cache", e.get_id());
|
||||
if e.get_id() == 0 {
|
||||
Err(OperationError::InvalidEntryID)
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
(*self.allids).insert_id(e.get_id());
|
||||
self.entry_cache
|
||||
|
@ -627,7 +627,7 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
idl.try_for_each(|i| {
|
||||
ltrace!(au, "Removing {:?} from cache", i);
|
||||
if i == 0 {
|
||||
Err(OperationError::InvalidEntryID)
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
(*self.allids).remove_id(i);
|
||||
self.entry_cache.remove_dirty(i);
|
||||
|
|
|
@ -36,13 +36,13 @@ impl TryFrom<IdSqliteEntry> for IdRawEntry {
|
|||
|
||||
fn try_from(value: IdSqliteEntry) -> Result<Self, Self::Error> {
|
||||
if value.id <= 0 {
|
||||
return Err(OperationError::InvalidEntryID);
|
||||
return Err(OperationError::InvalidEntryId);
|
||||
}
|
||||
Ok(IdRawEntry {
|
||||
id: value
|
||||
.id
|
||||
.try_into()
|
||||
.map_err(|_| OperationError::InvalidEntryID)?,
|
||||
.map_err(|_| OperationError::InvalidEntryId)?,
|
||||
data: value.data,
|
||||
})
|
||||
}
|
||||
|
@ -53,13 +53,13 @@ impl TryFrom<IdRawEntry> for IdSqliteEntry {
|
|||
|
||||
fn try_from(value: IdRawEntry) -> Result<Self, Self::Error> {
|
||||
if value.id == 0 {
|
||||
return Err(OperationError::InvalidEntryID);
|
||||
return Err(OperationError::InvalidEntryId);
|
||||
}
|
||||
Ok(IdSqliteEntry {
|
||||
id: value
|
||||
.id
|
||||
.try_into()
|
||||
.map_err(|_| OperationError::InvalidEntryID)?,
|
||||
.map_err(|_| OperationError::InvalidEntryId)?,
|
||||
data: value.data,
|
||||
})
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ pub trait IdlSqliteTransaction {
|
|||
.prepare("SELECT id, data FROM id2entry")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let id2entry_iter = stmt
|
||||
.query_map([], |row| {
|
||||
|
@ -120,13 +120,13 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
id2entry_iter
|
||||
.map(|v| {
|
||||
v.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
.and_then(|ise| {
|
||||
// Convert the idsqlite to id raw
|
||||
|
@ -141,7 +141,7 @@ pub trait IdlSqliteTransaction {
|
|||
.prepare("SELECT id, data FROM id2entry WHERE id = :idl")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
// TODO #258: Can this actually just load in a single select?
|
||||
|
@ -151,12 +151,12 @@ pub trait IdlSqliteTransaction {
|
|||
|
||||
/*
|
||||
let decompressed: Result<Vec<i64>, _> = idli.into_iter()
|
||||
.map(|u| i64::try_from(u).map_err(|_| OperationError::InvalidEntryID))
|
||||
.map(|u| i64::try_from(u).map_err(|_| OperationError::InvalidEntryId))
|
||||
.collect();
|
||||
*/
|
||||
|
||||
for id in idli {
|
||||
let iid = i64::try_from(id).map_err(|_| OperationError::InvalidEntryID)?;
|
||||
let iid = i64::try_from(id).map_err(|_| OperationError::InvalidEntryId)?;
|
||||
let id2entry_iter = stmt
|
||||
.query_map(&[&iid], |row| {
|
||||
Ok(IdSqliteEntry {
|
||||
|
@ -166,14 +166,14 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let r: Result<Vec<_>, _> = id2entry_iter
|
||||
.map(|v| {
|
||||
v.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
.and_then(|ise| {
|
||||
// Convert the idsqlite to id raw
|
||||
|
@ -201,13 +201,13 @@ pub trait IdlSqliteTransaction {
|
|||
.prepare("SELECT COUNT(name) from sqlite_master where name = :tname")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let i: Option<i64> = stmt
|
||||
.query_row(&[(":tname", &tname)], |row| row.get(0))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
if i.unwrap_or(0) == 0 {
|
||||
|
@ -238,7 +238,7 @@ pub trait IdlSqliteTransaction {
|
|||
);
|
||||
let mut stmt = self.get_conn().prepare(query.as_str()).map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let idl_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":idx_key", &idx_key)], |row| row.get(0))
|
||||
|
@ -246,7 +246,7 @@ pub trait IdlSqliteTransaction {
|
|||
.optional()
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let idl = match idl_raw {
|
||||
|
@ -274,7 +274,7 @@ pub trait IdlSqliteTransaction {
|
|||
.prepare("SELECT uuid FROM idx_name2uuid WHERE name = :name")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let uuid_raw: Option<String> = stmt
|
||||
.query_row(&[(":name", &name)], |row| row.get(0))
|
||||
|
@ -282,7 +282,7 @@ pub trait IdlSqliteTransaction {
|
|||
.optional()
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let uuid = uuid_raw.as_ref().and_then(|u| Uuid::parse_str(u).ok());
|
||||
|
@ -305,7 +305,7 @@ pub trait IdlSqliteTransaction {
|
|||
.prepare("SELECT spn FROM idx_uuid2spn WHERE uuid = :uuid")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let spn_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":uuid", &uuids)], |row| row.get(0))
|
||||
|
@ -313,7 +313,7 @@ pub trait IdlSqliteTransaction {
|
|||
.optional()
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let spn: Option<Value> = match spn_raw {
|
||||
|
@ -346,7 +346,7 @@ pub trait IdlSqliteTransaction {
|
|||
.prepare("SELECT rdn FROM idx_uuid2rdn WHERE uuid = :uuid")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let rdn: Option<String> = stmt
|
||||
.query_row(&[(":uuid", &uuids)], |row| row.get(0))
|
||||
|
@ -354,7 +354,7 @@ pub trait IdlSqliteTransaction {
|
|||
.optional()
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
ltrace!(audit, "Got rdn for uuid {:?} -> {:?}", uuid, rdn);
|
||||
|
@ -377,7 +377,7 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
// If no sid, we return none.
|
||||
})
|
||||
.map_err(|_| OperationError::SQLiteError)?;
|
||||
.map_err(|_| OperationError::SqliteError)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(
|
||||
|
@ -401,7 +401,7 @@ pub trait IdlSqliteTransaction {
|
|||
})
|
||||
// If no sid, we return none.
|
||||
})
|
||||
.map_err(|_| OperationError::SQLiteError)?;
|
||||
.map_err(|_| OperationError::SqliteError)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(
|
||||
|
@ -533,19 +533,19 @@ impl IdlSqliteWriteTransaction {
|
|||
let mut stmt = self
|
||||
.conn
|
||||
.prepare("SELECT MAX(id) as id_max FROM id2entry")
|
||||
.map_err(|_| OperationError::SQLiteError)?;
|
||||
.map_err(|_| OperationError::SqliteError)?;
|
||||
// This exists checks for if any rows WERE returned
|
||||
// that way we know to shortcut or not.
|
||||
let v = stmt.exists([]).map_err(|_| OperationError::SQLiteError)?;
|
||||
let v = stmt.exists([]).map_err(|_| OperationError::SqliteError)?;
|
||||
|
||||
if v {
|
||||
// We have some rows, let get max!
|
||||
let i: Option<i64> = stmt
|
||||
.query_row([], |row| row.get(0))
|
||||
.map_err(|_| OperationError::SQLiteError)?;
|
||||
.map_err(|_| OperationError::SqliteError)?;
|
||||
i.unwrap_or(0)
|
||||
.try_into()
|
||||
.map_err(|_| OperationError::InvalidEntryID)
|
||||
.map_err(|_| OperationError::InvalidEntryId)
|
||||
} else {
|
||||
// No rows are present, return a 0.
|
||||
Ok(0)
|
||||
|
@ -608,7 +608,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.prepare("INSERT OR REPLACE INTO id2entry (id, data) VALUES(:id, :data)")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
entries.try_for_each(|e| {
|
||||
|
@ -621,7 +621,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -638,18 +638,18 @@ impl IdlSqliteWriteTransaction {
|
|||
.prepare("DELETE FROM id2entry WHERE id = :id")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
idl.try_for_each(|id| {
|
||||
let iid: i64 = id
|
||||
.try_into()
|
||||
.map_err(|_| OperationError::InvalidEntryID)
|
||||
.map_err(|_| OperationError::InvalidEntryId)
|
||||
.and_then(|i| {
|
||||
if i > 0 {
|
||||
Ok(i)
|
||||
} else {
|
||||
Err(OperationError::InvalidEntryID)
|
||||
Err(OperationError::InvalidEntryId)
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -657,7 +657,7 @@ impl IdlSqliteWriteTransaction {
|
|||
|
||||
stmt.execute(&[&iid]).map(|_| ()).map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -671,17 +671,17 @@ impl IdlSqliteWriteTransaction {
|
|||
.prepare("DELETE FROM id2entry WHERE id = :id")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let iid: i64 = id
|
||||
.try_into()
|
||||
.map_err(|_| OperationError::InvalidEntryID)
|
||||
.map_err(|_| OperationError::InvalidEntryId)
|
||||
.and_then(|i| {
|
||||
if i > 0 {
|
||||
Ok(i)
|
||||
} else {
|
||||
Err(OperationError::InvalidEntryID)
|
||||
Err(OperationError::InvalidEntryId)
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -689,7 +689,7 @@ impl IdlSqliteWriteTransaction {
|
|||
|
||||
stmt.execute(&[&iid]).map(|_| ()).map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
// })
|
||||
}
|
||||
|
@ -718,7 +718,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.and_then(|mut stmt| stmt.execute(&[(":key", &idx_key)]))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
} else {
|
||||
ltrace!(audit, "writing idl -> {}", idl);
|
||||
|
@ -745,7 +745,7 @@ impl IdlSqliteWriteTransaction {
|
|||
})
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
// Get rid of the sqlite rows usize
|
||||
|
@ -762,7 +762,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -785,7 +785,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -800,7 +800,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -813,7 +813,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -840,7 +840,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
None => self
|
||||
|
@ -850,7 +850,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -864,7 +864,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -883,7 +883,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
}),
|
||||
None => self
|
||||
.conn
|
||||
|
@ -892,7 +892,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -919,7 +919,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -929,18 +929,18 @@ impl IdlSqliteWriteTransaction {
|
|||
.prepare("SELECT name from sqlite_master where type='table' and name LIKE 'idx_%'")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let idx_table_iter = stmt.query_map([], |row| row.get(0)).map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let r: Result<_, _> = idx_table_iter
|
||||
.map(|v| {
|
||||
v.map_err(|e| {
|
||||
ladmin_error!(audit, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
@ -958,7 +958,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.and_then(|mut stmt| stmt.execute([]).map(|_| ()))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -970,7 +970,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -988,7 +988,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
eprintln!("CRITICAL: rusqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1006,7 +1006,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
eprintln!("CRITICAL: rusqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1024,7 +1024,7 @@ impl IdlSqliteWriteTransaction {
|
|||
.map(|_| ())
|
||||
.map_err(|e| {
|
||||
eprintln!("CRITICAL: rusqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1044,7 +1044,7 @@ impl IdlSqliteWriteTransaction {
|
|||
})
|
||||
// If no sid, we return none.
|
||||
})
|
||||
.map_err(|_| OperationError::SQLiteError)?;
|
||||
.map_err(|_| OperationError::SqliteError)?;
|
||||
|
||||
Ok(match data {
|
||||
Some(d) => Some(
|
||||
|
@ -1089,7 +1089,7 @@ impl IdlSqliteWriteTransaction {
|
|||
pub(crate) fn set_db_index_version(&self, v: i64) -> Result<(), OperationError> {
|
||||
self.set_db_version_key(DBV_INDEXV, v).map_err(|e| {
|
||||
eprintln!("CRITICAL: rusqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1097,22 +1097,22 @@ impl IdlSqliteWriteTransaction {
|
|||
ltrace!(au, "Building allids...");
|
||||
let mut stmt = self.conn.prepare("SELECT id FROM id2entry").map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
let res = stmt.query_map([], |row| row.get(0)).map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
res.map(|v| {
|
||||
v.map_err(|e| {
|
||||
ladmin_error!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
.and_then(|id: i64| {
|
||||
// Convert the idsqlite to id raw
|
||||
id.try_into().map_err(|e| {
|
||||
ladmin_error!(au, "I64 Parse Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1143,7 +1143,7 @@ impl IdlSqliteWriteTransaction {
|
|||
)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
// If the table is empty, populate the versions as 0.
|
||||
|
@ -1174,7 +1174,7 @@ impl IdlSqliteWriteTransaction {
|
|||
})
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
dbv_id2entry = 1;
|
||||
|
@ -1197,7 +1197,7 @@ impl IdlSqliteWriteTransaction {
|
|||
)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
dbv_id2entry = 2;
|
||||
|
@ -1216,7 +1216,7 @@ impl IdlSqliteWriteTransaction {
|
|||
)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
dbv_id2entry = 3;
|
||||
ladmin_info!(
|
||||
|
@ -1242,7 +1242,7 @@ impl IdlSqliteWriteTransaction {
|
|||
self.set_db_version_key(DBV_ID2ENTRY, dbv_id2entry)
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "sqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
// NOTE: Indexing is configured in a different step!
|
||||
|
@ -1259,7 +1259,7 @@ impl IdlSqlite {
|
|||
cfg: &BackendConfig,
|
||||
vacuum: bool,
|
||||
) -> Result<Self, OperationError> {
|
||||
if cfg.path == "" {
|
||||
if cfg.path.is_empty() {
|
||||
debug_assert!(cfg.pool_size == 1);
|
||||
}
|
||||
// If provided, set the page size to match the tuning we want. By default we use 4096. The VACUUM
|
||||
|
@ -1279,48 +1279,48 @@ impl IdlSqlite {
|
|||
|
||||
let vconn = Connection::open_with_flags(cfg.path.as_str(), flags).map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
vconn
|
||||
.pragma_update(None, "journal_mode", &"DELETE")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite journal_mode update error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
vconn.close().map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite db close error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
let vconn = Connection::open_with_flags(cfg.path.as_str(), flags).map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
vconn
|
||||
.pragma_update(None, "page_size", &(cfg.fstype as u32))
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite page_size update error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
vconn.execute_batch("VACUUM").map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite vacuum error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
vconn
|
||||
.pragma_update(None, "journal_mode", &"WAL")
|
||||
.map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite journal_mode update error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
vconn.close().map_err(|e| {
|
||||
ladmin_error!(audit, "rusqlite db close error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
limmediate_warning!(audit, "NOTICE: db vacuum complete\n");
|
||||
|
@ -1341,7 +1341,7 @@ impl IdlSqlite {
|
|||
// Look at max_size and thread_pool here for perf later
|
||||
let pool = builder2.build(manager).map_err(|e| {
|
||||
ladmin_error!(audit, "r2d2 error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})?;
|
||||
|
||||
Ok(IdlSqlite { pool })
|
||||
|
@ -1355,7 +1355,7 @@ impl IdlSqlite {
|
|||
.query_row("select count(id) from id2entry", [], |row| row.get(0))
|
||||
.map_err(|e| {
|
||||
ltrace!(au, "SQLite Error {:?}", e);
|
||||
OperationError::SQLiteError
|
||||
OperationError::SqliteError
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ impl IdRawEntry {
|
|||
) -> Result<Entry<EntrySealed, EntryCommitted>, OperationError> {
|
||||
let db_e = serde_cbor::from_slice(self.data.as_slice())
|
||||
.map_err(|_| OperationError::SerdeCborError)?;
|
||||
// let id = u64::try_from(self.id).map_err(|_| OperationError::InvalidEntryID)?;
|
||||
// let id = u64::try_from(self.id).map_err(|_| OperationError::InvalidEntryId)?;
|
||||
Entry::from_dbentry(au, db_e, self.id).map_err(|_| OperationError::CorruptedEntry(self.id))
|
||||
}
|
||||
}
|
||||
|
@ -836,10 +836,10 @@ impl<'a> BackendWriteTransaction<'a> {
|
|||
.iter()
|
||||
.map(|e| {
|
||||
let id = i64::try_from(e.get_id())
|
||||
.map_err(|_| OperationError::InvalidEntryID)
|
||||
.map_err(|_| OperationError::InvalidEntryId)
|
||||
.and_then(|id| {
|
||||
if id == 0 {
|
||||
Err(OperationError::InvalidEntryID)
|
||||
Err(OperationError::InvalidEntryId)
|
||||
} else {
|
||||
Ok(id)
|
||||
}
|
||||
|
@ -1375,7 +1375,7 @@ impl Backend {
|
|||
info!("CPU Flags -> {}", env!("KANIDM_CPU_FLAGS"));
|
||||
|
||||
// If in memory, reduce pool to 1
|
||||
if &cfg.path == "" {
|
||||
if cfg.path.is_empty() {
|
||||
cfg.pool_size = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ use std::fmt;
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct IntegrationTestConfig {
|
||||
pub admin_user: String,
|
||||
pub admin_password: String,
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ pub mod webauthn;
|
|||
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::softlock::CredSoftLockPolicy;
|
||||
use crate::credential::totp::TOTP;
|
||||
use crate::credential::totp::Totp;
|
||||
|
||||
// NIST 800-63.b salt should be 112 bits -> 14 8u8.
|
||||
// I choose tinfoil hat though ...
|
||||
|
@ -207,10 +207,7 @@ impl Password {
|
|||
}
|
||||
|
||||
pub fn requires_upgrade(&self) -> bool {
|
||||
match &self.material {
|
||||
KDF::PBKDF2(_, _, _) => false,
|
||||
_ => true,
|
||||
}
|
||||
!matches!(&self.material, KDF::PBKDF2(_, _, _))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,7 +240,7 @@ pub enum CredentialType {
|
|||
Password(Password),
|
||||
GeneratedPassword(Password),
|
||||
Webauthn(Map<String, WebauthnCredential>),
|
||||
PasswordMFA(Password, Option<TOTP>, Map<String, WebauthnCredential>),
|
||||
PasswordMfa(Password, Option<Totp>, Map<String, WebauthnCredential>),
|
||||
// PasswordWebauthn(Password, Map<String, WebauthnCredential>),
|
||||
// WebauthnVerified(Map<String, WebauthnCredential>),
|
||||
// PasswordWebauthnVerified(Password, Map<String, WebauthnCredential>),
|
||||
|
@ -262,10 +259,10 @@ impl Into<CredentialDetail> for &Credential {
|
|||
labels.sort_unstable();
|
||||
CredentialDetailType::Webauthn(labels)
|
||||
}
|
||||
CredentialType::PasswordMFA(_, totp, wan) => {
|
||||
CredentialType::PasswordMfa(_, totp, wan) => {
|
||||
let mut labels: Vec<_> = wan.keys().cloned().collect();
|
||||
labels.sort_unstable();
|
||||
CredentialDetailType::PasswordMFA(totp.is_some(), labels)
|
||||
CredentialDetailType::PasswordMfa(totp.is_some(), labels)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -292,7 +289,7 @@ impl TryFrom<DbCredV1> for Credential {
|
|||
};
|
||||
|
||||
let v_totp = match totp {
|
||||
Some(dbt) => Some(TOTP::try_from(dbt)?),
|
||||
Some(dbt) => Some(Totp::try_from(dbt)?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
|
@ -320,7 +317,7 @@ impl TryFrom<DbCredV1> for Credential {
|
|||
DbCredTypeV1::GPw => v_password.map(CredentialType::GeneratedPassword),
|
||||
// In the future this could use .zip
|
||||
DbCredTypeV1::PwMfa => match (v_password, v_webauthn) {
|
||||
(Some(pw), Some(wn)) => Some(CredentialType::PasswordMFA(pw, v_totp, wn)),
|
||||
(Some(pw), Some(wn)) => Some(CredentialType::PasswordMfa(pw, v_totp, wn)),
|
||||
_ => None,
|
||||
},
|
||||
DbCredTypeV1::Wn => v_webauthn.map(CredentialType::Webauthn),
|
||||
|
@ -371,9 +368,9 @@ impl Credential {
|
|||
CredentialType::Password(pw) | CredentialType::GeneratedPassword(pw) => {
|
||||
let mut wan = Map::new();
|
||||
wan.insert(label, cred);
|
||||
CredentialType::PasswordMFA(pw.clone(), None, wan)
|
||||
CredentialType::PasswordMfa(pw.clone(), None, wan)
|
||||
}
|
||||
CredentialType::PasswordMFA(pw, totp, map) => {
|
||||
CredentialType::PasswordMfa(pw, totp, map) => {
|
||||
let mut nmap = map.clone();
|
||||
if nmap.insert(label.clone(), cred).is_some() {
|
||||
return Err(OperationError::InvalidAttribute(format!(
|
||||
|
@ -381,7 +378,7 @@ impl Credential {
|
|||
label
|
||||
)));
|
||||
}
|
||||
CredentialType::PasswordMFA(pw.clone(), totp.clone(), nmap)
|
||||
CredentialType::PasswordMfa(pw.clone(), totp.clone(), nmap)
|
||||
}
|
||||
CredentialType::Webauthn(map) => {
|
||||
let mut nmap = map.clone();
|
||||
|
@ -410,7 +407,7 @@ impl Credential {
|
|||
"Webauthn is not present on this credential".to_string(),
|
||||
));
|
||||
}
|
||||
CredentialType::PasswordMFA(pw, totp, map) => {
|
||||
CredentialType::PasswordMfa(pw, totp, map) => {
|
||||
let mut nmap = map.clone();
|
||||
if nmap.remove(label).is_none() {
|
||||
return Err(OperationError::InvalidAttribute(format!(
|
||||
|
@ -420,12 +417,12 @@ impl Credential {
|
|||
}
|
||||
if nmap.is_empty() {
|
||||
if totp.is_some() {
|
||||
CredentialType::PasswordMFA(pw.clone(), totp.clone(), nmap)
|
||||
CredentialType::PasswordMfa(pw.clone(), totp.clone(), nmap)
|
||||
} else {
|
||||
CredentialType::Password(pw.clone())
|
||||
}
|
||||
} else {
|
||||
CredentialType::PasswordMFA(pw.clone(), totp.clone(), nmap)
|
||||
CredentialType::PasswordMfa(pw.clone(), totp.clone(), nmap)
|
||||
}
|
||||
}
|
||||
CredentialType::Webauthn(map) => {
|
||||
|
@ -465,7 +462,7 @@ impl Credential {
|
|||
// No action required
|
||||
return Ok(None);
|
||||
}
|
||||
CredentialType::PasswordMFA(_, _, map) | CredentialType::Webauthn(map) => map
|
||||
CredentialType::PasswordMfa(_, _, map) | CredentialType::Webauthn(map) => map
|
||||
.iter()
|
||||
.fold(None, |acc, (k, v)| {
|
||||
if acc.is_none() && &v.cred_id == cid && v.counter < counter {
|
||||
|
@ -498,8 +495,8 @@ impl Credential {
|
|||
return Err(OperationError::InvalidState);
|
||||
}
|
||||
CredentialType::Webauthn(_) => CredentialType::Webauthn(map),
|
||||
CredentialType::PasswordMFA(pw, totp, _) => {
|
||||
CredentialType::PasswordMFA(pw.clone(), totp.clone(), map)
|
||||
CredentialType::PasswordMfa(pw, totp, _) => {
|
||||
CredentialType::PasswordMfa(pw.clone(), totp.clone(), map)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -515,7 +512,7 @@ impl Credential {
|
|||
CredentialType::Password(_) | CredentialType::GeneratedPassword(_) => Err(
|
||||
OperationError::InvalidAccountState("non-webauthn cred type?".to_string()),
|
||||
),
|
||||
CredentialType::PasswordMFA(_, _, map) | CredentialType::Webauthn(map) => Ok(map),
|
||||
CredentialType::PasswordMfa(_, _, map) | CredentialType::Webauthn(map) => Ok(map),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -523,7 +520,7 @@ impl Credential {
|
|||
match &self.type_ {
|
||||
CredentialType::Password(pw)
|
||||
| CredentialType::GeneratedPassword(pw)
|
||||
| CredentialType::PasswordMFA(pw, _, _) => Ok(pw),
|
||||
| CredentialType::PasswordMfa(pw, _, _) => Ok(pw),
|
||||
CredentialType::Webauthn(_) => Err(OperationError::InvalidAccountState(
|
||||
"non-password cred type?".to_string(),
|
||||
)),
|
||||
|
@ -555,7 +552,7 @@ impl Credential {
|
|||
claims,
|
||||
uuid,
|
||||
},
|
||||
CredentialType::PasswordMFA(pw, totp, map) => DbCredV1 {
|
||||
CredentialType::PasswordMfa(pw, totp, map) => DbCredV1 {
|
||||
type_: DbCredTypeV1::PwMfa,
|
||||
password: Some(pw.to_dbpasswordv1()),
|
||||
webauthn: Some(
|
||||
|
@ -598,10 +595,10 @@ impl Credential {
|
|||
let type_ = match &self.type_ {
|
||||
CredentialType::Password(_) => CredentialType::Password(pw),
|
||||
CredentialType::GeneratedPassword(_) => CredentialType::GeneratedPassword(pw),
|
||||
CredentialType::PasswordMFA(_, totp, wan) => {
|
||||
CredentialType::PasswordMFA(pw, totp.clone(), wan.clone())
|
||||
CredentialType::PasswordMfa(_, totp, wan) => {
|
||||
CredentialType::PasswordMfa(pw, totp.clone(), wan.clone())
|
||||
}
|
||||
CredentialType::Webauthn(wan) => CredentialType::PasswordMFA(pw, None, wan.clone()),
|
||||
CredentialType::Webauthn(wan) => CredentialType::PasswordMfa(pw, None, wan.clone()),
|
||||
};
|
||||
Credential {
|
||||
type_,
|
||||
|
@ -611,13 +608,13 @@ impl Credential {
|
|||
}
|
||||
|
||||
// We don't make totp accessible from outside the crate for now.
|
||||
pub(crate) fn update_totp(&self, totp: TOTP) -> Self {
|
||||
pub(crate) fn update_totp(&self, totp: Totp) -> Self {
|
||||
let type_ = match &self.type_ {
|
||||
CredentialType::Password(pw) | CredentialType::GeneratedPassword(pw) => {
|
||||
CredentialType::PasswordMFA(pw.clone(), Some(totp), Map::new())
|
||||
CredentialType::PasswordMfa(pw.clone(), Some(totp), Map::new())
|
||||
}
|
||||
CredentialType::PasswordMFA(pw, _, wan) => {
|
||||
CredentialType::PasswordMFA(pw.clone(), Some(totp), wan.clone())
|
||||
CredentialType::PasswordMfa(pw, _, wan) => {
|
||||
CredentialType::PasswordMfa(pw.clone(), Some(totp), wan.clone())
|
||||
}
|
||||
CredentialType::Webauthn(wan) => {
|
||||
debug_assert!(false);
|
||||
|
@ -633,11 +630,11 @@ impl Credential {
|
|||
|
||||
pub(crate) fn remove_totp(&self) -> Self {
|
||||
let type_ = match &self.type_ {
|
||||
CredentialType::PasswordMFA(pw, Some(_), wan) => {
|
||||
CredentialType::PasswordMfa(pw, Some(_), wan) => {
|
||||
if wan.is_empty() {
|
||||
CredentialType::Password(pw.clone())
|
||||
} else {
|
||||
CredentialType::PasswordMFA(pw.clone(), None, wan.clone())
|
||||
CredentialType::PasswordMfa(pw.clone(), None, wan.clone())
|
||||
}
|
||||
}
|
||||
_ => self.type_.clone(),
|
||||
|
@ -662,9 +659,9 @@ impl Credential {
|
|||
CredentialType::Password(_pw) | CredentialType::GeneratedPassword(_pw) => {
|
||||
Some(CredSoftLockPolicy::Password)
|
||||
}
|
||||
CredentialType::PasswordMFA(_pw, totp, wan) => {
|
||||
CredentialType::PasswordMfa(_pw, totp, wan) => {
|
||||
if let Some(r_totp) = totp {
|
||||
Some(CredSoftLockPolicy::TOTP(r_totp.step))
|
||||
Some(CredSoftLockPolicy::Totp(r_totp.step))
|
||||
} else if !wan.is_empty() {
|
||||
Some(CredSoftLockPolicy::Webauthn)
|
||||
} else {
|
||||
|
@ -700,7 +697,7 @@ impl CredentialType {
|
|||
fn is_valid(&self) -> bool {
|
||||
match self {
|
||||
CredentialType::Password(_) | CredentialType::GeneratedPassword(_) => true,
|
||||
CredentialType::PasswordMFA(_, m_totp, webauthn) => {
|
||||
CredentialType::PasswordMfa(_, m_totp, webauthn) => {
|
||||
m_totp.is_some() || !webauthn.is_empty()
|
||||
}
|
||||
CredentialType::Webauthn(webauthn) => !webauthn.is_empty(),
|
||||
|
|
|
@ -63,7 +63,7 @@ const ONEDAY: u64 = 86400;
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum CredSoftLockPolicy {
|
||||
Password,
|
||||
TOTP(u64),
|
||||
Totp(u64),
|
||||
Webauthn,
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ impl CredSoftLockPolicy {
|
|||
LockState::Locked(count, reset_at, reset_at)
|
||||
}
|
||||
}
|
||||
CredSoftLockPolicy::TOTP(step) => {
|
||||
CredSoftLockPolicy::Totp(step) => {
|
||||
// reset at is based on the next step ending.
|
||||
let next_window_end = ct.as_secs() + step;
|
||||
let rem = next_window_end % step;
|
||||
|
@ -170,10 +170,7 @@ impl CredSoftLock {
|
|||
|
||||
/// Is this credential valid to proceed at this point in time.
|
||||
pub fn is_valid(&self) -> bool {
|
||||
match self.state {
|
||||
LockState::Locked(_count, _reset_at, _unlock_at) => false,
|
||||
_ => true,
|
||||
}
|
||||
!matches!(self.state, LockState::Locked(_count, _reset_at, _unlock_at))
|
||||
}
|
||||
|
||||
/// Document a failure of authentication at this time.
|
||||
|
@ -320,7 +317,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_credential_softlock_policy_totp() {
|
||||
let policy = CredSoftLockPolicy::TOTP(TOTP_DEFAULT_STEP);
|
||||
let policy = CredSoftLockPolicy::Totp(TOTP_DEFAULT_STEP);
|
||||
|
||||
assert!(
|
||||
policy.failure_next_state(1, Duration::from_secs(10))
|
||||
|
|
|
@ -7,51 +7,51 @@ use std::convert::TryFrom;
|
|||
use std::convert::TryInto;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use kanidm_proto::v1::TOTPAlgo as ProtoTOTPAlgo;
|
||||
use kanidm_proto::v1::TOTPSecret as ProtoTOTP;
|
||||
use kanidm_proto::v1::TotpAlgo as ProtoTotpAlgo;
|
||||
use kanidm_proto::v1::TotpSecret as ProtoTotp;
|
||||
|
||||
// This is 64 bits of entropy, as the examples in https://tools.ietf.org/html/rfc6238 show.
|
||||
const SECRET_SIZE_BYTES: usize = 8;
|
||||
pub const TOTP_DEFAULT_STEP: u64 = 30;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum TOTPError {
|
||||
pub enum TotpError {
|
||||
OpenSSLError,
|
||||
HmacError,
|
||||
TimeError,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TOTPAlgo {
|
||||
pub enum TotpAlgo {
|
||||
Sha1,
|
||||
Sha256,
|
||||
Sha512,
|
||||
}
|
||||
|
||||
impl TOTPAlgo {
|
||||
pub(crate) fn digest(&self, key: &[u8], counter: u64) -> Result<Vec<u8>, TOTPError> {
|
||||
let key = PKey::hmac(key).map_err(|_e| TOTPError::OpenSSLError)?;
|
||||
impl TotpAlgo {
|
||||
pub(crate) fn digest(&self, key: &[u8], counter: u64) -> Result<Vec<u8>, TotpError> {
|
||||
let key = PKey::hmac(key).map_err(|_e| TotpError::OpenSSLError)?;
|
||||
let mut signer =
|
||||
match self {
|
||||
TOTPAlgo::Sha1 => Signer::new(MessageDigest::sha1(), &key)
|
||||
.map_err(|_e| TOTPError::OpenSSLError)?,
|
||||
TOTPAlgo::Sha256 => Signer::new(MessageDigest::sha256(), &key)
|
||||
.map_err(|_e| TOTPError::OpenSSLError)?,
|
||||
TOTPAlgo::Sha512 => Signer::new(MessageDigest::sha512(), &key)
|
||||
.map_err(|_e| TOTPError::OpenSSLError)?,
|
||||
TotpAlgo::Sha1 => Signer::new(MessageDigest::sha1(), &key)
|
||||
.map_err(|_e| TotpError::OpenSSLError)?,
|
||||
TotpAlgo::Sha256 => Signer::new(MessageDigest::sha256(), &key)
|
||||
.map_err(|_e| TotpError::OpenSSLError)?,
|
||||
TotpAlgo::Sha512 => Signer::new(MessageDigest::sha512(), &key)
|
||||
.map_err(|_e| TotpError::OpenSSLError)?,
|
||||
};
|
||||
signer
|
||||
.update(&counter.to_be_bytes())
|
||||
.map_err(|_e| TOTPError::OpenSSLError)?;
|
||||
let hmac = signer.sign_to_vec().map_err(|_e| TOTPError::OpenSSLError)?;
|
||||
.map_err(|_e| TotpError::OpenSSLError)?;
|
||||
let hmac = signer.sign_to_vec().map_err(|_e| TotpError::OpenSSLError)?;
|
||||
|
||||
let expect = match self {
|
||||
TOTPAlgo::Sha1 => 20,
|
||||
TOTPAlgo::Sha256 => 32,
|
||||
TOTPAlgo::Sha512 => 64,
|
||||
TotpAlgo::Sha1 => 20,
|
||||
TotpAlgo::Sha256 => 32,
|
||||
TotpAlgo::Sha512 => 64,
|
||||
};
|
||||
if hmac.len() != expect {
|
||||
return Err(TOTPError::HmacError);
|
||||
return Err(TotpError::HmacError);
|
||||
}
|
||||
Ok(hmac)
|
||||
}
|
||||
|
@ -59,23 +59,23 @@ impl TOTPAlgo {
|
|||
|
||||
/// https://tools.ietf.org/html/rfc6238 which relies on https://tools.ietf.org/html/rfc4226
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TOTP {
|
||||
pub struct Totp {
|
||||
label: String,
|
||||
secret: Vec<u8>,
|
||||
pub(crate) step: u64,
|
||||
algo: TOTPAlgo,
|
||||
algo: TotpAlgo,
|
||||
}
|
||||
|
||||
impl TryFrom<DbTotpV1> for TOTP {
|
||||
impl TryFrom<DbTotpV1> for Totp {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: DbTotpV1) -> Result<Self, Self::Error> {
|
||||
let algo = match value.a {
|
||||
DbTotpAlgoV1::S1 => TOTPAlgo::Sha1,
|
||||
DbTotpAlgoV1::S256 => TOTPAlgo::Sha256,
|
||||
DbTotpAlgoV1::S512 => TOTPAlgo::Sha512,
|
||||
DbTotpAlgoV1::S1 => TotpAlgo::Sha1,
|
||||
DbTotpAlgoV1::S256 => TotpAlgo::Sha256,
|
||||
DbTotpAlgoV1::S512 => TotpAlgo::Sha512,
|
||||
};
|
||||
Ok(TOTP {
|
||||
Ok(Totp {
|
||||
label: value.l,
|
||||
secret: value.k,
|
||||
step: value.s,
|
||||
|
@ -84,24 +84,24 @@ impl TryFrom<DbTotpV1> for TOTP {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ProtoTOTP> for TOTP {
|
||||
fn from(value: ProtoTOTP) -> Self {
|
||||
TOTP {
|
||||
impl From<ProtoTotp> for Totp {
|
||||
fn from(value: ProtoTotp) -> Self {
|
||||
Totp {
|
||||
label: "test_token".to_string(),
|
||||
secret: value.secret,
|
||||
algo: match value.algo {
|
||||
ProtoTOTPAlgo::Sha1 => TOTPAlgo::Sha1,
|
||||
ProtoTOTPAlgo::Sha256 => TOTPAlgo::Sha256,
|
||||
ProtoTOTPAlgo::Sha512 => TOTPAlgo::Sha512,
|
||||
ProtoTotpAlgo::Sha1 => TotpAlgo::Sha1,
|
||||
ProtoTotpAlgo::Sha256 => TotpAlgo::Sha256,
|
||||
ProtoTotpAlgo::Sha512 => TotpAlgo::Sha512,
|
||||
},
|
||||
step: value.step,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TOTP {
|
||||
pub fn new(label: String, secret: Vec<u8>, step: u64, algo: TOTPAlgo) -> Self {
|
||||
TOTP {
|
||||
impl Totp {
|
||||
pub fn new(label: String, secret: Vec<u8>, step: u64, algo: TotpAlgo) -> Self {
|
||||
Totp {
|
||||
label,
|
||||
secret,
|
||||
step,
|
||||
|
@ -113,8 +113,8 @@ impl TOTP {
|
|||
pub fn generate_secure(label: String, step: u64) -> Self {
|
||||
let mut rng = rand::thread_rng();
|
||||
let secret: Vec<u8> = (0..SECRET_SIZE_BYTES).map(|_| rng.gen()).collect();
|
||||
let algo = TOTPAlgo::Sha512;
|
||||
TOTP {
|
||||
let algo = TotpAlgo::Sha512;
|
||||
Totp {
|
||||
label,
|
||||
secret,
|
||||
step,
|
||||
|
@ -128,40 +128,40 @@ impl TOTP {
|
|||
k: self.secret.clone(),
|
||||
s: self.step,
|
||||
a: match self.algo {
|
||||
TOTPAlgo::Sha1 => DbTotpAlgoV1::S1,
|
||||
TOTPAlgo::Sha256 => DbTotpAlgoV1::S256,
|
||||
TOTPAlgo::Sha512 => DbTotpAlgoV1::S512,
|
||||
TotpAlgo::Sha1 => DbTotpAlgoV1::S1,
|
||||
TotpAlgo::Sha256 => DbTotpAlgoV1::S256,
|
||||
TotpAlgo::Sha512 => DbTotpAlgoV1::S512,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn digest(&self, counter: u64) -> Result<u32, TOTPError> {
|
||||
fn digest(&self, counter: u64) -> Result<u32, TotpError> {
|
||||
let hmac = self.algo.digest(&self.secret, counter)?;
|
||||
// Now take the hmac and encode it as hotp expects.
|
||||
// https://tools.ietf.org/html/rfc4226#page-7
|
||||
let offset = hmac
|
||||
.last()
|
||||
.map(|v| (v & 0xf) as usize)
|
||||
.ok_or(TOTPError::HmacError)?;
|
||||
.ok_or(TotpError::HmacError)?;
|
||||
let bytes: [u8; 4] = hmac[offset..offset + 4]
|
||||
.try_into()
|
||||
.map_err(|_| TOTPError::HmacError)?;
|
||||
.map_err(|_| TotpError::HmacError)?;
|
||||
|
||||
let otp = u32::from_be_bytes(bytes);
|
||||
Ok((otp & 0x7fff_ffff) % 1_000_000)
|
||||
}
|
||||
|
||||
pub fn do_totp_duration_from_epoch(&self, time: &Duration) -> Result<u32, TOTPError> {
|
||||
pub fn do_totp_duration_from_epoch(&self, time: &Duration) -> Result<u32, TotpError> {
|
||||
let secs = time.as_secs();
|
||||
// do the window calculation
|
||||
let counter = secs / self.step;
|
||||
self.digest(counter)
|
||||
}
|
||||
|
||||
pub fn do_totp(&self, time: &SystemTime) -> Result<u32, TOTPError> {
|
||||
pub fn do_totp(&self, time: &SystemTime) -> Result<u32, TotpError> {
|
||||
let dur = time
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map_err(|_| TOTPError::TimeError)?;
|
||||
.map_err(|_| TotpError::TimeError)?;
|
||||
self.do_totp_duration_from_epoch(&dur)
|
||||
}
|
||||
|
||||
|
@ -173,8 +173,8 @@ impl TOTP {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn to_proto(&self, accountname: &str, issuer: &str) -> ProtoTOTP {
|
||||
ProtoTOTP {
|
||||
pub fn to_proto(&self, accountname: &str, issuer: &str) -> ProtoTotp {
|
||||
ProtoTotp {
|
||||
accountname: accountname
|
||||
.replace(":", "")
|
||||
.replace("%3A", "")
|
||||
|
@ -186,9 +186,9 @@ impl TOTP {
|
|||
secret: self.secret.clone(),
|
||||
step: self.step,
|
||||
algo: match self.algo {
|
||||
TOTPAlgo::Sha1 => ProtoTOTPAlgo::Sha1,
|
||||
TOTPAlgo::Sha256 => ProtoTOTPAlgo::Sha256,
|
||||
TOTPAlgo::Sha512 => ProtoTOTPAlgo::Sha512,
|
||||
TotpAlgo::Sha1 => ProtoTotpAlgo::Sha1,
|
||||
TotpAlgo::Sha256 => ProtoTotpAlgo::Sha256,
|
||||
TotpAlgo::Sha512 => ProtoTotpAlgo::Sha512,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -196,21 +196,21 @@ impl TOTP {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::credential::totp::{TOTPAlgo, TOTPError, TOTP, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::totp::{Totp, TotpAlgo, TotpError, TOTP_DEFAULT_STEP};
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn hotp_basic() {
|
||||
let otp_sha1 = TOTP::new("".to_string(), vec![0], 30, TOTPAlgo::Sha1);
|
||||
let otp_sha1 = Totp::new("".to_string(), vec![0], 30, TotpAlgo::Sha1);
|
||||
assert!(otp_sha1.digest(0) == Ok(328482));
|
||||
let otp_sha256 = TOTP::new("".to_string(), vec![0], 30, TOTPAlgo::Sha256);
|
||||
let otp_sha256 = Totp::new("".to_string(), vec![0], 30, TotpAlgo::Sha256);
|
||||
assert!(otp_sha256.digest(0) == Ok(356306));
|
||||
let otp_sha512 = TOTP::new("".to_string(), vec![0], 30, TOTPAlgo::Sha512);
|
||||
let otp_sha512 = Totp::new("".to_string(), vec![0], 30, TotpAlgo::Sha512);
|
||||
assert!(otp_sha512.digest(0) == Ok(674061));
|
||||
}
|
||||
|
||||
fn do_test(key: Vec<u8>, algo: TOTPAlgo, secs: u64, step: u64, expect: Result<u32, TOTPError>) {
|
||||
let otp = TOTP::new("".to_string(), key.clone(), step, algo.clone());
|
||||
fn do_test(key: Vec<u8>, algo: TotpAlgo, secs: u64, step: u64, expect: Result<u32, TotpError>) {
|
||||
let otp = Totp::new("".to_string(), key.clone(), step, algo.clone());
|
||||
let d = Duration::from_secs(secs);
|
||||
let r = otp.do_totp_duration_from_epoch(&d);
|
||||
debug!(
|
||||
|
@ -224,14 +224,14 @@ mod tests {
|
|||
fn totp_sha1_vectors() {
|
||||
do_test(
|
||||
vec![0x00, 0x00, 0x00, 0x00],
|
||||
TOTPAlgo::Sha1,
|
||||
TotpAlgo::Sha1,
|
||||
1585368920,
|
||||
TOTP_DEFAULT_STEP,
|
||||
Ok(728926),
|
||||
);
|
||||
do_test(
|
||||
vec![0x00, 0xaa, 0xbb, 0xcc],
|
||||
TOTPAlgo::Sha1,
|
||||
TotpAlgo::Sha1,
|
||||
1585369498,
|
||||
TOTP_DEFAULT_STEP,
|
||||
Ok(985074),
|
||||
|
@ -242,14 +242,14 @@ mod tests {
|
|||
fn totp_sha256_vectors() {
|
||||
do_test(
|
||||
vec![0x00, 0x00, 0x00, 0x00],
|
||||
TOTPAlgo::Sha256,
|
||||
TotpAlgo::Sha256,
|
||||
1585369682,
|
||||
TOTP_DEFAULT_STEP,
|
||||
Ok(795483),
|
||||
);
|
||||
do_test(
|
||||
vec![0x00, 0xaa, 0xbb, 0xcc],
|
||||
TOTPAlgo::Sha256,
|
||||
TotpAlgo::Sha256,
|
||||
1585369689,
|
||||
TOTP_DEFAULT_STEP,
|
||||
Ok(728402),
|
||||
|
@ -260,14 +260,14 @@ mod tests {
|
|||
fn totp_sha512_vectors() {
|
||||
do_test(
|
||||
vec![0x00, 0x00, 0x00, 0x00],
|
||||
TOTPAlgo::Sha512,
|
||||
TotpAlgo::Sha512,
|
||||
1585369775,
|
||||
TOTP_DEFAULT_STEP,
|
||||
Ok(587735),
|
||||
);
|
||||
do_test(
|
||||
vec![0x00, 0xaa, 0xbb, 0xcc],
|
||||
TOTPAlgo::Sha512,
|
||||
TotpAlgo::Sha512,
|
||||
1585369780,
|
||||
TOTP_DEFAULT_STEP,
|
||||
Ok(952181),
|
||||
|
|
|
@ -246,10 +246,7 @@ impl Event {
|
|||
}
|
||||
|
||||
pub fn is_internal(&self) -> bool {
|
||||
match self.origin {
|
||||
EventOrigin::Internal => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.origin, EventOrigin::Internal)
|
||||
}
|
||||
|
||||
pub fn get_uuid(&self) -> Option<Uuid> {
|
||||
|
|
|
@ -75,7 +75,7 @@ pub fn f_andnot(fc: FC) -> FC {
|
|||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_self<'a>() -> FC<'a> {
|
||||
FC::SelfUUID
|
||||
FC::SelfUuid
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -116,7 +116,7 @@ pub enum FC<'a> {
|
|||
And(Vec<FC<'a>>),
|
||||
Inclusion(Vec<FC<'a>>),
|
||||
AndNot(Box<FC<'a>>),
|
||||
SelfUUID,
|
||||
SelfUuid,
|
||||
// Not(Box<FC>),
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ enum FilterComp {
|
|||
And(Vec<FilterComp>),
|
||||
Inclusion(Vec<FilterComp>),
|
||||
AndNot(Box<FilterComp>),
|
||||
SelfUUID,
|
||||
SelfUuid,
|
||||
// Does this mean we can add a true not to the type now?
|
||||
// Not(Box<FilterComp>),
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ impl Filter<FilterValid> {
|
|||
>,
|
||||
>,
|
||||
) -> Result<Filter<FilterValidResolved>, OperationError> {
|
||||
// Given a filter, resolve Not and SelfUUID to real terms.
|
||||
// Given a filter, resolve Not and SelfUuid to real terms.
|
||||
//
|
||||
// The benefit of moving optimisation to this step is from various inputs, we can
|
||||
// get to a resolved + optimised filter, and then we can cache those outputs in many
|
||||
|
@ -323,7 +323,7 @@ impl Filter<FilterValid> {
|
|||
None => f.fast_optimise(),
|
||||
}
|
||||
})
|
||||
.ok_or(OperationError::FilterUUIDResolution)?,
|
||||
.ok_or(OperationError::FilterUuidResolution)?,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -546,7 +546,7 @@ impl FilterComp {
|
|||
FC::And(v) => FilterComp::And(v.into_iter().map(FilterComp::new).collect()),
|
||||
FC::Inclusion(v) => FilterComp::Inclusion(v.into_iter().map(FilterComp::new).collect()),
|
||||
FC::AndNot(b) => FilterComp::AndNot(Box::new(FilterComp::new(*b))),
|
||||
FC::SelfUUID => FilterComp::SelfUUID,
|
||||
FC::SelfUuid => FilterComp::SelfUuid,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -594,7 +594,7 @@ impl FilterComp {
|
|||
FilterComp::And(vs) => vs.iter().for_each(|f| f.get_attr_set(r_set)),
|
||||
FilterComp::Inclusion(vs) => vs.iter().for_each(|f| f.get_attr_set(r_set)),
|
||||
FilterComp::AndNot(f) => f.get_attr_set(r_set),
|
||||
FilterComp::SelfUUID => {
|
||||
FilterComp::SelfUuid => {
|
||||
r_set.insert("uuid");
|
||||
}
|
||||
}
|
||||
|
@ -714,9 +714,9 @@ impl FilterComp {
|
|||
.validate(schema)
|
||||
.map(|r_filter| FilterComp::AndNot(Box::new(r_filter)))
|
||||
}
|
||||
FilterComp::SelfUUID => {
|
||||
FilterComp::SelfUuid => {
|
||||
// Pretty hard to mess this one up ;)
|
||||
Ok(FilterComp::SelfUUID)
|
||||
Ok(FilterComp::SelfUuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -770,7 +770,7 @@ impl FilterComp {
|
|||
.ok_or(OperationError::ResourceLimit)?;
|
||||
FilterComp::AndNot(Box::new(Self::from_ro(audit, l, qs, ndepth, elems)?))
|
||||
}
|
||||
ProtoFilter::SelfUUID => FilterComp::SelfUUID,
|
||||
ProtoFilter::SelfUuid => FilterComp::SelfUuid,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -824,7 +824,7 @@ impl FilterComp {
|
|||
|
||||
FilterComp::AndNot(Box::new(Self::from_rw(audit, l, qs, ndepth, elems)?))
|
||||
}
|
||||
ProtoFilter::SelfUUID => FilterComp::SelfUUID,
|
||||
ProtoFilter::SelfUuid => FilterComp::SelfUuid,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1036,7 +1036,7 @@ impl FilterResolved {
|
|||
idxmeta,
|
||||
)))
|
||||
}
|
||||
FilterComp::SelfUUID => panic!("Not possible to resolve SelfUUID in from_invalid!"),
|
||||
FilterComp::SelfUuid => panic!("Not possible to resolve SelfUuid in from_invalid!"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1092,7 +1092,7 @@ impl FilterResolved {
|
|||
FilterResolved::resolve_idx((*f).clone(), ev, idxmeta)
|
||||
.map(|fi| FilterResolved::AndNot(Box::new(fi)))
|
||||
}
|
||||
FilterComp::SelfUUID => ev.get_uuid().map(|uuid| {
|
||||
FilterComp::SelfUuid => ev.get_uuid().map(|uuid| {
|
||||
let uuid_s = AttrString::from("uuid");
|
||||
let idxkref = IdxKeyRef::new(&uuid_s, &IndexType::EQUALITY);
|
||||
let idx = idxmeta.contains(&idxkref as &dyn IdxKeyToRef);
|
||||
|
@ -1138,7 +1138,7 @@ impl FilterResolved {
|
|||
.map(|fi| FilterResolved::AndNot(Box::new(fi)))
|
||||
}
|
||||
|
||||
FilterComp::SelfUUID => ev.get_uuid().map(|uuid| {
|
||||
FilterComp::SelfUuid => ev.get_uuid().map(|uuid| {
|
||||
FilterResolved::Eq(
|
||||
AttrString::from("uuid"),
|
||||
PartialValue::new_uuid(uuid),
|
||||
|
@ -1173,10 +1173,7 @@ impl FilterResolved {
|
|||
let (f_list_inc, mut f_list_new): (Vec<_>, Vec<_>) = f_list
|
||||
.iter()
|
||||
.map(|f_ref| f_ref.optimise())
|
||||
.partition(|f| match f {
|
||||
FilterResolved::Inclusion(_) => true,
|
||||
_ => false,
|
||||
});
|
||||
.partition(|f| matches!(f, FilterResolved::Inclusion(_)));
|
||||
|
||||
f_list_inc.into_iter().for_each(|fc| {
|
||||
if let FilterResolved::Inclusion(mut l) = fc {
|
||||
|
@ -1193,10 +1190,7 @@ impl FilterResolved {
|
|||
let (f_list_and, mut f_list_new): (Vec<_>, Vec<_>) = f_list
|
||||
.iter()
|
||||
.map(|f_ref| f_ref.optimise())
|
||||
.partition(|f| match f {
|
||||
FilterResolved::And(_) => true,
|
||||
_ => false,
|
||||
});
|
||||
.partition(|f| matches!(f, FilterResolved::And(_)));
|
||||
|
||||
// now, iterate over this list - for each "and" term, fold
|
||||
// it's elements to this level.
|
||||
|
@ -1234,10 +1228,7 @@ impl FilterResolved {
|
|||
// Optimise all inner items.
|
||||
.map(|f_ref| f_ref.optimise())
|
||||
// Split out inner-or terms to fold into this term.
|
||||
.partition(|f| match f {
|
||||
FilterResolved::Or(_) => true,
|
||||
_ => false,
|
||||
});
|
||||
.partition(|f| matches!(f, FilterResolved::Or(_)));
|
||||
|
||||
// Append the inner terms.
|
||||
f_list_or.into_iter().for_each(|fc| {
|
||||
|
@ -1264,10 +1255,7 @@ impl FilterResolved {
|
|||
}
|
||||
|
||||
pub fn is_andnot(&self) -> bool {
|
||||
match self {
|
||||
FilterResolved::AndNot(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, FilterResolved::AndNot(_))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use kanidm_proto::v1::UserAuthToken;
|
|||
|
||||
use crate::constants::UUID_ANONYMOUS;
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::totp::TOTP;
|
||||
use crate::credential::totp::Totp;
|
||||
use crate::credential::{softlock::CredSoftLockPolicy, Credential};
|
||||
use crate::idm::claim::Claim;
|
||||
use crate::idm::group::Group;
|
||||
|
@ -260,7 +260,7 @@ impl Account {
|
|||
|
||||
pub(crate) fn gen_totp_mod(
|
||||
&self,
|
||||
token: TOTP,
|
||||
token: Totp,
|
||||
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
|
||||
match &self.primary {
|
||||
// Change the cred
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::prelude::*;
|
|||
use kanidm_proto::v1::OperationError;
|
||||
use kanidm_proto::v1::{AuthAllowed, AuthCredential, AuthMech};
|
||||
|
||||
use crate::credential::{totp::TOTP, Credential, CredentialType, Password};
|
||||
use crate::credential::{totp::Totp, Credential, CredentialType, Password};
|
||||
|
||||
use crate::idm::delayed::{DelayedAction, PasswordUpgrade, WebauthnCounterIncrement};
|
||||
// use crossbeam::channel::Sender;
|
||||
|
@ -50,7 +50,7 @@ enum CredVerifyState {
|
|||
struct CredMfa {
|
||||
pw: Password,
|
||||
pw_state: CredVerifyState,
|
||||
totp: Option<TOTP>,
|
||||
totp: Option<Totp>,
|
||||
wan: Option<(RequestChallengeResponse, AuthenticationState)>,
|
||||
mfa_state: CredVerifyState,
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ enum CredHandler {
|
|||
Anonymous,
|
||||
// AppPassword (?)
|
||||
Password(Password),
|
||||
PasswordMFA(Box<CredMfa>),
|
||||
PasswordMfa(Box<CredMfa>),
|
||||
Webauthn(CredWebauthn),
|
||||
// Webauthn + Password
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ impl CredHandler {
|
|||
CredentialType::Password(pw) | CredentialType::GeneratedPassword(pw) => {
|
||||
Ok(CredHandler::Password(pw.clone()))
|
||||
}
|
||||
CredentialType::PasswordMFA(pw, maybe_totp, maybe_wan) => {
|
||||
CredentialType::PasswordMfa(pw, maybe_totp, maybe_wan) => {
|
||||
let wan = if !maybe_wan.is_empty() {
|
||||
webauthn
|
||||
.generate_challenge_authenticate(maybe_wan.values().cloned().collect())
|
||||
|
@ -111,12 +111,12 @@ impl CredHandler {
|
|||
if cmfa.totp.is_none() && cmfa.wan.is_none() {
|
||||
lsecurity_critical!(
|
||||
au,
|
||||
"Unable to create CredHandler::PasswordMFA - totp and webauthn are both not present. Credentials MAY be corrupt!"
|
||||
"Unable to create CredHandler::PasswordMfa - totp and webauthn are both not present. Credentials MAY be corrupt!"
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(CredHandler::PasswordMFA(cmfa))
|
||||
Ok(CredHandler::PasswordMfa(cmfa))
|
||||
}
|
||||
CredentialType::Webauthn(wan) => webauthn
|
||||
.generate_challenge_authenticate(wan.values().cloned().collect())
|
||||
|
@ -258,19 +258,19 @@ impl CredHandler {
|
|||
CredState::Denied(BAD_WEBAUTHN_MSG)
|
||||
})
|
||||
}
|
||||
(AuthCredential::TOTP(totp_chal), Some(totp), _) => {
|
||||
(AuthCredential::Totp(totp_chal), Some(totp), _) => {
|
||||
if totp.verify(*totp_chal, ts) {
|
||||
pw_mfa.mfa_state = CredVerifyState::Success;
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Continue - TOTP OK, password -"
|
||||
"Handler::PasswordMfa -> Result::Continue - TOTP OK, password -"
|
||||
);
|
||||
CredState::Continue(vec![AuthAllowed::Password])
|
||||
} else {
|
||||
pw_mfa.mfa_state = CredVerifyState::Fail;
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Denied - TOTP Fail, password -"
|
||||
"Handler::PasswordMfa -> Result::Denied - TOTP Fail, password -"
|
||||
);
|
||||
CredState::Denied(BAD_TOTP_MSG)
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ impl CredHandler {
|
|||
_ => {
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Denied - invalid cred type for handler"
|
||||
"Handler::PasswordMfa -> Result::Denied - invalid cred type for handler"
|
||||
);
|
||||
CredState::Denied(BAD_AUTH_TYPE_MSG)
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ impl CredHandler {
|
|||
pw_mfa.pw_state = CredVerifyState::Fail;
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Denied - Password found in badlist during login"
|
||||
"Handler::PasswordMfa -> Result::Denied - Password found in badlist during login"
|
||||
);
|
||||
CredState::Denied(PW_BADLIST_MSG)
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ impl CredHandler {
|
|||
pw_mfa.pw_state = CredVerifyState::Success;
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Success - TOTP OK, password OK"
|
||||
"Handler::PasswordMfa -> Result::Success - TOTP OK, password OK"
|
||||
);
|
||||
Self::maybe_pw_upgrade(
|
||||
au,
|
||||
|
@ -318,7 +318,7 @@ impl CredHandler {
|
|||
pw_mfa.pw_state = CredVerifyState::Fail;
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Denied - TOTP OK, password Fail"
|
||||
"Handler::PasswordMfa -> Result::Denied - TOTP OK, password Fail"
|
||||
);
|
||||
CredState::Denied(BAD_PASSWORD_MSG)
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ impl CredHandler {
|
|||
_ => {
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Denied - invalid cred type for handler"
|
||||
"Handler::PasswordMfa -> Result::Denied - invalid cred type for handler"
|
||||
);
|
||||
CredState::Denied(BAD_AUTH_TYPE_MSG)
|
||||
}
|
||||
|
@ -335,12 +335,12 @@ impl CredHandler {
|
|||
_ => {
|
||||
lsecurity!(
|
||||
au,
|
||||
"Handler::PasswordMFA -> Result::Denied - invalid credential mfa and pw state"
|
||||
"Handler::PasswordMfa -> Result::Denied - invalid credential mfa and pw state"
|
||||
);
|
||||
CredState::Denied(BAD_AUTH_TYPE_MSG)
|
||||
}
|
||||
}
|
||||
} // end CredHandler::PasswordMFA
|
||||
} // end CredHandler::PasswordMfa
|
||||
|
||||
pub fn validate_webauthn(
|
||||
au: &mut AuditScope,
|
||||
|
@ -395,6 +395,7 @@ impl CredHandler {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn validate(
|
||||
&mut self,
|
||||
au: &mut AuditScope,
|
||||
|
@ -410,7 +411,7 @@ impl CredHandler {
|
|||
CredHandler::Password(ref mut pw) => {
|
||||
Self::validate_password(au, cred, pw, who, async_tx, pw_badlist_set)
|
||||
}
|
||||
CredHandler::PasswordMFA(ref mut pw_mfa) => Self::validate_password_mfa(
|
||||
CredHandler::PasswordMfa(ref mut pw_mfa) => Self::validate_password_mfa(
|
||||
au,
|
||||
cred,
|
||||
ts,
|
||||
|
@ -430,10 +431,10 @@ impl CredHandler {
|
|||
match &self {
|
||||
CredHandler::Anonymous => vec![AuthAllowed::Anonymous],
|
||||
CredHandler::Password(_) => vec![AuthAllowed::Password],
|
||||
CredHandler::PasswordMFA(ref pw_mfa) => pw_mfa
|
||||
CredHandler::PasswordMfa(ref pw_mfa) => pw_mfa
|
||||
.totp
|
||||
.iter()
|
||||
.map(|_| AuthAllowed::TOTP)
|
||||
.map(|_| AuthAllowed::Totp)
|
||||
.chain(
|
||||
pw_mfa
|
||||
.wan
|
||||
|
@ -449,7 +450,7 @@ impl CredHandler {
|
|||
match (self, mech) {
|
||||
(CredHandler::Anonymous, AuthMech::Anonymous)
|
||||
| (CredHandler::Password(_), AuthMech::Password)
|
||||
| (CredHandler::PasswordMFA(_), AuthMech::PasswordMFA)
|
||||
| (CredHandler::PasswordMfa(_), AuthMech::PasswordMfa)
|
||||
| (CredHandler::Webauthn(_), AuthMech::Webauthn) => true,
|
||||
(_, _) => false,
|
||||
}
|
||||
|
@ -459,7 +460,7 @@ impl CredHandler {
|
|||
match self {
|
||||
CredHandler::Anonymous => AuthMech::Anonymous,
|
||||
CredHandler::Password(_) => AuthMech::Password,
|
||||
CredHandler::PasswordMFA(_) => AuthMech::PasswordMFA,
|
||||
CredHandler::PasswordMfa(_) => AuthMech::PasswordMfa,
|
||||
CredHandler::Webauthn(_) => AuthMech::Webauthn,
|
||||
}
|
||||
}
|
||||
|
@ -727,7 +728,7 @@ impl AuthSession {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::totp::{TOTP, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::webauthn::WebauthnDomainConfig;
|
||||
use crate::credential::Credential;
|
||||
use crate::idm::authsession::{
|
||||
|
@ -994,7 +995,7 @@ mod tests {
|
|||
if let AuthState::Choose(auth_mechs) = state {
|
||||
assert!(
|
||||
true == auth_mechs.iter().fold(false, |acc, x| match x {
|
||||
AuthMech::PasswordMFA => true,
|
||||
AuthMech::PasswordMfa => true,
|
||||
_ => acc,
|
||||
})
|
||||
);
|
||||
|
@ -1003,7 +1004,7 @@ mod tests {
|
|||
}
|
||||
|
||||
let state = session
|
||||
.start_session($audit, &AuthMech::PasswordMFA)
|
||||
.start_session($audit, &AuthMech::PasswordMfa)
|
||||
.expect("Failed to select anonymous mech.");
|
||||
|
||||
let mut rchal = None;
|
||||
|
@ -1016,7 +1017,7 @@ mod tests {
|
|||
rchal = Some(chal.clone());
|
||||
true
|
||||
}
|
||||
AuthAllowed::TOTP => true,
|
||||
AuthAllowed::Totp => true,
|
||||
_ => acc,
|
||||
})
|
||||
);
|
||||
|
@ -1043,7 +1044,7 @@ mod tests {
|
|||
let ts = Duration::from_secs(12345);
|
||||
|
||||
// manually load in a cred
|
||||
let totp = TOTP::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
|
||||
let totp_good = totp
|
||||
.do_totp_duration_from_epoch(&ts)
|
||||
|
@ -1106,7 +1107,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_bad),
|
||||
&AuthCredential::Totp(totp_bad),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1123,7 +1124,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_good),
|
||||
&AuthCredential::Totp(totp_good),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1150,7 +1151,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_good),
|
||||
&AuthCredential::Totp(totp_good),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1190,7 +1191,7 @@ mod tests {
|
|||
let ts = Duration::from_secs(12345);
|
||||
|
||||
// manually load in a cred
|
||||
let totp = TOTP::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
|
||||
let totp_good = totp
|
||||
.do_totp_duration_from_epoch(&ts)
|
||||
|
@ -1218,7 +1219,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_good),
|
||||
&AuthCredential::Totp(totp_good),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1498,7 +1499,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(0),
|
||||
&AuthCredential::Totp(0),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1627,7 +1628,7 @@ mod tests {
|
|||
|
||||
let (webauthn, mut wa, wan_cred) = setup_webauthn(account.name.as_str());
|
||||
|
||||
let totp = TOTP::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp_good = totp
|
||||
.do_totp_duration_from_epoch(&ts)
|
||||
.expect("failed to perform totp.");
|
||||
|
@ -1671,7 +1672,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_bad),
|
||||
&AuthCredential::Totp(totp_bad),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1748,7 +1749,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_good),
|
||||
&AuthCredential::Totp(totp_good),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
@ -1774,7 +1775,7 @@ mod tests {
|
|||
|
||||
match session.validate_creds(
|
||||
&mut audit,
|
||||
&AuthCredential::TOTP(totp_good),
|
||||
&AuthCredential::Totp(totp_good),
|
||||
&ts,
|
||||
&async_tx,
|
||||
&webauthn,
|
||||
|
|
|
@ -264,13 +264,13 @@ impl UnixUserAuthEvent {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GenerateTOTPEvent {
|
||||
pub struct GenerateTotpEvent {
|
||||
pub event: Event,
|
||||
pub target: Uuid,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl GenerateTOTPEvent {
|
||||
impl GenerateTotpEvent {
|
||||
pub fn from_parts(
|
||||
audit: &mut AuditScope,
|
||||
qs: &QueryServerWriteTransaction,
|
||||
|
@ -280,7 +280,7 @@ impl GenerateTOTPEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
let e = Event::from_rw_uat(audit, qs, uat)?;
|
||||
|
||||
Ok(GenerateTOTPEvent {
|
||||
Ok(GenerateTotpEvent {
|
||||
event: e,
|
||||
target,
|
||||
label,
|
||||
|
@ -291,7 +291,7 @@ impl GenerateTOTPEvent {
|
|||
pub fn new_internal(target: Uuid) -> Self {
|
||||
let e = Event::from_internal();
|
||||
|
||||
GenerateTOTPEvent {
|
||||
GenerateTotpEvent {
|
||||
event: e,
|
||||
target,
|
||||
label: "internal_token".to_string(),
|
||||
|
@ -300,14 +300,14 @@ impl GenerateTOTPEvent {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VerifyTOTPEvent {
|
||||
pub struct VerifyTotpEvent {
|
||||
pub event: Event,
|
||||
pub target: Uuid,
|
||||
pub session: Uuid,
|
||||
pub chal: u32,
|
||||
}
|
||||
|
||||
impl VerifyTOTPEvent {
|
||||
impl VerifyTotpEvent {
|
||||
pub fn from_parts(
|
||||
audit: &mut AuditScope,
|
||||
qs: &QueryServerWriteTransaction,
|
||||
|
@ -318,7 +318,7 @@ impl VerifyTOTPEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
let e = Event::from_rw_uat(audit, qs, uat)?;
|
||||
|
||||
Ok(VerifyTOTPEvent {
|
||||
Ok(VerifyTotpEvent {
|
||||
event: e,
|
||||
target,
|
||||
session,
|
||||
|
@ -330,7 +330,7 @@ impl VerifyTOTPEvent {
|
|||
pub fn new_internal(target: Uuid, session: Uuid, chal: u32) -> Self {
|
||||
let e = Event::from_internal();
|
||||
|
||||
VerifyTOTPEvent {
|
||||
VerifyTotpEvent {
|
||||
event: e,
|
||||
target,
|
||||
session,
|
||||
|
@ -340,12 +340,12 @@ impl VerifyTOTPEvent {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RemoveTOTPEvent {
|
||||
pub struct RemoveTotpEvent {
|
||||
pub event: Event,
|
||||
pub target: Uuid,
|
||||
}
|
||||
|
||||
impl RemoveTOTPEvent {
|
||||
impl RemoveTotpEvent {
|
||||
pub fn from_parts(
|
||||
audit: &mut AuditScope,
|
||||
qs: &QueryServerWriteTransaction,
|
||||
|
@ -354,14 +354,14 @@ impl RemoveTOTPEvent {
|
|||
) -> Result<Self, OperationError> {
|
||||
let e = Event::from_rw_uat(audit, qs, uat)?;
|
||||
|
||||
Ok(RemoveTOTPEvent { event: e, target })
|
||||
Ok(RemoveTotpEvent { event: e, target })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn new_internal(target: Uuid) -> Self {
|
||||
let e = Event::from_internal();
|
||||
|
||||
RemoveTOTPEvent { event: e, target }
|
||||
RemoveTotpEvent { event: e, target }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::audit::AuditScope;
|
||||
use crate::credential::totp::{TOTP, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::webauthn::WebauthnDomainConfig;
|
||||
use crate::event::EventOriginId;
|
||||
use crate::idm::account::Account;
|
||||
use kanidm_proto::v1::TOTPSecret;
|
||||
use kanidm_proto::v1::TotpSecret;
|
||||
use kanidm_proto::v1::{OperationError, SetCredentialResponse};
|
||||
use std::mem;
|
||||
use std::time::Duration;
|
||||
|
@ -15,13 +15,13 @@ use webauthn_rs::RegistrationState as WebauthnRegistrationState;
|
|||
use webauthn_rs::{proto::UserVerificationPolicy, Webauthn};
|
||||
|
||||
pub(crate) enum MfaRegCred {
|
||||
TOTP(TOTP),
|
||||
Totp(Totp),
|
||||
Webauthn(String, WebauthnCredential),
|
||||
}
|
||||
|
||||
pub(crate) enum MfaRegNext {
|
||||
Success,
|
||||
TOTPCheck(TOTPSecret),
|
||||
TotpCheck(TotpSecret),
|
||||
WebauthnChallenge(CreationChallengeResponse),
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ impl MfaRegNext {
|
|||
pub fn to_proto(self, u: Uuid) -> SetCredentialResponse {
|
||||
match self {
|
||||
MfaRegNext::Success => SetCredentialResponse::Success,
|
||||
MfaRegNext::TOTPCheck(secret) => SetCredentialResponse::TOTPCheck(u, secret),
|
||||
MfaRegNext::TotpCheck(secret) => SetCredentialResponse::TotpCheck(u, secret),
|
||||
MfaRegNext::WebauthnChallenge(ccr) => {
|
||||
SetCredentialResponse::WebauthnCreateChallenge(u, ccr)
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ impl MfaRegNext {
|
|||
|
||||
#[derive(Clone)]
|
||||
enum MfaRegState {
|
||||
TOTPInit(TOTP),
|
||||
TOTPDone,
|
||||
TotpInit(Totp),
|
||||
TotpDone,
|
||||
WebauthnInit(String, WebauthnRegistrationState),
|
||||
WebauthnDone,
|
||||
}
|
||||
|
@ -65,13 +65,13 @@ impl MfaRegSession {
|
|||
) -> Result<(Self, MfaRegNext), OperationError> {
|
||||
// Based on the req, init our session, and the return the next step.
|
||||
// Store the ID of the event that start's the attempt
|
||||
let token = TOTP::generate_secure(label, TOTP_DEFAULT_STEP);
|
||||
let token = Totp::generate_secure(label, TOTP_DEFAULT_STEP);
|
||||
|
||||
let accountname = account.name.as_str();
|
||||
let issuer = account.spn.as_str();
|
||||
let next = MfaRegNext::TOTPCheck(token.to_proto(accountname, issuer));
|
||||
let next = MfaRegNext::TotpCheck(token.to_proto(accountname, issuer));
|
||||
|
||||
let state = MfaRegState::TOTPInit(token);
|
||||
let state = MfaRegState::TotpInit(token);
|
||||
let s = MfaRegSession {
|
||||
origin,
|
||||
account,
|
||||
|
@ -93,13 +93,13 @@ impl MfaRegSession {
|
|||
};
|
||||
|
||||
match &self.state {
|
||||
MfaRegState::TOTPInit(token) => {
|
||||
MfaRegState::TotpInit(token) => {
|
||||
if token.verify(chal, ct) {
|
||||
let mut nstate = MfaRegState::TOTPDone;
|
||||
let mut nstate = MfaRegState::TotpDone;
|
||||
mem::swap(&mut self.state, &mut nstate);
|
||||
match nstate {
|
||||
MfaRegState::TOTPInit(token) => {
|
||||
Ok((MfaRegNext::Success, Some(MfaRegCred::TOTP(token))))
|
||||
MfaRegState::TotpInit(token) => {
|
||||
Ok((MfaRegNext::Success, Some(MfaRegCred::Totp(token))))
|
||||
}
|
||||
_ => Err(OperationError::InvalidState),
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ impl MfaRegSession {
|
|||
let accountname = self.account.name.as_str();
|
||||
let issuer = self.account.spn.as_str();
|
||||
Ok((
|
||||
MfaRegNext::TOTPCheck(token.to_proto(accountname, issuer)),
|
||||
MfaRegNext::TotpCheck(token.to_proto(accountname, issuer)),
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ use crate::event::{AuthEvent, AuthEventStep, AuthResult};
|
|||
use crate::idm::account::Account;
|
||||
use crate::idm::authsession::AuthSession;
|
||||
use crate::idm::event::{
|
||||
CredentialStatusEvent, GeneratePasswordEvent, GenerateTOTPEvent, LdapAuthEvent,
|
||||
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, RemoveTOTPEvent,
|
||||
CredentialStatusEvent, GeneratePasswordEvent, GenerateTotpEvent, LdapAuthEvent,
|
||||
PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, RemoveTotpEvent,
|
||||
RemoveWebauthnEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent,
|
||||
UnixUserTokenEvent, VerifyTOTPEvent, WebauthnDoRegisterEvent, WebauthnInitRegisterEvent,
|
||||
UnixUserTokenEvent, VerifyTotpEvent, WebauthnDoRegisterEvent, WebauthnInitRegisterEvent,
|
||||
};
|
||||
use crate::idm::mfareg::{MfaRegCred, MfaRegNext, MfaRegSession};
|
||||
use crate::idm::radius::RadiusAccount;
|
||||
|
@ -1294,7 +1294,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
pub fn generate_account_totp(
|
||||
&mut self,
|
||||
au: &mut AuditScope,
|
||||
gte: &GenerateTOTPEvent,
|
||||
gte: &GenerateTotpEvent,
|
||||
ct: Duration,
|
||||
) -> Result<SetCredentialResponse, OperationError> {
|
||||
let account = self.target_to_account(au, >e.target)?;
|
||||
|
@ -1318,7 +1318,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
pub fn verify_account_totp(
|
||||
&mut self,
|
||||
au: &mut AuditScope,
|
||||
vte: &VerifyTOTPEvent,
|
||||
vte: &VerifyTotpEvent,
|
||||
ct: Duration,
|
||||
) -> Result<SetCredentialResponse, OperationError> {
|
||||
let sessionid = vte.session;
|
||||
|
@ -1337,7 +1337,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
e
|
||||
})?;
|
||||
|
||||
if let (MfaRegNext::Success, Some(MfaRegCred::TOTP(token))) = (&next, opt_cred) {
|
||||
if let (MfaRegNext::Success, Some(MfaRegCred::Totp(token))) = (&next, opt_cred) {
|
||||
// Purge the session.
|
||||
let session = self
|
||||
.mfareg_sessions
|
||||
|
@ -1376,7 +1376,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
pub fn remove_account_totp(
|
||||
&mut self,
|
||||
au: &mut AuditScope,
|
||||
rte: &RemoveTOTPEvent,
|
||||
rte: &RemoveTotpEvent,
|
||||
) -> Result<SetCredentialResponse, OperationError> {
|
||||
ltrace!(au, "Attempting to remove totp -> {:?}", rte.target);
|
||||
|
||||
|
@ -1534,14 +1534,14 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::totp::TOTP;
|
||||
use crate::credential::totp::Totp;
|
||||
use crate::credential::{Credential, Password};
|
||||
use crate::event::{AuthEvent, AuthResult, CreateEvent, ModifyEvent};
|
||||
use crate::idm::delayed::{DelayedAction, WebauthnCounterIncrement};
|
||||
use crate::idm::event::{
|
||||
GenerateTOTPEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
|
||||
RemoveTOTPEvent, RemoveWebauthnEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent,
|
||||
UnixUserAuthEvent, UnixUserTokenEvent, VerifyTOTPEvent, WebauthnDoRegisterEvent,
|
||||
GenerateTotpEvent, PasswordChangeEvent, RadiusAuthTokenEvent, RegenerateRadiusSecretEvent,
|
||||
RemoveTotpEvent, RemoveWebauthnEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent,
|
||||
UnixUserAuthEvent, UnixUserTokenEvent, VerifyTotpEvent, WebauthnDoRegisterEvent,
|
||||
WebauthnInitRegisterEvent,
|
||||
};
|
||||
use crate::idm::AuthState;
|
||||
|
@ -2312,7 +2312,7 @@ mod tests {
|
|||
let mut idms_prox_write = idms.proxy_write(ct.clone());
|
||||
|
||||
// verify with no session (fail)
|
||||
let vte1 = VerifyTOTPEvent::new_internal(UUID_ADMIN.clone(), Uuid::new_v4(), 0);
|
||||
let vte1 = VerifyTotpEvent::new_internal(UUID_ADMIN.clone(), Uuid::new_v4(), 0);
|
||||
|
||||
match idms_prox_write.verify_account_totp(au, &vte1, ct.clone()) {
|
||||
Err(e) => {
|
||||
|
@ -2322,18 +2322,18 @@ mod tests {
|
|||
};
|
||||
|
||||
// reg, expire session, attempt verify (fail)
|
||||
let gte1 = GenerateTOTPEvent::new_internal(UUID_ADMIN.clone());
|
||||
let gte1 = GenerateTotpEvent::new_internal(UUID_ADMIN.clone());
|
||||
|
||||
let res = idms_prox_write
|
||||
.generate_account_totp(au, >e1, ct.clone())
|
||||
.unwrap();
|
||||
let sesid = match res {
|
||||
SetCredentialResponse::TOTPCheck(id, _) => id,
|
||||
SetCredentialResponse::TotpCheck(id, _) => id,
|
||||
_ => panic!("invalid state!"),
|
||||
};
|
||||
idms_prox_write.expire_mfareg_sessions(expire.clone());
|
||||
|
||||
let vte2 = VerifyTOTPEvent::new_internal(UUID_ADMIN.clone(), sesid, 0);
|
||||
let vte2 = VerifyTotpEvent::new_internal(UUID_ADMIN.clone(), sesid, 0);
|
||||
|
||||
match idms_prox_write.verify_account_totp(au, &vte1, ct.clone()) {
|
||||
Err(e) => {
|
||||
|
@ -2347,16 +2347,16 @@ mod tests {
|
|||
.generate_account_totp(au, >e1, ct.clone())
|
||||
.unwrap();
|
||||
let (sesid, tok) = match res {
|
||||
SetCredentialResponse::TOTPCheck(id, tok) => (id, tok),
|
||||
SetCredentialResponse::TotpCheck(id, tok) => (id, tok),
|
||||
_ => panic!("invalid state!"),
|
||||
};
|
||||
// get the correct otp
|
||||
let r_tok: TOTP = tok.into();
|
||||
let r_tok: Totp = tok.into();
|
||||
let chal = r_tok
|
||||
.do_totp_duration_from_epoch(&ct)
|
||||
.expect("Failed to do totp?");
|
||||
// attempt the verify
|
||||
let vte3 = VerifyTOTPEvent::new_internal(UUID_ADMIN.clone(), sesid, chal);
|
||||
let vte3 = VerifyTotpEvent::new_internal(UUID_ADMIN.clone(), sesid, chal);
|
||||
|
||||
match idms_prox_write.verify_account_totp(au, &vte3, ct.clone()) {
|
||||
Err(e) => assert!(e == OperationError::InvalidState),
|
||||
|
@ -2375,16 +2375,16 @@ mod tests {
|
|||
.generate_account_totp(au, >e1, ct.clone())
|
||||
.unwrap();
|
||||
let (sesid, tok) = match res {
|
||||
SetCredentialResponse::TOTPCheck(id, tok) => (id, tok),
|
||||
SetCredentialResponse::TotpCheck(id, tok) => (id, tok),
|
||||
_ => panic!("invalid state!"),
|
||||
};
|
||||
// get the correct otp
|
||||
let r_tok: TOTP = tok.into();
|
||||
let r_tok: Totp = tok.into();
|
||||
let chal = r_tok
|
||||
.do_totp_duration_from_epoch(&ct)
|
||||
.expect("Failed to do totp?");
|
||||
// attempt the verify
|
||||
let vte3 = VerifyTOTPEvent::new_internal(UUID_ANONYMOUS.clone(), sesid, chal);
|
||||
let vte3 = VerifyTotpEvent::new_internal(UUID_ANONYMOUS.clone(), sesid, chal);
|
||||
|
||||
match idms_prox_write.verify_account_totp(au, &vte3, ct.clone()) {
|
||||
Err(e) => assert!(e == OperationError::InvalidRequestState),
|
||||
|
@ -2396,14 +2396,14 @@ mod tests {
|
|||
.generate_account_totp(au, >e1, ct.clone())
|
||||
.unwrap();
|
||||
let (_sesid, _tok) = match res {
|
||||
SetCredentialResponse::TOTPCheck(id, tok) => (id, tok),
|
||||
SetCredentialResponse::TotpCheck(id, tok) => (id, tok),
|
||||
_ => panic!("invalid state!"),
|
||||
};
|
||||
|
||||
// We can reuse the OTP/Vte2 from before, since we want the invalid otp.
|
||||
match idms_prox_write.verify_account_totp(au, &vte2, ct.clone()) {
|
||||
// On failure we get back another attempt to setup the token.
|
||||
Ok(SetCredentialResponse::TOTPCheck(_id, _tok)) => {}
|
||||
Ok(SetCredentialResponse::TotpCheck(_id, _tok)) => {}
|
||||
_ => panic!(),
|
||||
};
|
||||
idms_prox_write.expire_mfareg_sessions(expire.clone());
|
||||
|
@ -2414,16 +2414,16 @@ mod tests {
|
|||
.generate_account_totp(au, >e1, ct.clone())
|
||||
.unwrap();
|
||||
let (sesid, tok) = match res {
|
||||
SetCredentialResponse::TOTPCheck(id, tok) => (id, tok),
|
||||
SetCredentialResponse::TotpCheck(id, tok) => (id, tok),
|
||||
_ => panic!("invalid state!"),
|
||||
};
|
||||
// We can't reuse the OTP/Vte from before, since the token seed changes
|
||||
let r_tok: TOTP = tok.into();
|
||||
let r_tok: Totp = tok.into();
|
||||
let chal = r_tok
|
||||
.do_totp_duration_from_epoch(&ct)
|
||||
.expect("Failed to do totp?");
|
||||
// attempt the verify
|
||||
let vte3 = VerifyTOTPEvent::new_internal(UUID_ADMIN.clone(), sesid, chal);
|
||||
let vte3 = VerifyTotpEvent::new_internal(UUID_ADMIN.clone(), sesid, chal);
|
||||
|
||||
match idms_prox_write.verify_account_totp(au, &vte3, ct.clone()) {
|
||||
Ok(SetCredentialResponse::Success) => {}
|
||||
|
@ -2432,7 +2432,7 @@ mod tests {
|
|||
idms_prox_write.expire_mfareg_sessions(expire.clone());
|
||||
|
||||
// Test removing the TOTP and then authing with password only.
|
||||
let rte = RemoveTOTPEvent::new_internal(UUID_ADMIN.clone());
|
||||
let rte = RemoveTotpEvent::new_internal(UUID_ADMIN.clone());
|
||||
idms_prox_write.remove_account_totp(au, &rte).unwrap();
|
||||
assert!(idms_prox_write.commit(au).is_ok());
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ impl LdapServer {
|
|||
) -> Result<Vec<LdapMsg>, OperationError> {
|
||||
ladmin_info!(au, "Attempt LDAP Search for {}", uat.spn);
|
||||
// If the request is "", Base, Present("objectclass"), [], then we want the rootdse.
|
||||
if sr.base == "" && sr.scope == LdapSearchScope::Base {
|
||||
if sr.base.is_empty() && sr.scope == LdapSearchScope::Base {
|
||||
ladmin_info!(au, "LDAP Search success - RootDSE");
|
||||
Ok(vec![
|
||||
sr.gen_result_entry(self.rootdse.clone()),
|
||||
|
@ -296,12 +296,12 @@ impl LdapServer {
|
|||
lsecurity!(
|
||||
au,
|
||||
"Attempt LDAP Bind for {}",
|
||||
if dn == "" { "anonymous" } else { dn }
|
||||
if dn.is_empty() { "anonymous" } else { dn }
|
||||
);
|
||||
let mut idm_auth = idms.auth_async().await;
|
||||
|
||||
let target_uuid: Uuid = if dn == "" {
|
||||
if pw == "" {
|
||||
let target_uuid: Uuid = if dn.is_empty() {
|
||||
if pw.is_empty() {
|
||||
lsecurity!(au, "✅ LDAP Bind success anonymous");
|
||||
*UUID_ANONYMOUS
|
||||
} else {
|
||||
|
@ -321,7 +321,7 @@ impl LdapServer {
|
|||
|
||||
ltrace!(au, "rdn val is -> {:?}", rdn);
|
||||
|
||||
if rdn == "" {
|
||||
if rdn.is_empty() {
|
||||
// That's weird ...
|
||||
return Err(OperationError::NoMatchingEntries);
|
||||
}
|
||||
|
@ -427,9 +427,11 @@ impl LdapServer {
|
|||
fn ldap_domain_to_dc(input: &str) -> String {
|
||||
let mut output: String = String::new();
|
||||
input.split('.').for_each(|dc| {
|
||||
output.push_str("dc=");
|
||||
output.push_str(dc);
|
||||
output.push_str(",");
|
||||
// output.push_str("dc=");
|
||||
// output.push_str(dc);
|
||||
// #[allow(clippy::single_char_add_str)]
|
||||
// output.push_str(",");
|
||||
output.push_str(concat!("dc=", dc, ","));
|
||||
});
|
||||
// Remove the last ','
|
||||
output.pop();
|
||||
|
|
|
@ -124,7 +124,7 @@ impl Plugin for PasswordImport {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::credential::policy::CryptoPolicy;
|
||||
use crate::credential::totp::{TOTP, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::totp::{Totp, TOTP_DEFAULT_STEP};
|
||||
use crate::credential::{Credential, CredentialType};
|
||||
use crate::modify::{Modify, ModifyList};
|
||||
use crate::prelude::*;
|
||||
|
@ -234,7 +234,7 @@ mod tests {
|
|||
}"#,
|
||||
);
|
||||
|
||||
let totp = TOTP::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let totp = Totp::generate_secure("test_totp".to_string(), TOTP_DEFAULT_STEP);
|
||||
let p = CryptoPolicy::minimum();
|
||||
let c = Credential::new_password_only(&p, "password")
|
||||
.unwrap()
|
||||
|
@ -263,7 +263,7 @@ mod tests {
|
|||
.get_ava_single_credential("primary_credential")
|
||||
.expect("failed to get primary cred.");
|
||||
match &c.type_ {
|
||||
CredentialType::PasswordMFA(_pw, totp, webauthn) => {
|
||||
CredentialType::PasswordMfa(_pw, totp, webauthn) => {
|
||||
assert!(totp.is_some());
|
||||
assert!(webauthn.is_empty());
|
||||
}
|
||||
|
|
|
@ -219,7 +219,7 @@ impl Plugin for Spn {
|
|||
e.get_uuid()
|
||||
);
|
||||
debug_assert!(false);
|
||||
r.push(Err(ConsistencyError::InvalidSPN(e.get_id())));
|
||||
r.push(Err(ConsistencyError::InvalidSpn(e.get_id())));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
@ -235,12 +235,12 @@ impl Plugin for Spn {
|
|||
g_spn,
|
||||
);
|
||||
debug_assert!(false);
|
||||
r.push(Err(ConsistencyError::InvalidSPN(e.get_id())))
|
||||
r.push(Err(ConsistencyError::InvalidSpn(e.get_id())))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
ladmin_error!(au, "Entry {:?} does not contain an SPN", e.get_uuid(),);
|
||||
r.push(Err(ConsistencyError::InvalidSPN(e.get_id())))
|
||||
r.push(Err(ConsistencyError::InvalidSpn(e.get_id())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1957,7 +1957,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
Err(e) => Err(OperationError::SchemaViolation(e)),
|
||||
}
|
||||
} else {
|
||||
Err(OperationError::InvalidDBState)
|
||||
Err(OperationError::InvalidDbState)
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -2022,7 +2022,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
Ok(())
|
||||
}
|
||||
} else {
|
||||
Err(OperationError::InvalidDBState)
|
||||
Err(OperationError::InvalidDbState)
|
||||
}
|
||||
}
|
||||
Err(er) => {
|
||||
|
|
|
@ -288,10 +288,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_utf8(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Utf8(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Utf8(_))
|
||||
}
|
||||
|
||||
pub fn new_iutf8(s: &str) -> Self {
|
||||
|
@ -315,17 +312,11 @@ impl PartialValue {
|
|||
*/
|
||||
|
||||
pub fn is_iutf8(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Iutf8(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Iutf8(_))
|
||||
}
|
||||
|
||||
pub fn is_iname(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Iname(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Iname(_))
|
||||
}
|
||||
|
||||
pub fn new_bool(b: bool) -> Self {
|
||||
|
@ -340,10 +331,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_bool(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Bool(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Bool(_))
|
||||
}
|
||||
|
||||
pub fn new_uuid(u: Uuid) -> Self {
|
||||
|
@ -362,10 +350,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_uuid(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Uuid(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Uuid(_))
|
||||
}
|
||||
|
||||
pub fn new_refer(u: Uuid) -> Self {
|
||||
|
@ -384,10 +369,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_refer(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Refer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Refer(_))
|
||||
}
|
||||
|
||||
pub fn new_indexs(s: &str) -> Option<Self> {
|
||||
|
@ -399,10 +381,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_index(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Index(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Index(_))
|
||||
}
|
||||
|
||||
pub fn new_syntaxs(s: &str) -> Option<Self> {
|
||||
|
@ -414,10 +393,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_syntax(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Syntax(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Syntax(_))
|
||||
}
|
||||
|
||||
pub fn new_json_filter(s: &str) -> Option<Self> {
|
||||
|
@ -429,10 +405,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_json_filter(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::JsonFilt(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::JsonFilt(_))
|
||||
}
|
||||
|
||||
pub fn new_credential_tag(s: &str) -> Self {
|
||||
|
@ -440,10 +413,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_credential(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Cred(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Cred(_))
|
||||
}
|
||||
|
||||
pub fn new_radius_string() -> Self {
|
||||
|
@ -451,10 +421,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_radius_string(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::RadiusCred => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::RadiusCred)
|
||||
}
|
||||
|
||||
pub fn new_sshkey_tag(s: String) -> Self {
|
||||
|
@ -466,10 +433,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_sshkey(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::SshKey(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::SshKey(_))
|
||||
}
|
||||
|
||||
pub fn new_spn_s(s: &str) -> Option<Self> {
|
||||
|
@ -491,10 +455,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_spn(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Spn(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Spn(_, _))
|
||||
}
|
||||
|
||||
pub fn new_uint32(u: u32) -> Self {
|
||||
|
@ -506,10 +467,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_uint32(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Uint32(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Uint32(_))
|
||||
}
|
||||
|
||||
pub fn new_cid(c: Cid) -> Self {
|
||||
|
@ -521,10 +479,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_cid(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Cid(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Cid(_))
|
||||
}
|
||||
|
||||
pub fn new_nsuniqueid_s(s: &str) -> Self {
|
||||
|
@ -532,10 +487,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_nsuniqueid(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::Nsuniqueid(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::Nsuniqueid(_))
|
||||
}
|
||||
|
||||
pub fn new_datetime_epoch(ts: Duration) -> Self {
|
||||
|
@ -550,10 +502,7 @@ impl PartialValue {
|
|||
}
|
||||
|
||||
pub fn is_datetime(&self) -> bool {
|
||||
match self {
|
||||
PartialValue::DateTime(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self, PartialValue::DateTime(_))
|
||||
}
|
||||
|
||||
pub fn to_str(&self) -> Option<&str> {
|
||||
|
@ -744,10 +693,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_utf8(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Utf8(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Utf8(_))
|
||||
}
|
||||
|
||||
pub fn new_iutf8(s: &str) -> Self {
|
||||
|
@ -758,10 +704,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_insensitive_utf8(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Iutf8(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Iutf8(_))
|
||||
}
|
||||
|
||||
pub fn new_iname(s: &str) -> Self {
|
||||
|
@ -772,10 +715,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_iname(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Iname(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Iname(_))
|
||||
}
|
||||
|
||||
pub fn new_uuid(u: Uuid) -> Self {
|
||||
|
@ -801,10 +741,7 @@ impl Value {
|
|||
|
||||
// Is this correct? Should ref be seperate?
|
||||
pub fn is_uuid(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Uuid(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Uuid(_))
|
||||
}
|
||||
|
||||
pub fn new_class(s: &str) -> Self {
|
||||
|
@ -837,10 +774,7 @@ impl Value {
|
|||
|
||||
#[inline]
|
||||
pub fn is_bool(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Bool(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Bool(_))
|
||||
}
|
||||
|
||||
pub fn new_syntaxs(s: &str) -> Option<Self> {
|
||||
|
@ -851,10 +785,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_syntax(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Syntax(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Syntax(_))
|
||||
}
|
||||
|
||||
pub fn new_indexs(s: &str) -> Option<Self> {
|
||||
|
@ -865,10 +796,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_index(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Index(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Index(_))
|
||||
}
|
||||
|
||||
pub fn new_refer(u: Uuid) -> Self {
|
||||
|
@ -893,10 +821,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_refer(&self) -> bool {
|
||||
match self.pv {
|
||||
PartialValue::Refer(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(self.pv, PartialValue::Refer(_))
|
||||
}
|
||||
|
||||
pub fn new_json_filter(s: &str) -> Option<Self> {
|
||||
|
@ -925,10 +850,7 @@ impl Value {
|
|||
}
|
||||
|
||||
pub fn is_credential(&self) -> bool {
|
||||
match &self.pv {
|
||||
PartialValue::Cred(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
matches!(&self.pv, PartialValue::Cred(_))
|
||||
}
|
||||
|
||||
pub fn to_credential(&self) -> Option<&Credential> {
|
||||
|
@ -1394,10 +1316,7 @@ impl Value {
|
|||
}
|
||||
}
|
||||
PartialValue::Cred(_) => match &self.data {
|
||||
Some(v) => match v.as_ref() {
|
||||
DataValue::Cred(_) => true,
|
||||
_ => false,
|
||||
},
|
||||
Some(v) => matches!(v.as_ref(), DataValue::Cred(_)),
|
||||
None => false,
|
||||
},
|
||||
PartialValue::SshKey(_) => match &self.data {
|
||||
|
@ -1410,10 +1329,7 @@ impl Value {
|
|||
None => false,
|
||||
},
|
||||
PartialValue::RadiusCred => match &self.data {
|
||||
Some(v) => match v.as_ref() {
|
||||
DataValue::RadiusCred(_) => true,
|
||||
_ => false,
|
||||
},
|
||||
Some(v) => matches!(v.as_ref(), DataValue::RadiusCred(_)),
|
||||
None => false,
|
||||
},
|
||||
PartialValue::Nsuniqueid(s) => NSUNIQUEID_RE.is_match(s),
|
||||
|
|
|
@ -262,7 +262,7 @@ impl Component for LoginApp {
|
|||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
ConsoleService::log(format!("create").as_str());
|
||||
ConsoleService::log("create");
|
||||
|
||||
// First we need to work out what state we are in.
|
||||
let lstorage = StorageService::new(yew::services::storage::Area::Local).unwrap();
|
||||
|
@ -328,7 +328,7 @@ impl Component for LoginApp {
|
|||
Ok(totp) => {
|
||||
self.state = LoginState::Totp(TotpState::Disabled);
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Cred(AuthCredential::TOTP(totp)),
|
||||
step: AuthStep::Cred(AuthCredential::Totp(totp)),
|
||||
};
|
||||
self.auth_step(authreq);
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ impl Component for LoginApp {
|
|||
// Go to the password view.
|
||||
self.state = LoginState::Password(true);
|
||||
}
|
||||
AuthAllowed::TOTP => {
|
||||
AuthAllowed::Totp => {
|
||||
self.state = LoginState::Totp(TotpState::Enabled);
|
||||
}
|
||||
AuthAllowed::Webauthn(challenge) => {
|
||||
|
|
Loading…
Reference in a new issue