mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Rename to SetCredentialRequest::BackupCodeGenerate (#524)
This commit is contained in:
parent
e4d0f3ffbc
commit
8306c3bc6a
|
@ -634,6 +634,56 @@ impl KanidmAsyncClient {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn auth_password_backup_code(
|
||||
&self,
|
||||
ident: &str,
|
||||
password: &str,
|
||||
backup_code: &str,
|
||||
) -> Result<(), ClientError> {
|
||||
let mechs = match self.auth_step_init(ident).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !mechs.contains(&AuthMech::PasswordMfa) {
|
||||
debug!("PasswordMfa mech not presented");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let state = match self.auth_step_begin(AuthMech::PasswordMfa).await {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
if !state.contains(&AuthAllowed::BackupCode) {
|
||||
debug!("Backup Code step not offered.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
|
||||
let r = self.auth_step_backup_code(backup_code).await?;
|
||||
|
||||
// Should need to continue.
|
||||
match r.state {
|
||||
AuthState::Continue(allowed) => {
|
||||
if !allowed.contains(&AuthAllowed::Password) {
|
||||
debug!("Password step not offered.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
debug!("Invalid AuthState presented.");
|
||||
return Err(ClientError::AuthenticationFailed);
|
||||
}
|
||||
};
|
||||
|
||||
let r = self.auth_step_password(password).await?;
|
||||
|
||||
match r.state {
|
||||
AuthState::Success(_token) => Ok(()),
|
||||
_ => Err(ClientError::AuthenticationFailed),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn auth_webauthn_begin(
|
||||
&self,
|
||||
ident: &str,
|
||||
|
@ -1105,7 +1155,7 @@ impl KanidmAsyncClient {
|
|||
&self,
|
||||
id: &str,
|
||||
) -> Result<Vec<String>, ClientError> {
|
||||
let r = SetCredentialRequest::GenerateBackupCode;
|
||||
let r = SetCredentialRequest::BackupCodeGenerate;
|
||||
let res: Result<SetCredentialResponse, ClientError> = self
|
||||
.perform_put_request(
|
||||
format!("/v1/account/{}/_credential/primary", id).as_str(),
|
||||
|
|
|
@ -457,6 +457,18 @@ impl KanidmClient {
|
|||
tokio_block_on(self.asclient.auth_password_totp(ident, password, totp))
|
||||
}
|
||||
|
||||
pub fn auth_password_backup_code(
|
||||
&self,
|
||||
ident: &str,
|
||||
password: &str,
|
||||
backup_code: &str,
|
||||
) -> Result<(), ClientError> {
|
||||
tokio_block_on(
|
||||
self.asclient
|
||||
.auth_password_backup_code(ident, password, backup_code),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn auth_webauthn_begin(
|
||||
&self,
|
||||
ident: &str,
|
||||
|
|
|
@ -820,6 +820,79 @@ fn test_server_rest_totp_auth_lifecycle() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_server_rest_backup_code_auth_lifecycle() {
|
||||
run_test(|rsclient: KanidmClient| {
|
||||
let res = rsclient.auth_simple_password("admin", ADMIN_TEST_PASSWORD);
|
||||
assert!(res.is_ok());
|
||||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.unwrap();
|
||||
|
||||
// Create a new account
|
||||
rsclient
|
||||
.idm_account_create("demo_account", "Deeeeemo")
|
||||
.unwrap();
|
||||
|
||||
// Enroll a totp to the account
|
||||
assert!(rsclient
|
||||
.idm_account_primary_credential_set_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
||||
.is_ok());
|
||||
let (sessionid, tok) = rsclient
|
||||
.idm_account_primary_credential_generate_totp("demo_account")
|
||||
.unwrap();
|
||||
|
||||
let r_tok: Totp = tok.into();
|
||||
let totp = r_tok
|
||||
.do_totp_duration_from_epoch(
|
||||
&SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.unwrap(),
|
||||
)
|
||||
.expect("Failed to do totp?");
|
||||
|
||||
rsclient
|
||||
.idm_account_primary_credential_verify_totp("demo_account", totp, sessionid)
|
||||
.unwrap(); // the result
|
||||
|
||||
// Generate backup codes
|
||||
let backup_codes = rsclient
|
||||
.idm_account_primary_credential_generate_backup_code("demo_account")
|
||||
.expect("Failed to generate backup codes?");
|
||||
|
||||
// Check a good auth using a backup code
|
||||
let rsclient_good = rsclient.new_session().unwrap();
|
||||
assert!(rsclient_good
|
||||
.auth_password_backup_code(
|
||||
"demo_account",
|
||||
"sohdi3iuHo6mai7noh0a",
|
||||
backup_codes[0].as_str()
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
// Check a bad auth - needs to be second as we are going to trigger the slock.
|
||||
// Get a new connection
|
||||
let rsclient_bad = rsclient.new_session().unwrap();
|
||||
assert!(rsclient_bad
|
||||
.auth_password_backup_code("demo_account", "sohdi3iuHo6mai7noh0a", "wrong-backup-code")
|
||||
.is_err());
|
||||
// Delay by one second to allow the account to recover from the softlock.
|
||||
std::thread::sleep(std::time::Duration::from_millis(1100));
|
||||
|
||||
// Remove TOTP and backup codes on the account.
|
||||
rsclient
|
||||
.idm_account_primary_credential_remove_totp("demo_account")
|
||||
.unwrap();
|
||||
// Check password auth.
|
||||
let rsclient_good = rsclient.new_session().unwrap();
|
||||
assert!(rsclient_good
|
||||
.auth_simple_password("demo_account", "sohdi3iuHo6mai7noh0a")
|
||||
.is_ok());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_server_rest_webauthn_auth_lifecycle() {
|
||||
run_test(|rsclient: KanidmClient| {
|
||||
|
|
|
@ -685,7 +685,7 @@ pub enum SetCredentialRequest {
|
|||
WebauthnRegister(Uuid, RegisterPublicKeyCredential),
|
||||
// Remove
|
||||
WebauthnRemove(String),
|
||||
GenerateBackupCode,
|
||||
BackupCodeGenerate,
|
||||
BackupCodeRemove,
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ impl AccountOpt {
|
|||
AccountCredential::RemoveWebauthn(acs) => acs.copt.debug,
|
||||
AccountCredential::RegisterTotp(acs) => acs.copt.debug,
|
||||
AccountCredential::RemoveTotp(acs) => acs.copt.debug,
|
||||
AccountCredential::GenerateBackupCode(acs) => acs.copt.debug,
|
||||
AccountCredential::BackupCodeGenerate(acs) => acs.copt.debug,
|
||||
AccountCredential::BackupCodeRemove(acs) => acs.copt.debug,
|
||||
AccountCredential::Status(acs) => acs.copt.debug,
|
||||
},
|
||||
|
@ -283,7 +283,7 @@ impl AccountOpt {
|
|||
}
|
||||
}
|
||||
}
|
||||
AccountCredential::GenerateBackupCode(acsopt) => {
|
||||
AccountCredential::BackupCodeGenerate(acsopt) => {
|
||||
let client = acsopt.copt.to_client();
|
||||
match client.idm_account_primary_credential_generate_backup_code(
|
||||
acsopt.aopts.account_id.as_str(),
|
||||
|
|
|
@ -181,7 +181,7 @@ pub enum AccountCredential {
|
|||
GeneratePassword(AccountCredentialSet),
|
||||
/// Generate a new set of backup codes.
|
||||
#[structopt(name = "generate_backup_codes")]
|
||||
GenerateBackupCode(AccountNamedOpt),
|
||||
BackupCodeGenerate(AccountNamedOpt),
|
||||
/// Remove backup codes from the account.
|
||||
#[structopt(name = "remove_backup_codes")]
|
||||
BackupCodeRemove(AccountNamedOpt),
|
||||
|
|
|
@ -691,7 +691,7 @@ impl QueryServerWriteV1 {
|
|||
.remove_account_webauthn(&mut audit, &rwe)
|
||||
.and_then(|r| idms_prox_write.commit(&mut audit).map(|_| r))
|
||||
}
|
||||
SetCredentialRequest::GenerateBackupCode => {
|
||||
SetCredentialRequest::BackupCodeGenerate => {
|
||||
let gbe = GenerateBackupCodeEvent::from_parts(
|
||||
&mut audit,
|
||||
// &idms_prox_write.qs_write,
|
||||
|
|
Loading…
Reference in a new issue