Option to force a user to change his password (#3872).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@12081 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
bd4fba08e5
commit
b764e39847
|
@ -20,7 +20,7 @@ class AccountController < ApplicationController
|
||||||
include CustomFieldsHelper
|
include CustomFieldsHelper
|
||||||
|
|
||||||
# prevents login action to be filtered by check_if_login_required application scope filter
|
# prevents login action to be filtered by check_if_login_required application scope filter
|
||||||
skip_before_filter :check_if_login_required
|
skip_before_filter :check_if_login_required, :check_password_change
|
||||||
|
|
||||||
# Login request and validation
|
# Login request and validation
|
||||||
def login
|
def login
|
||||||
|
|
|
@ -38,7 +38,7 @@ class ApplicationController < ActionController::Base
|
||||||
cookies.delete(autologin_cookie_name)
|
cookies.delete(autologin_cookie_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
before_filter :session_expiration, :user_setup, :check_if_login_required, :set_localization
|
before_filter :session_expiration, :user_setup, :check_if_login_required, :check_password_change, :set_localization
|
||||||
|
|
||||||
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
|
rescue_from ActionController::InvalidAuthenticityToken, :with => :invalid_authenticity_token
|
||||||
rescue_from ::Unauthorized, :with => :deny_access
|
rescue_from ::Unauthorized, :with => :deny_access
|
||||||
|
@ -78,6 +78,9 @@ class ApplicationController < ActionController::Base
|
||||||
session[:user_id] = user.id
|
session[:user_id] = user.id
|
||||||
session[:ctime] = Time.now.utc.to_i
|
session[:ctime] = Time.now.utc.to_i
|
||||||
session[:atime] = Time.now.utc.to_i
|
session[:atime] = Time.now.utc.to_i
|
||||||
|
if user.must_change_password?
|
||||||
|
session[:pwd] = '1'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_setup
|
def user_setup
|
||||||
|
@ -112,6 +115,10 @@ class ApplicationController < ActionController::Base
|
||||||
authenticate_with_http_basic do |username, password|
|
authenticate_with_http_basic do |username, password|
|
||||||
user = User.try_to_login(username, password) || User.find_by_api_key(username)
|
user = User.try_to_login(username, password) || User.find_by_api_key(username)
|
||||||
end
|
end
|
||||||
|
if user && user.must_change_password?
|
||||||
|
render_error :message => 'You must change your password', :status => 403
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
# Switch user if requested by an admin user
|
# Switch user if requested by an admin user
|
||||||
if user && user.admin? && (username = api_switch_user_from_request)
|
if user && user.admin? && (username = api_switch_user_from_request)
|
||||||
|
@ -170,6 +177,16 @@ class ApplicationController < ActionController::Base
|
||||||
require_login if Setting.login_required?
|
require_login if Setting.login_required?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_password_change
|
||||||
|
if session[:pwd]
|
||||||
|
if User.current.must_change_password?
|
||||||
|
redirect_to my_password_path
|
||||||
|
else
|
||||||
|
session.delete(:pwd)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def set_localization
|
def set_localization
|
||||||
lang = nil
|
lang = nil
|
||||||
if User.current.logged?
|
if User.current.logged?
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
class MyController < ApplicationController
|
class MyController < ApplicationController
|
||||||
before_filter :require_login
|
before_filter :require_login
|
||||||
|
# let user change his password when he has to
|
||||||
|
skip_before_filter :check_password_change, :only => :password
|
||||||
|
|
||||||
helper :issues
|
helper :issues
|
||||||
helper :users
|
helper :users
|
||||||
|
@ -90,14 +92,17 @@ class MyController < ApplicationController
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if request.post?
|
if request.post?
|
||||||
if @user.check_password?(params[:password])
|
if !@user.check_password?(params[:password])
|
||||||
|
flash.now[:error] = l(:notice_account_wrong_password)
|
||||||
|
elsif params[:password] == params[:new_password]
|
||||||
|
flash.now[:error] = 'Your new password must be different from your current password'
|
||||||
|
else
|
||||||
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
|
@user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
|
||||||
|
@user.must_change_passwd = false
|
||||||
if @user.save
|
if @user.save
|
||||||
flash[:notice] = l(:notice_account_password_updated)
|
flash[:notice] = l(:notice_account_password_updated)
|
||||||
redirect_to my_account_path
|
redirect_to my_account_path
|
||||||
end
|
end
|
||||||
else
|
|
||||||
flash[:error] = l(:notice_account_wrong_password)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -280,6 +280,10 @@ class User < Principal
|
||||||
return auth_source.allow_password_changes?
|
return auth_source.allow_password_changes?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def must_change_password?
|
||||||
|
must_change_passwd? && change_password_allowed?
|
||||||
|
end
|
||||||
|
|
||||||
def generate_password?
|
def generate_password?
|
||||||
generate_password == '1' || generate_password == true
|
generate_password == '1' || generate_password == true
|
||||||
end
|
end
|
||||||
|
@ -568,6 +572,7 @@ class User < Principal
|
||||||
safe_attributes 'status',
|
safe_attributes 'status',
|
||||||
'auth_source_id',
|
'auth_source_id',
|
||||||
'generate_password',
|
'generate_password',
|
||||||
|
'must_change_passwd',
|
||||||
:if => lambda {|user, current_user| current_user.admin?}
|
:if => lambda {|user, current_user| current_user.admin?}
|
||||||
|
|
||||||
safe_attributes 'group_ids',
|
safe_attributes 'group_ids',
|
||||||
|
|
|
@ -17,6 +17,10 @@
|
||||||
<%= submit_tag l(:button_apply) %>
|
<%= submit_tag l(:button_apply) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
<% unless @user.must_change_passwd? %>
|
||||||
<% content_for :sidebar do %>
|
<% content_for :sidebar do %>
|
||||||
<%= render :partial => 'sidebar' %>
|
<%= render :partial => 'sidebar' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= javascript_tag "$('#password').focus();" %>
|
||||||
|
|
|
@ -28,10 +28,11 @@
|
||||||
<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>
|
||||||
|
<p><%= f.check_box :generate_password %></p>
|
||||||
|
<p><%= f.check_box :must_change_passwd %></p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -335,6 +335,7 @@ en:
|
||||||
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
|
field_generate_password: Generate password
|
||||||
|
field_must_change_passwd: Must change password at next logon
|
||||||
|
|
||||||
setting_app_title: Application title
|
setting_app_title: Application title
|
||||||
setting_app_subtitle: Application subtitle
|
setting_app_subtitle: Application subtitle
|
||||||
|
|
|
@ -335,6 +335,7 @@ fr:
|
||||||
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
|
field_generate_password: Générer un mot de passe
|
||||||
|
field_must_change_passwd: Doit changer de mot de passe à la prochaine connexion
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
class AddUsersMustChangePasswd < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
add_column :users, :must_change_passwd, :boolean, :default => false, :null => false
|
||||||
|
end
|
||||||
|
|
||||||
|
def down
|
||||||
|
remove_column :users, :must_change_passwd
|
||||||
|
end
|
||||||
|
end
|
|
@ -128,6 +128,35 @@ class AccountTest < ActionController::IntegrationTest
|
||||||
assert_equal 0, Token.count
|
assert_equal 0, Token.count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_user_with_must_change_passwd_should_be_forced_to_change_its_password
|
||||||
|
User.find_by_login('jsmith').update_attribute :must_change_passwd, true
|
||||||
|
|
||||||
|
post '/login', :username => 'jsmith', :password => 'jsmith'
|
||||||
|
assert_redirected_to '/my/page'
|
||||||
|
follow_redirect!
|
||||||
|
assert_redirected_to '/my/password'
|
||||||
|
|
||||||
|
get '/issues'
|
||||||
|
assert_redirected_to '/my/password'
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_with_must_change_passwd_should_be_able_to_change_its_password
|
||||||
|
User.find_by_login('jsmith').update_attribute :must_change_passwd, true
|
||||||
|
|
||||||
|
post '/login', :username => 'jsmith', :password => 'jsmith'
|
||||||
|
assert_redirected_to '/my/page'
|
||||||
|
follow_redirect!
|
||||||
|
assert_redirected_to '/my/password'
|
||||||
|
follow_redirect!
|
||||||
|
assert_response :success
|
||||||
|
post '/my/password', :password => 'jsmith', :new_password => 'newpassword', :new_password_confirmation => 'newpassword'
|
||||||
|
assert_redirected_to '/my/account'
|
||||||
|
follow_redirect!
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
assert_equal false, User.find_by_login('jsmith').must_change_passwd?
|
||||||
|
end
|
||||||
|
|
||||||
def test_register_with_automatic_activation
|
def test_register_with_automatic_activation
|
||||||
Setting.self_registration = '3'
|
Setting.self_registration = '3'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue