diff --git a/server/core/src/actors/v1_write.rs b/server/core/src/actors/v1_write.rs index 3eddada0b..ed7c71e0c 100644 --- a/server/core/src/actors/v1_write.rs +++ b/server/core/src/actors/v1_write.rs @@ -420,7 +420,7 @@ impl QueryServerWriteV1 { e })?; idms_prox_write - .generate_account_password(&gpe) + .generate_service_account_password(&gpe) .and_then(|r| idms_prox_write.commit().map(|_| r)) } diff --git a/server/lib/src/idm/account.rs b/server/lib/src/idm/account.rs index a8b466261..1724efd55 100644 --- a/server/lib/src/idm/account.rs +++ b/server/lib/src/idm/account.rs @@ -371,16 +371,6 @@ impl Account { self.uuid == UUID_ANONYMOUS } - pub(crate) fn gen_generatedpassword_recover_mod( - &self, - cleartext: &str, - crypto_policy: &CryptoPolicy, - ) -> Result, OperationError> { - let ncred = Credential::new_generatedpassword_only(crypto_policy, cleartext)?; - let vcred = Value::new_credential("primary", ncred); - Ok(ModifyList::new_purge_and_set("primary_credential", vcred)) - } - pub(crate) fn gen_password_mod( &self, cleartext: &str, diff --git a/server/lib/src/idm/server.rs b/server/lib/src/idm/server.rs index db61fbbdd..57ed299c7 100644 --- a/server/lib/src/idm/server.rs +++ b/server/lib/src/idm/server.rs @@ -27,7 +27,7 @@ use webauthn_rs::prelude::{Webauthn, WebauthnBuilder}; use super::event::ReadBackupCodeEvent; use super::ldap::{LdapBoundToken, LdapSession}; -use crate::credential::softlock::CredSoftLock; +use crate::credential::{softlock::CredSoftLock, Credential}; use crate::idm::account::Account; use crate::idm::authsession::AuthSession; use crate::idm::credupdatesession::CredentialUpdateSessionMutex; @@ -39,9 +39,9 @@ use crate::idm::delayed::{ use crate::idm::event::PasswordChangeEvent; use crate::idm::event::{AuthEvent, AuthEventStep, AuthResult}; use crate::idm::event::{ - CredentialStatusEvent, GeneratePasswordEvent, LdapAuthEvent, LdapTokenAuthEvent, - RadiusAuthTokenEvent, RegenerateRadiusSecretEvent, UnixGroupTokenEvent, - UnixPasswordChangeEvent, UnixUserAuthEvent, UnixUserTokenEvent, + CredentialStatusEvent, LdapAuthEvent, LdapTokenAuthEvent, RadiusAuthTokenEvent, + RegenerateRadiusSecretEvent, UnixGroupTokenEvent, UnixPasswordChangeEvent, UnixUserAuthEvent, + UnixUserTokenEvent, }; use crate::idm::oauth2::{ Oauth2ResourceServers, Oauth2ResourceServersReadTransaction, @@ -1478,6 +1478,10 @@ impl<'a> IdmServerTransaction<'a> for IdmServerProxyWriteTransaction<'a> { } impl<'a> IdmServerProxyWriteTransaction<'a> { + pub(crate) fn crypto_policy(&self) -> &CryptoPolicy { + self.crypto_policy + } + pub fn get_origin(&self) -> &Url { #[allow(clippy::unwrap_used)] self.webauthn.get_allowed_origins().get(0).unwrap() @@ -1716,18 +1720,23 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { e })?; - let account = self.target_to_account(target)?; - let cleartext = cleartext .map(|s| s.to_string()) .unwrap_or_else(password_from_random); - let modlist = account - .gen_generatedpassword_recover_mod(&cleartext, self.crypto_policy) + let ncred = Credential::new_generatedpassword_only(self.crypto_policy, &cleartext) .map_err(|e| { - admin_error!("Failed to generate password mod {:?}", e); + admin_error!("Unable to generate password mod {:?}", e); e })?; + let vcred = Value::new_credential("primary", ncred); + // We need to remove other credentials too. + let modlist = ModifyList::new_list(vec![ + m_purge("passkeys"), + m_purge("primary_credential"), + Modify::Present("primary_credential".into(), vcred), + ]); + trace!(?modlist, "processing change"); self.qs_write @@ -1744,116 +1753,6 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { Ok(cleartext) } - pub fn generate_account_password( - &mut self, - gpe: &GeneratePasswordEvent, - ) -> Result { - let account = self.target_to_account(gpe.target)?; - // Ask if tis all good - this step checks pwpolicy and such - - // Generate a new random, long pw. - // Because this is generated, we can bypass policy checks! - let cleartext = password_from_random(); - - // check a password badlist - even if generated, we still don't want to - // reuse something that has been disclosed. - - // it returns a modify - let modlist = account - .gen_generatedpassword_recover_mod(cleartext.as_str(), self.crypto_policy) - .map_err(|e| { - admin_error!("Unable to generate password mod {:?}", e); - e - })?; - - trace!(?modlist, "processing change"); - // given the new credential generate a modify - // We use impersonate here to get the event from ae - self.qs_write - .impersonate_modify( - // Filter as executed - &filter!(f_eq("uuid", PartialValue::Uuid(gpe.target))), - // Filter as intended (acp) - &filter_all!(f_eq("uuid", PartialValue::Uuid(gpe.target))), - &modlist, - // Provide the event to impersonate - &gpe.ident, - ) - .map(|_| cleartext) - .map_err(|e| { - admin_error!("Failed to generate account password {:?}", e); - e - }) - } - - /* - /// Generate a new set of backup code and remove the old ones. - pub(crate) fn generate_backup_code( - &mut self, - gbe: &GenerateBackupCodeEvent, - ) -> Result, OperationError> { - let account = self.target_to_account(&gbe.target)?; - - // Generate a new set of backup code. - let s = backup_code_from_random(); - - // it returns a modify - let modlist = account - .gen_backup_code_mod(BackupCodes::new(s.clone())) - .map_err(|e| { - admin_error!("Unable to generate backup code mod {:?}", e); - e - })?; - - trace!(?modlist, "processing change"); - // given the new credential generate a modify - // We use impersonate here to get the event from ae - self.qs_write - .impersonate_modify( - // Filter as executed - &filter!(f_eq("uuid", PartialValue::Uuid(gbe.target))), - // Filter as intended (acp) - &filter_all!(f_eq("uuid", PartialValue::Uuid(gbe.target))), - &modlist, - // Provide the event to impersonate - &gbe.ident, - ) - .map(|_| s.into_iter().collect()) - .map_err(|e| { - admin_error!("Failed to generate backup code {:?}", e); - e - }) - } - - pub(crate) fn remove_backup_code( - &mut self, - rte: &RemoveBackupCodeEvent, - ) -> Result { - trace!(target = ?rte.target, "Attempting to remove backup code"); - - let account = self.target_to_account(&rte.target)?; - let modlist = account.gen_backup_code_remove_mod().map_err(|e| { - admin_error!("Failed to gen backup code remove mod {:?}", e); - e - })?; - // Perform the mod - self.qs_write - .impersonate_modify( - // Filter as executed - &filter!(f_eq("uuid", PartialValue::Uuid(account.uuid))), - // Filter as intended (acp) - &filter_all!(f_eq("uuid", PartialValue::Uuid(account.uuid))), - &modlist, - &rte.ident, - ) - .map_err(|e| { - admin_error!("remove_backup_code {:?}", e); - e - }) - .map(|_| SetCredentialResponse::Success) - } - */ - pub fn regenerate_radius_secret( &mut self, rrse: &RegenerateRadiusSecretEvent, diff --git a/server/lib/src/idm/serviceaccount.rs b/server/lib/src/idm/serviceaccount.rs index e654b1bb9..5e52cbed5 100644 --- a/server/lib/src/idm/serviceaccount.rs +++ b/server/lib/src/idm/serviceaccount.rs @@ -5,10 +5,13 @@ use compact_jwt::{Jws, JwsSigner}; use kanidm_proto::v1::ApiToken as ProtoApiToken; use time::OffsetDateTime; +use crate::credential::Credential; use crate::event::SearchEvent; use crate::idm::account::Account; +use crate::idm::event::GeneratePasswordEvent; use crate::idm::server::{IdmServerProxyReadTransaction, IdmServerProxyWriteTransaction}; use crate::prelude::*; +use crate::utils::password_from_random; use crate::value::ApiToken; // Need to add KID to es256 der for lookups ✅ @@ -305,6 +308,46 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { e }) } + + pub fn generate_service_account_password( + &mut self, + gpe: &GeneratePasswordEvent, + ) -> Result { + // Generate a new random, long pw. + // Because this is generated, we can bypass policy checks! + let cleartext = password_from_random(); + let ncred = Credential::new_generatedpassword_only(self.crypto_policy(), &cleartext) + .map_err(|e| { + admin_error!("Unable to generate password mod {:?}", e); + e + })?; + let vcred = Value::new_credential("primary", ncred); + // We need to remove other credentials too. + let modlist = ModifyList::new_list(vec![ + m_purge("passkeys"), + m_purge("primary_credential"), + Modify::Present("primary_credential".into(), vcred), + ]); + + trace!(?modlist, "processing change"); + // given the new credential generate a modify + // We use impersonate here to get the event from ae + self.qs_write + .impersonate_modify( + // Filter as executed + &filter!(f_eq("uuid", PartialValue::Uuid(gpe.target))), + // Filter as intended (acp) + &filter_all!(f_eq("uuid", PartialValue::Uuid(gpe.target))), + &modlist, + // Provide the event to impersonate + &gpe.ident, + ) + .map(|_| cleartext) + .map_err(|e| { + admin_error!("Failed to generate account password {:?}", e); + e + }) + } } impl<'a> IdmServerProxyReadTransaction<'a> {