summaryrefslogtreecommitdiff
path: root/internal/authentication/ldap_user_provider.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/authentication/ldap_user_provider.go')
-rw-r--r--internal/authentication/ldap_user_provider.go111
1 files changed, 110 insertions, 1 deletions
diff --git a/internal/authentication/ldap_user_provider.go b/internal/authentication/ldap_user_provider.go
index 905bf07e8..502dbe502 100644
--- a/internal/authentication/ldap_user_provider.go
+++ b/internal/authentication/ldap_user_provider.go
@@ -164,7 +164,11 @@ func (p *LDAPUserProvider) GetDetailsExtended(username string) (details *UserDet
return nil, err
}
- defer client.Close()
+ defer func() {
+ if err := p.factory.ReleaseClient(client); err != nil {
+ p.log.WithError(err).Warn("Error occurred releasing the LDAP client")
+ }
+ }()
if profile, err = p.getUserProfileExtended(client, username); err != nil {
return nil, err
@@ -299,6 +303,111 @@ func (p *LDAPUserProvider) UpdatePassword(username, password string) (err error)
return nil
}
+// ChangePassword is used to change a user's password but requires their old password to be successfully verified.
+//
+//nolint:gocyclo
+func (p *LDAPUserProvider) ChangePassword(username, oldPassword string, newPassword string) (err error) {
+ var (
+ client ldap.Client
+ profile *ldapUserProfile
+ )
+
+ if client, err = p.factory.GetClient(); err != nil {
+ return fmt.Errorf("unable to update password for user '%s'. Cause: %w", username, err)
+ }
+
+ defer func() {
+ if err := p.factory.ReleaseClient(client); err != nil {
+ p.log.WithError(err).Warn("Error occurred releasing the LDAP client")
+ }
+ }()
+
+ if profile, err = p.getUserProfile(client, username); err != nil {
+ return fmt.Errorf("unable to update password for user '%s'. Cause: %w", username, err)
+ }
+
+ var controls []ldap.Control
+
+ switch {
+ case p.features.ControlTypes.MsftPwdPolHints:
+ controls = append(controls, &controlMsftServerPolicyHints{ldapOIDControlMsftServerPolicyHints})
+ case p.features.ControlTypes.MsftPwdPolHintsDeprecated:
+ controls = append(controls, &controlMsftServerPolicyHints{ldapOIDControlMsftServerPolicyHintsDeprecated})
+ }
+
+ userPasswordOk, err := p.CheckUserPassword(username, oldPassword)
+
+ if err != nil {
+ errorCode := ldapGetErrorCode(err)
+ if errorCode == ldap.LDAPResultInvalidCredentials {
+ return ErrIncorrectPassword
+ } else {
+ return err
+ }
+ }
+
+ if !userPasswordOk {
+ return ErrIncorrectPassword
+ }
+
+ if oldPassword == newPassword {
+ return ErrPasswordWeak
+ }
+
+ switch {
+ case p.features.Extensions.PwdModifyExOp:
+ pwdModifyRequest := ldap.NewPasswordModifyRequest(
+ profile.DN,
+ oldPassword,
+ newPassword,
+ )
+
+ err = p.pwdModify(client, pwdModifyRequest)
+ case p.config.Implementation == schema.LDAPImplementationActiveDirectory:
+ modifyRequest := ldap.NewModifyRequest(profile.DN, controls)
+ // The password needs to be enclosed in quotes
+ // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/6e803168-f140-4d23-b2d3-c3a8ab5917d2
+ pwdEncoded, err := encodingUTF16LittleEndian.NewEncoder().String(fmt.Sprintf("\"%s\"", newPassword))
+ if err != nil {
+ return fmt.Errorf("failed to encode new password for user '%s'. Cause: %w", username, err)
+ }
+
+ modifyRequest.Replace(ldapAttributeUnicodePwd, []string{pwdEncoded})
+
+ //nolint
+ err = p.modify(client, modifyRequest)
+ default:
+ modifyRequest := ldap.NewModifyRequest(profile.DN, controls)
+ modifyRequest.Replace(ldapAttributeUserPassword, []string{newPassword})
+
+ err = p.modify(client, modifyRequest)
+ }
+
+ //TODO: Better inform users regarding password reuse/password history.
+ if err != nil {
+ if errorCode := ldapGetErrorCode(err); errorCode != -1 {
+ switch errorCode {
+ case ldap.LDAPResultInvalidCredentials,
+ ldap.LDAPResultInappropriateAuthentication:
+ return ErrIncorrectPassword
+ case ldap.LDAPResultConstraintViolation,
+ ldap.LDAPResultObjectClassViolation,
+ ldap.ErrorEmptyPassword,
+ ldap.LDAPResultUnwillingToPerform:
+ return ErrPasswordWeak
+ case ldap.LDAPResultInsufficientAccessRights:
+ return ErrOperationFailed
+ default:
+ return ErrOperationFailed
+ }
+ }
+
+ return ErrOperationFailed
+ }
+
+ return nil
+}
+
func (p *LDAPUserProvider) search(client ldap.Client, request *ldap.SearchRequest) (result *ldap.SearchResult, err error) {
if result, err = client.Search(request); err != nil {
if referral, ok := p.getReferral(err); ok {