Rename to SetCredentialRequest::BackupCodeGenerate (#524)

This commit is contained in:
cuberoot74088 2021-07-22 04:04:56 +02:00 committed by GitHub
parent e4d0f3ffbc
commit 8306c3bc6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 141 additions and 6 deletions

View file

@ -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(),

View file

@ -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,

View file

@ -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| {

View file

@ -685,7 +685,7 @@ pub enum SetCredentialRequest {
WebauthnRegister(Uuid, RegisterPublicKeyCredential),
// Remove
WebauthnRemove(String),
GenerateBackupCode,
BackupCodeGenerate,
BackupCodeRemove,
}

View file

@ -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(),

View file

@ -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),

View file

@ -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,