mirror of
https://github.com/lldap/lldap.git
synced 2026-07-25 17:58:44 +00:00
ldap: Add support for modifying the password with a modify operation
This commit is contained in:
committed by
nitnelave
parent
e5bc06a617
commit
d9f4adcb0e
@@ -128,9 +128,11 @@ impl CommonComponent<ChangePasswordForm> for ChangePasswordForm {
|
|||||||
Msg::SubmitNewPassword => {
|
Msg::SubmitNewPassword => {
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let new_password = self.form.model().password;
|
let new_password = self.form.model().password;
|
||||||
let registration_start_request =
|
let registration_start_request = opaque::client::registration::start_registration(
|
||||||
opaque::client::registration::start_registration(&new_password, &mut rng)
|
new_password.as_bytes(),
|
||||||
.context("Could not initiate password change")?;
|
&mut rng,
|
||||||
|
)
|
||||||
|
.context("Could not initiate password change")?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: ctx.props().username.clone(),
|
username: ctx.props().username.clone(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
|
|||||||
@@ -117,7 +117,10 @@ impl CommonComponent<CreateUserForm> for CreateUserForm {
|
|||||||
let opaque::client::registration::ClientRegistrationStartResult {
|
let opaque::client::registration::ClientRegistrationStartResult {
|
||||||
state,
|
state,
|
||||||
message,
|
message,
|
||||||
} = opaque::client::registration::start_registration(&password, &mut rng)?;
|
} = opaque::client::registration::start_registration(
|
||||||
|
password.as_bytes(),
|
||||||
|
&mut rng,
|
||||||
|
)?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: user_id,
|
username: user_id,
|
||||||
registration_start_request: message,
|
registration_start_request: message,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ impl CommonComponent<ResetPasswordStep2Form> for ResetPasswordStep2Form {
|
|||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let new_password = self.form.model().password;
|
let new_password = self.form.model().password;
|
||||||
let registration_start_request =
|
let registration_start_request =
|
||||||
opaque_registration::start_registration(&new_password, &mut rng)
|
opaque_registration::start_registration(new_password.as_bytes(), &mut rng)
|
||||||
.context("Could not initiate password change")?;
|
.context("Could not initiate password change")?;
|
||||||
let req = registration::ClientRegistrationStartRequest {
|
let req = registration::ClientRegistrationStartRequest {
|
||||||
username: self.username.clone().unwrap(),
|
username: self.username.clone().unwrap(),
|
||||||
|
|||||||
+2
-2
@@ -77,10 +77,10 @@ pub mod client {
|
|||||||
pub use opaque_ke::ClientRegistrationFinishParameters;
|
pub use opaque_ke::ClientRegistrationFinishParameters;
|
||||||
/// Initiate the registration negotiation.
|
/// Initiate the registration negotiation.
|
||||||
pub fn start_registration<R: RngCore + CryptoRng>(
|
pub fn start_registration<R: RngCore + CryptoRng>(
|
||||||
password: &str,
|
password: &[u8],
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> AuthenticationResult<ClientRegistrationStartResult> {
|
) -> AuthenticationResult<ClientRegistrationStartResult> {
|
||||||
Ok(ClientRegistration::start(rng, password.as_bytes())?)
|
Ok(ClientRegistration::start(rng, password)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finalize the registration negotiation.
|
/// Finalize the registration negotiation.
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ pub mod tests {
|
|||||||
insert_user_no_password(handler, name).await;
|
insert_user_no_password(handler, name).await;
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let client_registration_start =
|
let client_registration_start =
|
||||||
opaque::client::registration::start_registration(pass, &mut rng).unwrap();
|
opaque::client::registration::start_registration(pass.as_bytes(), &mut rng).unwrap();
|
||||||
let response = handler
|
let response = handler
|
||||||
.registration_start(registration::ClientRegistrationStartRequest {
|
.registration_start(registration::ClientRegistrationStartRequest {
|
||||||
username: name.to_string(),
|
username: name.to_string(),
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ pub(crate) async fn register_password(
|
|||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
use registration::*;
|
use registration::*;
|
||||||
let registration_start =
|
let registration_start =
|
||||||
opaque::client::registration::start_registration(password.unsecure(), &mut rng)?;
|
opaque::client::registration::start_registration(password.unsecure().as_bytes(), &mut rng)?;
|
||||||
let start_response = opaque_handler
|
let start_response = opaque_handler
|
||||||
.registration_start(ClientRegistrationStartRequest {
|
.registration_start(ClientRegistrationStartRequest {
|
||||||
username: username.to_string(),
|
username: username.to_string(),
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ use crate::{
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use ldap3_proto::proto::{
|
use ldap3_proto::proto::{
|
||||||
LdapAddRequest, LdapBindCred, LdapBindRequest, LdapBindResponse, LdapCompareRequest,
|
LdapAddRequest, LdapBindCred, LdapBindRequest, LdapBindResponse, LdapCompareRequest,
|
||||||
LdapDerefAliases, LdapExtendedRequest, LdapExtendedResponse, LdapFilter, LdapOp,
|
LdapDerefAliases, LdapExtendedRequest, LdapExtendedResponse, LdapFilter, LdapModify,
|
||||||
LdapPartialAttribute, LdapPasswordModifyRequest, LdapResult as LdapResultOp, LdapResultCode,
|
LdapModifyRequest, LdapModifyType, LdapOp, LdapPartialAttribute, LdapPasswordModifyRequest,
|
||||||
LdapSearchRequest, LdapSearchResultEntry, LdapSearchScope,
|
LdapResult as LdapResultOp, LdapResultCode, LdapSearchRequest, LdapSearchResultEntry,
|
||||||
|
LdapSearchScope,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tracing::{debug, instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
@@ -128,6 +129,15 @@ fn make_extended_response(code: LdapResultCode, message: String) -> LdapOp {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_modify_response(code: LdapResultCode, message: String) -> LdapOp {
|
||||||
|
LdapOp::ModifyResponse(LdapResultOp {
|
||||||
|
code,
|
||||||
|
matcheddn: "".to_string(),
|
||||||
|
message,
|
||||||
|
referral: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn root_dse_response(base_dn: &str) -> LdapOp {
|
fn root_dse_response(base_dn: &str) -> LdapOp {
|
||||||
LdapOp::SearchResultEntry(LdapSearchResultEntry {
|
LdapOp::SearchResultEntry(LdapSearchResultEntry {
|
||||||
dn: "".to_string(),
|
dn: "".to_string(),
|
||||||
@@ -270,7 +280,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
&self,
|
&self,
|
||||||
backend_handler: &B,
|
backend_handler: &B,
|
||||||
user: &UserId,
|
user: &UserId,
|
||||||
password: &str,
|
password: &[u8],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use lldap_auth::*;
|
use lldap_auth::*;
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
@@ -334,7 +344,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
),
|
),
|
||||||
})
|
})
|
||||||
} else if let Err(e) = self
|
} else if let Err(e) = self
|
||||||
.change_password(self.get_opaque_handler(), &uid, password)
|
.change_password(self.get_opaque_handler(), &uid, password.as_bytes())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Err(LdapError {
|
Err(LdapError {
|
||||||
@@ -374,6 +384,104 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_modify_change(
|
||||||
|
&mut self,
|
||||||
|
user_id: &UserId,
|
||||||
|
credentials: &ValidationResults,
|
||||||
|
user_is_admin: bool,
|
||||||
|
change: &LdapModify,
|
||||||
|
) -> LdapResult<()> {
|
||||||
|
if change.modification.atype.to_ascii_lowercase() != "userpassword"
|
||||||
|
|| change.operation != LdapModifyType::Replace
|
||||||
|
{
|
||||||
|
return Err(LdapError {
|
||||||
|
code: LdapResultCode::UnwillingToPerform,
|
||||||
|
message: format!(
|
||||||
|
r#"Unsupported operation: `{:?}` for `{}`"#,
|
||||||
|
change.operation, change.modification.atype
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if !credentials.can_change_password(user_id, user_is_admin) {
|
||||||
|
return Err(LdapError {
|
||||||
|
code: LdapResultCode::InsufficentAccessRights,
|
||||||
|
message: format!(
|
||||||
|
r#"User `{}` cannot modify the password of user `{}`"#,
|
||||||
|
&credentials.user, &user_id
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let [value] = &change.modification.vals.as_slice() {
|
||||||
|
self.change_password(self.get_opaque_handler(), user_id, value)
|
||||||
|
.await
|
||||||
|
.map_err(|e| LdapError {
|
||||||
|
code: LdapResultCode::Other,
|
||||||
|
message: format!("Error while changing the password: {:#?}", e),
|
||||||
|
})?;
|
||||||
|
} else {
|
||||||
|
return Err(LdapError {
|
||||||
|
code: LdapResultCode::InvalidAttributeSyntax,
|
||||||
|
message: format!(
|
||||||
|
r#"Wrong number of values for password attribute: {}"#,
|
||||||
|
change.modification.vals.len()
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_modify_request(
|
||||||
|
&mut self,
|
||||||
|
request: &LdapModifyRequest,
|
||||||
|
) -> LdapResult<Vec<LdapOp>> {
|
||||||
|
let credentials = self
|
||||||
|
.user_info
|
||||||
|
.as_ref()
|
||||||
|
.ok_or_else(|| LdapError {
|
||||||
|
code: LdapResultCode::InsufficentAccessRights,
|
||||||
|
message: "No user currently bound".to_string(),
|
||||||
|
})?
|
||||||
|
.clone();
|
||||||
|
match get_user_id_from_distinguished_name(
|
||||||
|
&request.dn,
|
||||||
|
&self.ldap_info.base_dn,
|
||||||
|
&self.ldap_info.base_dn_str,
|
||||||
|
) {
|
||||||
|
Ok(uid) => {
|
||||||
|
let user_is_admin = self
|
||||||
|
.backend_handler
|
||||||
|
.get_readable_handler(&credentials, &uid)
|
||||||
|
.expect("Unexpected permission error")
|
||||||
|
.get_user_groups(&uid)
|
||||||
|
.await
|
||||||
|
.map_err(|e| LdapError {
|
||||||
|
code: LdapResultCode::OperationsError,
|
||||||
|
message: format!("Internal error while requesting user's groups: {:#?}", e),
|
||||||
|
})?
|
||||||
|
.iter()
|
||||||
|
.any(|g| g.display_name == "lldap_admin");
|
||||||
|
for change in &request.changes {
|
||||||
|
self.handle_modify_change(&uid, &credentials, user_is_admin, change)
|
||||||
|
.await?
|
||||||
|
}
|
||||||
|
Ok(vec![make_modify_response(
|
||||||
|
LdapResultCode::Success,
|
||||||
|
String::new(),
|
||||||
|
)])
|
||||||
|
}
|
||||||
|
Err(e) => Err(LdapError {
|
||||||
|
code: LdapResultCode::InvalidDNSyntax,
|
||||||
|
message: format!("Invalid username: {}", e),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_modify_request(&mut self, request: &LdapModifyRequest) -> Vec<LdapOp> {
|
||||||
|
self.handle_modify_request(request)
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|e: LdapError| vec![make_modify_response(e.code, e.message)])
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn do_search_or_dse(
|
pub async fn do_search_or_dse(
|
||||||
&mut self,
|
&mut self,
|
||||||
request: &LdapSearchRequest,
|
request: &LdapSearchRequest,
|
||||||
@@ -650,6 +758,7 @@ impl<Backend: BackendHandler + LoginHandler + OpaqueHandler> LdapHandler<Backend
|
|||||||
// No need to notify on unbind (per rfc4511)
|
// No need to notify on unbind (per rfc4511)
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
LdapOp::ModifyRequest(request) => self.do_modify_request(&request).await,
|
||||||
LdapOp::ExtendedRequest(request) => self.do_extended_request(&request).await,
|
LdapOp::ExtendedRequest(request) => self.do_extended_request(&request).await,
|
||||||
LdapOp::AddRequest(request) => self
|
LdapOp::AddRequest(request) => self
|
||||||
.do_create_user(request)
|
.do_create_user(request)
|
||||||
@@ -2007,7 +2116,8 @@ mod tests {
|
|||||||
use lldap_auth::*;
|
use lldap_auth::*;
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let registration_start_request =
|
let registration_start_request =
|
||||||
opaque::client::registration::start_registration("password", &mut rng).unwrap();
|
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
||||||
|
.unwrap();
|
||||||
let request = registration::ClientRegistrationStartRequest {
|
let request = registration::ClientRegistrationStartRequest {
|
||||||
username: "bob".to_string(),
|
username: "bob".to_string(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
@@ -2045,6 +2155,56 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_password_change_modify_request() {
|
||||||
|
let mut mock = MockTestBackendHandler::new();
|
||||||
|
mock.expect_get_user_groups()
|
||||||
|
.with(eq(UserId::new("bob")))
|
||||||
|
.returning(|_| Ok(HashSet::new()));
|
||||||
|
use lldap_auth::*;
|
||||||
|
let mut rng = rand::rngs::OsRng;
|
||||||
|
let registration_start_request =
|
||||||
|
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
||||||
|
.unwrap();
|
||||||
|
let request = registration::ClientRegistrationStartRequest {
|
||||||
|
username: "bob".to_string(),
|
||||||
|
registration_start_request: registration_start_request.message,
|
||||||
|
};
|
||||||
|
let start_response = opaque::server::registration::start_registration(
|
||||||
|
&opaque::server::ServerSetup::new(&mut rng),
|
||||||
|
request.registration_start_request,
|
||||||
|
&request.username,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
mock.expect_registration_start().times(1).return_once(|_| {
|
||||||
|
Ok(registration::ServerRegistrationStartResponse {
|
||||||
|
server_data: "".to_string(),
|
||||||
|
registration_response: start_response.message,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
mock.expect_registration_finish()
|
||||||
|
.times(1)
|
||||||
|
.return_once(|_| Ok(()));
|
||||||
|
let mut ldap_handler = setup_bound_admin_handler(mock).await;
|
||||||
|
let request = LdapOp::ModifyRequest(LdapModifyRequest {
|
||||||
|
dn: "uid=bob,ou=people,dc=example,dc=com".to_string(),
|
||||||
|
changes: vec![LdapModify {
|
||||||
|
operation: LdapModifyType::Replace,
|
||||||
|
modification: LdapPartialAttribute {
|
||||||
|
atype: "userPassword".to_owned(),
|
||||||
|
vals: vec!["password".as_bytes().to_vec()],
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
assert_eq!(
|
||||||
|
ldap_handler.handle_ldap_message(request).await,
|
||||||
|
Some(vec![make_modify_response(
|
||||||
|
LdapResultCode::Success,
|
||||||
|
"".to_string(),
|
||||||
|
)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_password_change_password_manager() {
|
async fn test_password_change_password_manager() {
|
||||||
let mut mock = MockTestBackendHandler::new();
|
let mut mock = MockTestBackendHandler::new();
|
||||||
@@ -2054,7 +2214,8 @@ mod tests {
|
|||||||
use lldap_auth::*;
|
use lldap_auth::*;
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let registration_start_request =
|
let registration_start_request =
|
||||||
opaque::client::registration::start_registration("password", &mut rng).unwrap();
|
opaque::client::registration::start_registration("password".as_bytes(), &mut rng)
|
||||||
|
.unwrap();
|
||||||
let request = registration::ClientRegistrationStartRequest {
|
let request = registration::ClientRegistrationStartRequest {
|
||||||
username: "bob".to_string(),
|
username: "bob".to_string(),
|
||||||
registration_start_request: registration_start_request.message,
|
registration_start_request: registration_start_request.message,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
let mut rng = rand::rngs::OsRng;
|
let mut rng = rand::rngs::OsRng;
|
||||||
let registration_start_request =
|
let registration_start_request =
|
||||||
opaque::client::registration::start_registration(&opts.password, &mut rng)
|
opaque::client::registration::start_registration(opts.password.as_bytes(), &mut rng)
|
||||||
.context("Could not initiate password change")?;
|
.context("Could not initiate password change")?;
|
||||||
let start_request = registration::ClientRegistrationStartRequest {
|
let start_request = registration::ClientRegistrationStartRequest {
|
||||||
username: opts.username.to_string(),
|
username: opts.username.to_string(),
|
||||||
|
|||||||
Reference in New Issue
Block a user