Browse Source

Add the ability to expire passwords after a configurable number of days (#19458).

Patch by Holger Just and Go MAEDA.

git-svn-id: http://svn.redmine.org/redmine/trunk@14264 e93f8b46-1217-0410-a6f0-8f06a7374b81
tags/3.1.0
Jean-Philippe Lang 9 years ago
parent
commit
51f7060aa8

+ 1
- 0
app/controllers/application_controller.rb View File

@@ -204,6 +204,7 @@ class ApplicationController < ActionController::Base
def check_password_change
if session[:pwd]
if User.current.must_change_password?
flash[:error] = l(:error_password_expired)
redirect_to my_password_path
else
session.delete(:pwd)

+ 12
- 1
app/models/user.rb View File

@@ -323,8 +323,19 @@ class User < Principal
return auth_source.allow_password_changes?
end

def password_expired?
changed_on = self.passwd_changed_on || Time.at(0)
period = Setting.password_max_age.to_i

if period.zero?
false
else
changed_on < period.days.ago
end
end

def must_change_password?
must_change_passwd? && change_password_allowed?
(must_change_passwd? || password_expired?) && change_password_allowed?
end

def generate_password?

+ 1
- 1
app/views/my/password.html.erb View File

@@ -17,7 +17,7 @@
<%= submit_tag l(:button_apply) %>
<% end %>

<% unless @user.must_change_passwd? %>
<% unless @user.must_change_passwd? || @user.password_expired? %>
<% content_for :sidebar do %>
<%= render :partial => 'sidebar' %>
<% end %>

+ 4
- 0
app/views/settings/_authentication.html.erb View File

@@ -14,6 +14,10 @@

<p><%= setting_text_field :password_min_length, :size => 6 %></p>

<p>
<%= setting_select :password_max_age, [[l(:label_disabled), 0]] + [7, 30, 60, 90, 180, 365].collect{|days| [l('datetime.distance_in_words.x_days', :count => days), days.to_s]} %>
</p>

<p><%= setting_check_box :lost_password, :label => :label_password_lost %></p>

<p><%= setting_text_field :max_additional_emails, :size => 6 %></p>

+ 1
- 0
config/locales/de.yml View File

@@ -1026,6 +1026,7 @@ de:
setting_non_working_week_days: Arbeitsfreie Tage
setting_openid: Erlaube OpenID-Anmeldung und -Registrierung
setting_password_min_length: Mindestlänge des Kennworts
setting_password_max_age: Erzwinge Passwortwechsel nach
setting_per_page_options: Objekte pro Seite
setting_plain_text_mail: Nur reinen Text (kein HTML) senden
setting_protocol: Protokoll

+ 2
- 0
config/locales/en.yml View File

@@ -204,6 +204,7 @@ en:
error_attachment_too_big: "This file cannot be uploaded because it exceeds the maximum allowed file size (%{max_size})"
error_session_expired: "Your session has expired. Please login again."
warning_attachments_not_saved: "%{count} file(s) could not be saved."
error_password_expired: "Your password has expired or the administrator requires you to change it."

mail_subject_lost_password: "Your %{value} password"
mail_body_lost_password: 'To change your password, click on the following link:'
@@ -386,6 +387,7 @@ en:
setting_file_max_size_displayed: Maximum size of text files displayed inline
setting_repository_log_display_limit: Maximum number of revisions displayed on file log
setting_openid: Allow OpenID login and registration
setting_password_max_age: Require password change after
setting_password_min_length: Minimum password length
setting_new_project_user_role_id: Role given to a non-admin user who creates a project
setting_default_projects_modules: Default enabled modules for new projects

+ 2
- 0
config/locales/fr.yml View File

@@ -224,6 +224,7 @@ fr:
error_attachment_too_big: Ce fichier ne peut pas être attaché car il excède la taille maximale autorisée (%{max_size})
error_session_expired: "Votre session a expiré. Veuillez vous reconnecter."
warning_attachments_not_saved: "%{count} fichier(s) n'ont pas pu être sauvegardés."
error_password_expired: "Votre mot de passe a expiré ou nécessite d'être changé."

mail_subject_lost_password: "Votre mot de passe %{value}"
mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :'
@@ -406,6 +407,7 @@ fr:
setting_file_max_size_displayed: Taille maximum des fichiers texte affichés en ligne
setting_repository_log_display_limit: "Nombre maximum de révisions affichées sur l'historique d'un fichier"
setting_openid: "Autoriser l'authentification et l'enregistrement OpenID"
setting_password_max_age: Expiration des mots de passe après
setting_password_min_length: Longueur minimum des mots de passe
setting_new_project_user_role_id: Rôle donné à un utilisateur non-administrateur qui crée un projet
setting_default_projects_modules: Modules activés par défaut pour les nouveaux projets

+ 4
- 0
config/settings.yml View File

@@ -36,6 +36,10 @@ unsubscribe:
password_min_length:
format: int
default: 8
# Maximum password age in days
password_max_age:
format: int
default: 0
# Maximum number of additional email addresses per user
max_additional_emails:
format: int

+ 34
- 0
test/integration/account_test.rb View File

@@ -150,6 +150,40 @@ class AccountTest < Redmine::IntegrationTest
assert_equal false, User.find_by_login('jsmith').must_change_passwd?
end

def test_user_with_expired_password_should_be_forced_to_change_its_password
User.find_by_login('jsmith').update_attribute :passwd_changed_on, 14.days.ago

with_settings :password_max_age => 7 do
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
end

def test_user_with_expired_password_should_be_able_to_change_its_password
User.find_by_login('jsmith').update_attribute :passwd_changed_on, 14.days.ago

with_settings :password_max_age => 7 do
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

end

def test_register_with_automatic_activation
Setting.self_registration = '3'


Loading…
Cancel
Save