Option to generate a random password on user creation/update.
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@11456 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
78997eea16
commit
fac4a79d4c
|
@ -80,6 +80,7 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
def new
|
def new
|
||||||
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
|
@user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
|
||||||
|
@user.safe_attributes = params[:user]
|
||||||
@auth_sources = AuthSource.all
|
@auth_sources = AuthSource.all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -96,13 +97,14 @@ class UsersController < ApplicationController
|
||||||
@user.pref.save
|
@user.pref.save
|
||||||
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
|
||||||
|
|
||||||
Mailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
|
Mailer.account_information(@user, @user.password).deliver if params[:send_information]
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html {
|
format.html {
|
||||||
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
|
flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user)))
|
||||||
if params[:continue]
|
if params[:continue]
|
||||||
redirect_to new_user_path
|
attrs = params[:user].slice(:generate_password)
|
||||||
|
redirect_to new_user_path(:user => attrs)
|
||||||
else
|
else
|
||||||
redirect_to edit_user_path(@user)
|
redirect_to edit_user_path(@user)
|
||||||
end
|
end
|
||||||
|
@ -145,8 +147,8 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
if was_activated
|
if was_activated
|
||||||
Mailer.account_activated(@user).deliver
|
Mailer.account_activated(@user).deliver
|
||||||
elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
|
elsif @user.active? && params[:send_information] && @user.password.present? && @user.auth_source_id.nil?
|
||||||
Mailer.account_information(@user, params[:user][:password]).deliver
|
Mailer.account_information(@user, @user.password).deliver
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -81,7 +81,7 @@ class User < Principal
|
||||||
|
|
||||||
acts_as_customizable
|
acts_as_customizable
|
||||||
|
|
||||||
attr_accessor :password, :password_confirmation
|
attr_accessor :password, :password_confirmation, :generate_password
|
||||||
attr_accessor :last_before_login_on
|
attr_accessor :last_before_login_on
|
||||||
# Prevents unauthorized assignments
|
# Prevents unauthorized assignments
|
||||||
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
|
attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
|
||||||
|
@ -103,7 +103,7 @@ class User < Principal
|
||||||
validate :validate_password_length
|
validate :validate_password_length
|
||||||
|
|
||||||
before_create :set_mail_notification
|
before_create :set_mail_notification
|
||||||
before_save :update_hashed_password
|
before_save :generate_password_if_needed, :update_hashed_password
|
||||||
before_destroy :remove_references_before_destroy
|
before_destroy :remove_references_before_destroy
|
||||||
|
|
||||||
scope :in_group, lambda {|group|
|
scope :in_group, lambda {|group|
|
||||||
|
@ -274,13 +274,16 @@ class User < Principal
|
||||||
return auth_source.allow_password_changes?
|
return auth_source.allow_password_changes?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generate and set a random password. Useful for automated user creation
|
def generate_password?
|
||||||
# Based on Token#generate_token_value
|
generate_password == '1' || generate_password == true
|
||||||
#
|
end
|
||||||
def random_password
|
|
||||||
|
# Generate and set a random password on given length
|
||||||
|
def random_password(length=40)
|
||||||
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
|
||||||
|
chars -= %w(0 O 1 l)
|
||||||
password = ''
|
password = ''
|
||||||
40.times { |i| password << chars[rand(chars.size-1)] }
|
length.times {|i| password << chars[SecureRandom.random_number(chars.size)] }
|
||||||
self.password = password
|
self.password = password
|
||||||
self.password_confirmation = password
|
self.password_confirmation = password
|
||||||
self
|
self
|
||||||
|
@ -541,6 +544,7 @@ class User < Principal
|
||||||
|
|
||||||
safe_attributes 'status',
|
safe_attributes 'status',
|
||||||
'auth_source_id',
|
'auth_source_id',
|
||||||
|
'generate_password',
|
||||||
:if => lambda {|user, current_user| current_user.admin?}
|
:if => lambda {|user, current_user| current_user.admin?}
|
||||||
|
|
||||||
safe_attributes 'group_ids',
|
safe_attributes 'group_ids',
|
||||||
|
@ -610,6 +614,7 @@ class User < Principal
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def validate_password_length
|
def validate_password_length
|
||||||
|
return if password.blank? && generate_password?
|
||||||
# Password length validation based on setting
|
# Password length validation based on setting
|
||||||
if !password.nil? && password.size < Setting.password_min_length.to_i
|
if !password.nil? && password.size < Setting.password_min_length.to_i
|
||||||
errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
|
errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
|
||||||
|
@ -618,6 +623,13 @@ class User < Principal
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def generate_password_if_needed
|
||||||
|
if generate_password? && auth_source.nil?
|
||||||
|
length = [Setting.password_min_length.to_i + 2, 10].max
|
||||||
|
random_password(length)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Removes references that are not handled by associations
|
# Removes references that are not handled by associations
|
||||||
# Things that are not deleted are reassociated with the anonymous user
|
# Things that are not deleted are reassociated with the anonymous user
|
||||||
def remove_references_before_destroy
|
def remove_references_before_destroy
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }), {}, :onchange => "if (this.value=='') {$('#password_fields').show();} else {$('#password_fields').hide();}" %></p>
|
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }), {}, :onchange => "if (this.value=='') {$('#password_fields').show();} else {$('#password_fields').hide();}" %></p>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div id="password_fields" style="<%= 'display:none;' if @user.auth_source %>">
|
<div id="password_fields" style="<%= 'display:none;' if @user.auth_source %>">
|
||||||
|
<p><%= f.check_box :generate_password %></p>
|
||||||
<p><%= f.password_field :password, :required => true, :size => 25 %>
|
<p><%= f.password_field :password, :required => true, :size => 25 %>
|
||||||
<em class="info"><%= l(:text_caracters_minimum, :count => Setting.password_min_length) %></em></p>
|
<em class="info"><%= l(:text_caracters_minimum, :count => Setting.password_min_length) %></em></p>
|
||||||
<p><%= f.password_field :password_confirmation, :required => true, :size => 25 %></p>
|
<p><%= f.password_field :password_confirmation, :required => true, :size => 25 %></p>
|
||||||
|
@ -49,3 +50,16 @@
|
||||||
</div>
|
</div>
|
||||||
<div style="clear:left;"></div>
|
<div style="clear:left;"></div>
|
||||||
<!--[eoform:user]-->
|
<!--[eoform:user]-->
|
||||||
|
|
||||||
|
<%= javascript_tag do %>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#user_generate_password').change(function(){
|
||||||
|
var passwd = $('#user_password, #user_password_confirmation');
|
||||||
|
if ($(this).is(':checked')){
|
||||||
|
passwd.val('').attr('disabled', true);
|
||||||
|
}else{
|
||||||
|
passwd.removeAttr('disabled');
|
||||||
|
}
|
||||||
|
}).trigger('change');
|
||||||
|
});
|
||||||
|
<% end %>
|
||||||
|
|
|
@ -331,6 +331,7 @@ en:
|
||||||
field_board_parent: Parent forum
|
field_board_parent: Parent forum
|
||||||
field_private_notes: Private notes
|
field_private_notes: Private notes
|
||||||
field_inherit_members: Inherit members
|
field_inherit_members: Inherit members
|
||||||
|
field_generate_password: Generate password
|
||||||
|
|
||||||
setting_app_title: Application title
|
setting_app_title: Application title
|
||||||
setting_app_subtitle: Application subtitle
|
setting_app_subtitle: Application subtitle
|
||||||
|
|
|
@ -331,6 +331,7 @@ fr:
|
||||||
field_board_parent: Forum parent
|
field_board_parent: Forum parent
|
||||||
field_private_notes: Notes privées
|
field_private_notes: Notes privées
|
||||||
field_inherit_members: Hériter les membres
|
field_inherit_members: Hériter les membres
|
||||||
|
field_generate_password: Générer un mot de passe
|
||||||
|
|
||||||
setting_app_title: Titre de l'application
|
setting_app_title: Titre de l'application
|
||||||
setting_app_subtitle: Sous-titre de l'application
|
setting_app_subtitle: Sous-titre de l'application
|
||||||
|
|
|
@ -218,6 +218,30 @@ class UsersControllerTest < ActionController::TestCase
|
||||||
assert_equal '0', user.pref[:warn_on_leaving_unsaved]
|
assert_equal '0', user.pref[:warn_on_leaving_unsaved]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_create_with_generate_password_should_email_the_password
|
||||||
|
assert_difference 'User.count' do
|
||||||
|
post :create, :user => {
|
||||||
|
:login => 'randompass',
|
||||||
|
:firstname => 'Random',
|
||||||
|
:lastname => 'Pass',
|
||||||
|
:mail => 'randompass@example.net',
|
||||||
|
:language => 'en',
|
||||||
|
:generate_password => '1',
|
||||||
|
:password => '',
|
||||||
|
:password_confirmation => ''
|
||||||
|
}, :send_information => 1
|
||||||
|
end
|
||||||
|
user = User.order('id DESC').first
|
||||||
|
assert_equal 'randompass', user.login
|
||||||
|
|
||||||
|
mail = ActionMailer::Base.deliveries.last
|
||||||
|
assert_not_nil mail
|
||||||
|
m = mail_body(mail).match(/Password: ([a-zA-Z0-9]+)/)
|
||||||
|
assert m
|
||||||
|
password = m[1]
|
||||||
|
assert user.check_password?(password)
|
||||||
|
end
|
||||||
|
|
||||||
def test_create_with_failure
|
def test_create_with_failure
|
||||||
assert_no_difference 'User.count' do
|
assert_no_difference 'User.count' do
|
||||||
post :create, :user => {}
|
post :create, :user => {}
|
||||||
|
@ -290,6 +314,37 @@ class UsersControllerTest < ActionController::TestCase
|
||||||
assert_mail_body_match 'newpass123', mail
|
assert_mail_body_match 'newpass123', mail
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_update_with_generate_password_should_email_the_password
|
||||||
|
ActionMailer::Base.deliveries.clear
|
||||||
|
Setting.bcc_recipients = '1'
|
||||||
|
|
||||||
|
put :update, :id => 2, :user => {
|
||||||
|
:generate_password => '1',
|
||||||
|
:password => '',
|
||||||
|
:password_confirmation => ''
|
||||||
|
}, :send_information => '1'
|
||||||
|
|
||||||
|
mail = ActionMailer::Base.deliveries.last
|
||||||
|
assert_not_nil mail
|
||||||
|
m = mail_body(mail).match(/Password: ([a-zA-Z0-9]+)/)
|
||||||
|
assert m
|
||||||
|
password = m[1]
|
||||||
|
assert User.find(2).check_password?(password)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_update_without_generate_password_should_not_change_password
|
||||||
|
put :update, :id => 2, :user => {
|
||||||
|
:firstname => 'changed',
|
||||||
|
:generate_password => '0',
|
||||||
|
:password => '',
|
||||||
|
:password_confirmation => ''
|
||||||
|
}, :send_information => '1'
|
||||||
|
|
||||||
|
user = User.find(2)
|
||||||
|
assert_equal 'changed', user.firstname
|
||||||
|
assert user.check_password?('jsmith')
|
||||||
|
end
|
||||||
|
|
||||||
def test_update_user_switchin_from_auth_source_to_password_authentication
|
def test_update_user_switchin_from_auth_source_to_password_authentication
|
||||||
# Configure as auth source
|
# Configure as auth source
|
||||||
u = User.find(2)
|
u = User.find(2)
|
||||||
|
|
|
@ -70,6 +70,27 @@ class UserTest < ActiveSupport::TestCase
|
||||||
assert user.save
|
assert user.save
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_generate_password_on_create_should_set_password
|
||||||
|
user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
|
||||||
|
user.login = "newuser"
|
||||||
|
user.generate_password = true
|
||||||
|
assert user.save
|
||||||
|
|
||||||
|
password = user.password
|
||||||
|
assert user.check_password?(password)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_generate_password_on_update_should_update_password
|
||||||
|
user = User.find(2)
|
||||||
|
hash = user.hashed_password
|
||||||
|
user.generate_password = true
|
||||||
|
assert user.save
|
||||||
|
|
||||||
|
password = user.password
|
||||||
|
assert user.check_password?(password)
|
||||||
|
assert_not_equal hash, user.reload.hashed_password
|
||||||
|
end
|
||||||
|
|
||||||
def test_create
|
def test_create
|
||||||
user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
|
user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue