Patch #5690 » ldap_change_password_3.2.0.patch
| app/controllers/my_controller.rb | ||
|---|---|---|
| 100 | 100 |
elsif params[:password] == params[:new_password] |
| 101 | 101 |
flash.now[:error] = l(:notice_new_password_must_be_different) |
| 102 | 102 |
else |
| 103 |
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] |
|
| 104 |
@user.must_change_passwd = false |
|
| 105 |
if @user.save |
|
| 106 |
# The session token was destroyed by the password change, generate a new one |
|
| 107 |
session[:tk] = @user.generate_session_token |
|
| 108 |
flash[:notice] = l(:notice_account_password_updated) |
|
| 109 |
redirect_to my_account_path |
|
| 103 |
if @user.isExternal? |
|
| 104 |
if @user.changeExternalPassword(params[:password],params[:new_password], params[:new_password_confirmation]) |
|
| 105 |
session[:ctime] = Time.now.change(:usec => 0).utc.to_i |
|
| 106 |
flash[:notice] = l(:notice_account_password_updated) |
|
| 107 |
redirect_to my_account_path |
|
| 108 |
else |
|
| 109 |
flash[:error] = l(:notice_external_password_error) |
|
| 110 |
end |
|
| 111 |
else |
|
| 112 |
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation] |
|
| 113 |
@user.must_change_passwd = false |
|
| 114 |
if @user.save |
|
| 115 |
# Reset the session creation time to not log out this session on next |
|
| 116 |
# request due to ApplicationController#force_logout_if_password_changed |
|
| 117 |
session[:ctime] = User.current.passwd_changed_on.utc.to_i |
|
| 118 |
flash[:notice] = l(:notice_account_password_updated) |
|
| 119 |
redirect_to my_account_path |
|
| 120 |
end |
|
| 110 | 121 |
end |
| 111 | 122 |
end |
| 112 | 123 |
end |
| app/helpers/auth_sources_helper.rb | ||
|---|---|---|
| 21 | 21 |
def auth_source_partial_name(auth_source) |
| 22 | 22 |
"form_#{auth_source.class.name.underscore}"
|
| 23 | 23 |
end |
| 24 | ||
| 25 |
module Encryption |
|
| 26 |
# Return an array of password encryptions |
|
| 27 |
def self.encryptiontypes |
|
| 28 |
["MD5","SSHA","CLEAR"] |
|
| 29 |
end |
|
| 30 |
end |
|
| 24 | 31 |
end |
| app/models/auth_source_ldap.rb | ||
|---|---|---|
| 18 | 18 |
require 'net/ldap' |
| 19 | 19 |
require 'net/ldap/dn' |
| 20 | 20 |
require 'timeout' |
| 21 |
require 'digest' |
|
| 22 |
require 'base64' |
|
| 21 | 23 | |
| 22 | 24 |
class AuthSourceLdap < AuthSource |
| 23 | 25 |
NETWORK_EXCEPTIONS = [ |
| ... | ... | |
| 70 | 72 |
"LDAP" |
| 71 | 73 |
end |
| 72 | 74 | |
| 75 |
def allow_password_changes? |
|
| 76 |
return self.enabled_passwd |
|
| 77 |
end |
|
| 78 | ||
| 79 |
def encode_password(clear_password) |
|
| 80 |
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
|
| 81 |
salt = '' |
|
| 82 |
10.times { |i| salt << chars[rand(chars.size-1)] }
|
|
| 83 | ||
| 84 |
if self.password_encryption == "MD5" |
|
| 85 |
logger.debug "Encode as md5" |
|
| 86 |
return "{MD5}"+Base64.encode64(Digest::MD5.digest(clear_password)).chomp!
|
|
| 87 |
end |
|
| 88 |
if self.password_encryption == "SSHA" |
|
| 89 |
logger.debug "Encode as ssha" |
|
| 90 |
return "{SSHA}"+Base64.encode64(Digest::SHA1.digest(clear_password+salt)+salt).chomp!
|
|
| 91 |
end |
|
| 92 | ||
| 93 |
if self.password_encryption == "CLEAR" |
|
| 94 |
logger.debug "Encode as cleartype" |
|
| 95 |
return clear_password |
|
| 96 |
end |
|
| 97 |
# |
|
| 98 |
end |
|
| 99 | ||
| 100 |
# change password |
|
| 101 |
def change_password(login,password,newPassword) |
|
| 102 |
begin |
|
| 103 |
attrs = get_user_dn(login, password) |
|
| 104 |
if attrs |
|
| 105 |
if self.account.blank? || self.account_password.blank? |
|
| 106 |
logger.debug "Binding with user account" |
|
| 107 |
ldap_con = initialize_ldap_con(attrs[:dn], password) |
|
| 108 |
else |
|
| 109 |
logger.debug "Binding with administrator account" |
|
| 110 |
ldap_con = initialize_ldap_con(self.account, self.account_password) |
|
| 111 |
end |
|
| 112 | ||
| 113 |
ops = [ |
|
| 114 |
[:delete, :userPassword, password], |
|
| 115 |
[:add, :userPassword, newPassword] |
|
| 116 |
] |
|
| 117 |
#return ldap_con.modify :dn => attrs[:dn], :operations => ops |
|
| 118 |
# This is another password change method, probably more common |
|
| 119 |
newPassword = encode_password(newPassword) |
|
| 120 |
if newPassword.blank? |
|
| 121 |
logger.debug "Invaild password" |
|
| 122 |
return false |
|
| 123 |
else |
|
| 124 |
logger.debug "Try to change password" |
|
| 125 |
return ldap_con.replace_attribute attrs[:dn], :userPassword, newPassword |
|
| 126 |
end |
|
| 127 |
end |
|
| 128 |
rescue |
|
| 129 |
return false |
|
| 130 |
end |
|
| 131 |
return false |
|
| 132 |
end |
|
| 133 | ||
| 73 | 134 |
# Returns true if this source can be searched for users |
| 74 | 135 |
def searchable? |
| 75 | 136 |
!account.to_s.include?("$login") && %w(login firstname lastname mail).all? {|a| send("attr_#{a}?")}
|
| app/models/user.rb | ||
|---|---|---|
| 756 | 756 |
end |
| 757 | 757 |
end |
| 758 | 758 | |
| 759 |
def isExternal? |
|
| 760 |
return auth_source_id.present? |
|
| 761 |
end |
|
| 762 | ||
| 763 |
def changeExternalPassword(password,newPassword,newPasswordConfirm) |
|
| 764 |
return false if newPassword == "" || newPassword.length < Setting.password_min_length.to_i |
|
| 765 |
return false if newPassword != newPasswordConfirm |
|
| 766 |
if (self.isExternal?) |
|
| 767 |
return self.auth_source.change_password(self.login,password,newPassword) |
|
| 768 |
end |
|
| 769 |
return false |
|
| 770 |
end |
|
| 771 | ||
| 759 | 772 |
protected |
| 760 | 773 | |
| 761 | 774 |
def validate_password_length |
| app/views/auth_sources/_form_auth_source_ldap.html.erb | ||
|---|---|---|
| 14 | 14 |
<p><%= f.text_area :filter, :size => 60, :label => :field_auth_source_ldap_filter %></p> |
| 15 | 15 |
<p><%= f.text_field :timeout, :size => 4 %></p> |
| 16 | 16 |
<p><%= f.check_box :onthefly_register, :label => :field_onthefly %></p> |
| 17 |
<p><%= f.check_box :enabled_passwd, :label => :field_enabled_passwd %></p> |
|
| 17 | 18 |
</div> |
| 18 | 19 | |
| 19 | 20 |
<fieldset class="box tabular"><legend><%=l(:label_attribute_plural)%></legend> |
| ... | ... | |
| 21 | 22 |
<p><%= f.text_field :attr_firstname, :size => 20 %></p> |
| 22 | 23 |
<p><%= f.text_field :attr_lastname, :size => 20 %></p> |
| 23 | 24 |
<p><%= f.text_field :attr_mail, :size => 20 %></p> |
| 25 |
<p><%= f.select :password_encryption, AuthSourcesHelper::Encryption.encryptiontypes %></p> |
|
| 24 | 26 |
</fieldset> |
| config/locales/en.yml | ||
|---|---|---|
| 1168 | 1168 |
description_date_from: Enter start date |
| 1169 | 1169 |
description_date_to: Enter end date |
| 1170 | 1170 |
text_repository_identifier_info: 'Only lower case letters (a-z), numbers, dashes and underscores are allowed.<br />Once saved, the identifier cannot be changed.' |
| 1171 |
notice_external_password_error: Error changing external password. |
|
| 1172 |
field_password_encryption: Encryption |
|
| 1173 |
field_enabled_passwd: Enabled password changing |
|
| db/migrate/20151209092800_add_auth_source_external_password.rb | ||
|---|---|---|
| 1 |
class AddAuthSourceExternalPassword < ActiveRecord::Migration |
|
| 2 |
def self.up |
|
| 3 |
add_column :auth_sources, :enabled_passwd, :boolean, :default => false, :null => false |
|
| 4 |
add_column :auth_sources, :password_encryption, :string, :limit => 30 |
|
| 5 |
end |
|
| 6 | ||
| 7 |
def self.down |
|
| 8 |
remove_column :auth_sources, :enabled_passwd |
|
| 9 |
remove_column :auth_sources, :password_encryption |
|
| 10 |
end |
|
| 11 |
end |
|