]> source.dussan.org Git - redmine.git/commitdiff
Add the ability to expire passwords after a configurable number of days (#19458).
authorJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 10 May 2015 10:26:55 +0000 (10:26 +0000)
committerJean-Philippe Lang <jp_lang@yahoo.fr>
Sun, 10 May 2015 10:26:55 +0000 (10:26 +0000)
Patch by Holger Just and Go MAEDA.

git-svn-id: http://svn.redmine.org/redmine/trunk@14264 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/controllers/application_controller.rb
app/models/user.rb
app/views/my/password.html.erb
app/views/settings/_authentication.html.erb
config/locales/de.yml
config/locales/en.yml
config/locales/fr.yml
config/settings.yml
test/integration/account_test.rb

index b6e2eb1205c593da78832cb16b9efa307ce7175f..e1bc6a97f617d336e17d550470c17af96cecd216 100644 (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)
index 8811a65fd9d5600b34508fe33f157e386e619783..5978f06de210b44b481be97e7607881b4c6de85f 100644 (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?
index c3f86b99f9c1a688a66444ce7898782624e2ef2b..6ba2bfc40b58b211a05095d95698cf704394e5bf 100644 (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 %>
index 77b5afced4eb8069783d39ece37b069a946e0b4b..80fb4bd5aae49040cc71cbbe4a65d4da63d6259a 100644 (file)
 
 <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>
index 8c069a4bbf6686b88f4afb1f8d7d632da5894693..c29cd60c4f5a71d364de6c94f1a8f9f45b1ab5fc 100644 (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
index f1a04a0ce2d6c49ce72238cb8bd84f36ffae229b..1f205b0ff13ecce0a951f38e9147c01f161c70af 100644 (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
index 17b9a4cda1defed38ae4bdb6aa4e95cd566d1d40..54dc13a61c877e2979ce16160ef98f5f8ddd8ad4 100644 (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
index d2f0ff9bcd078ed1834b7416dbad5ffa09e7c64e..e5093dd58079b9729089fc7ed1de05cd81cbe7c5 100644 (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
index 7f2f0e4fff6c24a3cfcc97f1e2c2308fdd72860f..9ff6cc0a36f4de2aeb0b5beefade35533096c753 100644 (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'