Index: app/controllers/account_controller.rb =================================================================== --- app/controllers/account_controller.rb (revision 17785) +++ app/controllers/account_controller.rb (working copy) @@ -83,6 +83,15 @@ if request.post? if @user.must_change_passwd? && @user.check_password?(params[:new_password]) flash.now[:error] = l(:notice_new_password_must_be_different) + elsif @user.isExternal? + if @user.newExternalPassword(params[:new_password], params[:new_password_confirmation]) + @token.destroy + flash[:notice] = l(:notice_account_password_updated) + redirect_to signin_path + return + else + flash[:error] = l(:error_changing_external_password) + end else @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] @user.must_change_passwd = false Index: app/controllers/my_controller.rb =================================================================== --- app/controllers/my_controller.rb (revision 17785) +++ app/controllers/my_controller.rb (working copy) @@ -103,6 +103,14 @@ flash.now[:error] = l(:notice_account_wrong_password) elsif params[:password] == params[:new_password] flash.now[:error] = l(:notice_new_password_must_be_different) + elsif @user.isExternal? + if @user.changeExternalPassword(params[:password], params[:new_password], params[:new_password_confirmation]) + session[:tk] = @user.generate_session_token + flash[:notice] = l(:notice_account_password_updated) + redirect_to my_account_path + else + flash[:error] = l(:error_changing_external_password) + end else @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] @user.must_change_passwd = false Index: app/models/auth_source_ldap.rb =================================================================== --- app/models/auth_source_ldap.rb (revision 17785) +++ app/models/auth_source_ldap.rb (working copy) @@ -20,6 +20,8 @@ require 'net/ldap' require 'net/ldap/dn' require 'timeout' +require 'digest' +require 'base64' class AuthSourceLdap < AuthSource NETWORK_EXCEPTIONS = [ @@ -84,6 +86,94 @@ "LDAP" end + def allow_password_changes? + true + end + + def password_encryption + "MD5" + end + + def encode_password(clear_password) + salt = User.generate_salt + + if self.password_encryption == "MD5" + logger.debug "Encode as md5" + return "{MD5}"+Base64.encode64(Digest::MD5.digest(clear_password)).chomp! + end + if self.password_encryption == "SSHA" + logger.debug "Encode as ssha" + return "{SSHA}"+Base64.encode64(Digest::SHA1.digest(clear_password+salt)+salt).chomp! + end + + if self.password_encryption == "CLEAR" + logger.debug "Encode as cleartype" + return clear_password + end + # + end + + # change password + def change_password(login,password,newPassword) + begin + attrs = get_user_dn(login, password) + if attrs + logger.debug "Binding with user account" + ldap_con = initialize_ldap_con(attrs[:dn], password) + ops = [ + [:delete, :userPassword, password], + [:add, :userPassword, newPassword] + ] + #return ldap_con.modify :dn => attrs[:dn], :operations => ops + # This is another password change method, probably more common + newPassword = encode_password(newPassword) + # logger.info("NEW PASSWORD #{newPassword}") + if newPassword.blank? + logger.debug "Invaild password" + return false + else + logger.debug "Try to change password" + return ldap_con.replace_attribute attrs[:dn], :userPassword, newPassword + end + end + rescue Exception => ex + logger.error "LDAP: #{ex.message}" + return false + end + return false + end + + def lost_password(login,newPassword) + begin + attrs = get_user_dn_nopass(login) + if attrs + ldap_con = initialize_ldap_con(self.account, self.account_password) + return ldap_con.replace_attribute attrs[:dn], :userPassword, encode_password(newPassword) + end + rescue + return false + end + return false + end + + def get_user_dn_nopass(login) + ldap_con = nil + ldap_con = initialize_ldap_con(self.account, self.account_password) + attrs = {} + search_filter = base_filter & Net::LDAP::Filter.eq(self.attr_login, login) + ldap_con.search(:base => self.base_dn, + :filter => search_filter, + :attributes=> search_attributes) do |entry| + if onthefly_register? + attrs = get_user_attributes_from_ldap_entry(entry) + else + attrs = {:dn => entry.dn} + end + logger.debug "DN found for #{login}: #{attrs[:dn]}" if logger && logger.debug? + end + attrs + end + # Returns true if this source can be searched for users def searchable? !account.to_s.include?("$login") && %w(login firstname lastname mail).all? {|a| send(:"attr_#{a}?")} Index: app/models/user.rb =================================================================== --- app/models/user.rb (revision 17785) +++ app/models/user.rb (working copy) @@ -903,6 +903,28 @@ User.where("created_on < ? AND status = ?", Time.now - age, STATUS_REGISTERED).destroy_all end + def isExternal? + return auth_source_id.present? + end + + def changeExternalPassword(password,newPassword,newPasswordConfirm) + return false if newPassword == "" || newPassword.length < Setting.password_min_length.to_i + return false if newPassword != newPasswordConfirm + if (self.isExternal?) + return self.auth_source.change_password(self.login,password,newPassword) + end + return false + end + + def newExternalPassword(newPassword,newPasswordConfirm) + return false if newPassword == "" || newPassword.length < 4 + return false if newPassword != newPasswordConfirm + if (self.isExternal?) + return self.auth_source.lost_password(self.login,newPassword) + end + return false + end + protected def validate_password_length Index: config/locales/en.yml =================================================================== --- config/locales/en.yml (revision 17785) +++ config/locales/en.yml (working copy) @@ -226,6 +226,7 @@ error_no_data_in_file: "The file does not contain any data" error_attachment_extension_not_allowed: "Attachment extension %{extension} is not allowed" error_ldap_bind_credentials: "Invalid LDAP Account/Password" + error_changing_external_password: "Error changing external password" error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue" error_no_projects_with_tracker_allowed_for_new_issue: "There are no projects with trackers for which you can create an issue" error_move_of_child_not_possible: "Subtask %{child} could not be moved to the new project: %{errors}"