From 8d883b1e1701438218abf62f286a2907e92b5051 Mon Sep 17 00:00:00 2001 From: Marcia Date: Thu, 15 Sep 2016 11:45:03 +0200 Subject: [PATCH 1/7] mejoras de codigo --- Gemfile | 1 + Gemfile.lock | 4 + app/models/user.rb | 4 +- .../devise/password_expired/show.html.erb | 14 ++ config/i18n-tasks.yml | 1 + .../initializers/devise_security_extension.rb | 127 ++++++++++++++++++ config/locales/activerecord.en.yml | 1 + config/locales/activerecord.es.yml | 1 + config/locales/devise.en.yml | 7 + config/locales/devise.es.yml | 7 + .../locales/devise.security_extension.de.yml | 16 +++ .../locales/devise.security_extension.en.yml | 16 +++ .../locales/devise.security_extension.it.yml | 10 ++ .../20160901104320_add_password_expired.rb | 6 + .../20160901104320_add_password_expired.rb~ | 4 + db/schema.rb | 4 +- spec/features/users_auth_spec.rb | 37 +++++ 17 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 app/views/devise/password_expired/show.html.erb create mode 100644 config/initializers/devise_security_extension.rb create mode 100644 config/locales/devise.security_extension.de.yml create mode 100644 config/locales/devise.security_extension.en.yml create mode 100644 config/locales/devise.security_extension.it.yml create mode 100644 db/migrate/20160901104320_add_password_expired.rb create mode 100644 db/migrate/20160901104320_add_password_expired.rb~ diff --git a/Gemfile b/Gemfile index 3f3119770..9d7499d82 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem 'jquery-ui-rails' gem 'turbolinks' gem 'devise', '~> 3.5.7' +gem 'devise_security_extension' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' gem 'omniauth' diff --git a/Gemfile.lock b/Gemfile.lock index 103ec214d..e918a9648 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -138,6 +138,9 @@ GEM warden (~> 1.2.3) devise-async (0.10.2) devise (>= 3.2, < 4.0) + devise_security_extension (0.10.0) + devise (>= 3.0.0, < 4.0) + railties (>= 3.2.6, < 5.0) diff-lcs (1.2.5) docile (1.1.5) easy_translate (0.5.0) @@ -460,6 +463,7 @@ DEPENDENCIES delayed_job_active_record (~> 4.1.0) devise (~> 3.5.7) devise-async + devise_security_extension email_spec factory_girl_rails faker diff --git a/app/models/user.rb b/app/models/user.rb index 60bc2364a..633610d9d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,8 +1,8 @@ -class User < ActiveRecord::Base + class User < ActiveRecord::Base include Verification - devise :database_authenticatable, :registerable, :confirmable, + devise :password_expirable, :secure_validatable, :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :async acts_as_voter diff --git a/app/views/devise/password_expired/show.html.erb b/app/views/devise/password_expired/show.html.erb new file mode 100644 index 000000000..0c750dd1e --- /dev/null +++ b/app/views/devise/password_expired/show.html.erb @@ -0,0 +1,14 @@ +

<%= t("devise.password_expired.expire_password") %>

+ +<%= form_for(resource, :as => resource_name, :url => [resource_name, :password_expired], :html => { :method => :put }) do |f| %> + <%= devise_error_messages! %> + + <%= f.password_field :current_password %>

+ + <%= f.label t("devise.password_expired.new_password") %> + <%= f.password_field :password, label: false %>

+ + <%= f.password_field :password_confirmation %>

+ +

<%= f.submit t("devise.password_expired.change_password") %>

+<% end %> \ No newline at end of file diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index 250ed18f9..9cf9f8137 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -101,6 +101,7 @@ ignore_missing: - 'errors.messages.taken' - 'devise.failure.invalid' - 'devise.registrations.destroyed' + - 'devise.password_expired.*' ## Consider these keys used: ignore_unused: diff --git a/config/initializers/devise_security_extension.rb b/config/initializers/devise_security_extension.rb new file mode 100644 index 000000000..83b88200e --- /dev/null +++ b/config/initializers/devise_security_extension.rb @@ -0,0 +1,127 @@ +Devise.setup do |config| + # ==> Security Extension + # Configure security extension for devise + + # Should the password expire (e.g 3.months) + # config.expire_password_after = false + config.expire_password_after = 1.year + + # Need 1 char of A-Z, a-z and 0-9 + # config.password_regex = /(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])/ + + # How many passwords to keep in archive + # config.password_archiving_count = 5 + + # Deny old password (true, false, count) + # config.deny_old_passwords = true + + # enable email validation for :secure_validatable. (true, false, validation_options) + # dependency: need an email validator like rails_email_validator + # config.email_validation = true + + # captcha integration for recover form + # config.captcha_for_recover = true + + # captcha integration for sign up form + # config.captcha_for_sign_up = true + + # captcha integration for sign in form + # config.captcha_for_sign_in = true + + # captcha integration for unlock form + # config.captcha_for_unlock = true + + # captcha integration for confirmation form + # config.captcha_for_confirmation = true + + # Time period for account expiry from last_activity_at + # config.expire_after = 90.days +end + +module Devise + module Models + module PasswordExpirable + def need_change_password? + if self.administrator? + #is administrator + if self.expire_password_after.is_a? Fixnum or self.expire_password_after.is_a? Float + self.password_changed_at.nil? or self.password_changed_at < self.expire_password_after.ago + else + #not change password + false + end + else + #It is not an administrator + false + end + end + end + + module SecureValidatable + def self.included(base) + base.extend ClassMethods + assert_secure_validations_api!(base) + + base.class_eval do + # validate login in a strict way if not yet validated + unless devise_validation_enabled? + validates :email, :presence => true, :if => :email_required? + validates :email, :uniqueness => true, :allow_blank => true, :if => :email_changed? # check uniq for email ever + validates :password, :presence => true, :length => password_length, :confirmation => true, :if => :password_required? + end + + # extra validations + #validates :password, :format => { :with => password_regex, :message => :password_format }, :if => :password_required? + # don't allow use same password + validate :current_equal_password_validation + end + end + + def self.assert_secure_validations_api!(base) + raise "Could not use SecureValidatable on #{base}" unless base.respond_to?(:validates) + end + + def current_equal_password_validation + if !self.new_record? && !self.encrypted_password_change.nil? && !self.erased? + dummy = self.class.new + dummy.encrypted_password = self.encrypted_password_change.first + dummy.password_salt = self.password_salt_change.first if self.respond_to? :password_salt_change and not self.password_salt_change.nil? + self.errors.add(:password, :equal_to_current_password) if dummy.valid_password?(self.password) + end + end + + protected + + # Checks whether a password is needed or not. For validations only. + # Passwords are always required if it's a new record, or if the password + # or confirmation are being set somewhere. + def password_required? + !persisted? || !password.nil? || !password_confirmation.nil? + end + + def email_required? + true + end + + module ClassMethods + Devise::Models.config(self, :password_regex, :password_length, :email_validation) + + private + def has_uniqueness_validation_of_login? + validators.any? do |validator| + validator.kind_of?(ActiveRecord::Validations::UniquenessValidator) && + validator.attributes.include?(login_attribute) + end + end + + def login_attribute + authentication_keys[0] + end + + def devise_validation_enabled? + self.ancestors.map(&:to_s).include? 'Devise::Models::Validatable' + end + end + end + end +end \ No newline at end of file diff --git a/config/locales/activerecord.en.yml b/config/locales/activerecord.en.yml index 07791e53c..7de5b340e 100644 --- a/config/locales/activerecord.en.yml +++ b/config/locales/activerecord.en.yml @@ -54,6 +54,7 @@ en: username: "Username" password_confirmation: "Password confirmation" password: "Password" + current_password: "Current password" phone_number: "Phone number" official_position: "Official position" official_level: "Official level" diff --git a/config/locales/activerecord.es.yml b/config/locales/activerecord.es.yml index d5b7f0005..4dae4cb56 100644 --- a/config/locales/activerecord.es.yml +++ b/config/locales/activerecord.es.yml @@ -54,6 +54,7 @@ es: username: "Nombre de usuario" password_confirmation: "Confirmación de contraseña" password: "Contraseña" + current_password: "Contraseña actual" phone_number: "Teléfono" official_position: "Cargo público" official_level: "Nivel del cargo" diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 30fb488ce..f031d62b5 100755 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -2,6 +2,12 @@ en: devise: + password_expired: + expire_password: "Password expired" + change_required: "Your password has expired" + change_password: "Change your password" + new_password: "New password" + updated: "Password successfully updated" confirmations: confirmed: "Your account has been confirmed." send_instructions: "In a few minutes you will receive an email containing instructions on how to reset your password." @@ -62,3 +68,4 @@ en: not_saved: one: "1 error prevented this %{resource} from being saved:" other: "%{count} errors prevented this %{resource} from being saved:" + equal_to_current_password: "must be different than the current password." diff --git a/config/locales/devise.es.yml b/config/locales/devise.es.yml index 1a9c3a3b8..6ede31f7d 100644 --- a/config/locales/devise.es.yml +++ b/config/locales/devise.es.yml @@ -1,5 +1,11 @@ es: devise: + password_expired: + expire_password: "Contraseña caducada" + change_required: "Tu contraseña ha caducado" + change_password: "Cambia tu contraseña" + new_password: "Nueva contraseña" + updated: "Contraseña actualizada con éxito" confirmations: confirmed: "Tu cuenta ha sido confirmada. Por favor autentifícate con tu red social o tu usuario y contraseña" send_instructions: "Recibirás un correo electrónico en unos minutos con instrucciones sobre cómo restablecer tu contraseña." @@ -60,3 +66,4 @@ es: not_saved: one: "1 error impidió que este %{resource} fuera guardado:" other: "%{count} errores impidieron que este %{resource} fuera guardado:" + equal_to_current_password: "debe ser diferente a la contraseña actual" diff --git a/config/locales/devise.security_extension.de.yml b/config/locales/devise.security_extension.de.yml new file mode 100644 index 000000000..cad39d9b7 --- /dev/null +++ b/config/locales/devise.security_extension.de.yml @@ -0,0 +1,16 @@ +de: + errors: + messages: + taken_in_past: "wurde bereits in der Vergangenheit verwendet!" + equal_to_current_password: "darf nicht dem aktuellen Passwort entsprechen!" + password_format: "müssen große, kleine Buchstaben und Ziffern enthalten" + devise: + invalid_captcha: "Die Captchaeingabe ist nicht gültig!" + paranoid_verify: + code_required: "Bitte geben Sie den Code unser Support-Team zur Verfügung gestellt" + password_expired: + updated: "Das neue Passwort wurde übernommen." + change_required: "Ihr Passwort ist abgelaufen. Bitte vergeben sie ein neues Passwort!" + failure: + session_limited: 'Ihre Anmeldedaten wurden in einem anderen Browser genutzt. Bitte melden Sie sich erneut an, um in diesem Browser fortzufahren.' + expired: 'Ihr Account ist aufgrund zu langer Inaktiviät abgelaufen. Bitte kontaktieren Sie den Administrator.' diff --git a/config/locales/devise.security_extension.en.yml b/config/locales/devise.security_extension.en.yml new file mode 100644 index 000000000..e73d6e245 --- /dev/null +++ b/config/locales/devise.security_extension.en.yml @@ -0,0 +1,16 @@ +en: + errors: + messages: + taken_in_past: "was used previously." + equal_to_current_password: "must be different than the current password." + password_format: "must contain big, small letters and digits" + devise: + invalid_captcha: "The captcha input was invalid." + paranoid_verify: + code_required: "Please enter the code our support team provided" + password_expired: + updated: "Your new password is saved." + change_required: "Your password is expired. Please renew your password." + failure: + session_limited: 'Your login credentials were used in another browser. Please sign in again to continue in this browser.' + expired: 'Your account has expired due to inactivity. Please contact the site administrator.' diff --git a/config/locales/devise.security_extension.it.yml b/config/locales/devise.security_extension.it.yml new file mode 100644 index 000000000..646ae4ea0 --- /dev/null +++ b/config/locales/devise.security_extension.it.yml @@ -0,0 +1,10 @@ +it: + errors: + messages: + taken_in_past: "e' stata gia' utilizzata in passato!" + equal_to_current_password: " deve essere differente dalla password corrente!" + devise: + invalid_captcha: "Il captcha inserito non e' valido!" + password_expired: + updated: "La tua nuova password e' stata salvata." + change_required: "La tua password e' scaduta. Si prega di rinnovarla!" \ No newline at end of file diff --git a/db/migrate/20160901104320_add_password_expired.rb b/db/migrate/20160901104320_add_password_expired.rb new file mode 100644 index 000000000..aa759dd61 --- /dev/null +++ b/db/migrate/20160901104320_add_password_expired.rb @@ -0,0 +1,6 @@ +class AddPasswordExpired < ActiveRecord::Migration + def change + add_column :users, :password_changed_at, :datetime + add_index :users, :password_changed_at + end +end diff --git a/db/migrate/20160901104320_add_password_expired.rb~ b/db/migrate/20160901104320_add_password_expired.rb~ new file mode 100644 index 000000000..ac3a48f49 --- /dev/null +++ b/db/migrate/20160901104320_add_password_expired.rb~ @@ -0,0 +1,4 @@ +class AddPasswordExpired < ActiveRecord::Migration + def change + end +end diff --git a/db/schema.rb b/db/schema.rb index becde4f8a..339ce7675 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160803154011) do +ActiveRecord::Schema.define(version: 20160901104320) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -464,12 +464,14 @@ ActiveRecord::Schema.define(version: 20160803154011) do t.boolean "email_digest", default: true t.boolean "email_on_direct_message", default: true t.boolean "official_position_badge", default: false + t.datetime "password_changed_at" end add_index "users", ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["geozone_id"], name: "index_users_on_geozone_id", using: :btree add_index "users", ["hidden_at"], name: "index_users_on_hidden_at", using: :btree + add_index "users", ["password_changed_at"], name: "index_users_on_password_changed_at", using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["username"], name: "index_users_on_username", using: :btree diff --git a/spec/features/users_auth_spec.rb b/spec/features/users_auth_spec.rb index 9e1e67ef9..573e34a27 100644 --- a/spec/features/users_auth_spec.rb +++ b/spec/features/users_auth_spec.rb @@ -285,4 +285,41 @@ feature 'Users' do expect(page).to have_content "Your password has been changed successfully." end + + scenario 'Sign in, admin with password expired' do + user = create(:user, password_changed_at: Time.now - 1.year) + admin = create(:administrator, user: user) + + login_as(admin.user) + visit root_path + + expect(page).to have_content "Your password has expired" + + fill_in 'user_current_password', with: 'judgmentday' + fill_in 'user_password', with: '123456789' + fill_in 'user_password_confirmation', with: '123456789' + + click_button 'Change your password' + + expect(page).to have_content "Password successfully updated" + end + + scenario 'Sign in, admin without password expired' do + user = create(:user, password_changed_at: Time.now - 360.days) + admin = create(:administrator, user: user) + + login_as(admin.user) + visit root_path + + expect(page).to_not have_content "Your password has expired" + end + + scenario 'Sign in, user with password expired' do + user = create(:user, password_changed_at: Time.now - 1.year) + + login_as(user) + visit root_path + expect(page).to_not have_content "Your password has expired" + end + end From 48325589485207108aa5cc239da0b0b024fde32d Mon Sep 17 00:00:00 2001 From: Marcia Date: Thu, 15 Sep 2016 12:21:25 +0200 Subject: [PATCH 2/7] modificado mensaje en el test --- spec/features/users_auth_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/features/users_auth_spec.rb b/spec/features/users_auth_spec.rb index 573e34a27..c842e73bc 100644 --- a/spec/features/users_auth_spec.rb +++ b/spec/features/users_auth_spec.rb @@ -311,7 +311,7 @@ feature 'Users' do login_as(admin.user) visit root_path - expect(page).to_not have_content "Your password has expired" + expect(page).to_not have_content "Your password is expired" end scenario 'Sign in, user with password expired' do @@ -319,7 +319,7 @@ feature 'Users' do login_as(user) visit root_path - expect(page).to_not have_content "Your password has expired" + expect(page).to_not have_content "Your password is expired" end end From fa833f5a4a4a913d494907b35970ff6d8620f83a Mon Sep 17 00:00:00 2001 From: Marcia Date: Thu, 15 Sep 2016 13:06:03 +0200 Subject: [PATCH 3/7] modificado otro mensaje en los test --- spec/features/users_auth_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/features/users_auth_spec.rb b/spec/features/users_auth_spec.rb index c842e73bc..1b365f16d 100644 --- a/spec/features/users_auth_spec.rb +++ b/spec/features/users_auth_spec.rb @@ -293,7 +293,7 @@ feature 'Users' do login_as(admin.user) visit root_path - expect(page).to have_content "Your password has expired" + expect(page).to have_content "Your password is expired" fill_in 'user_current_password', with: 'judgmentday' fill_in 'user_password', with: '123456789' From 78c6a30424df21f6743b8812ac7445a23d3594a3 Mon Sep 17 00:00:00 2001 From: Marcia Date: Tue, 27 Sep 2016 13:07:06 +0200 Subject: [PATCH 4/7] optimize code --- app/models/user.rb | 6 ++ .../initializers/devise_security_extension.rb | 81 +++++-------------- config/locales/devise.en.yml | 2 +- .../locales/devise.security_extension.de.yml | 16 ---- .../locales/devise.security_extension.en.yml | 16 ---- .../locales/devise.security_extension.it.yml | 10 --- lib/tasks/users.rake | 8 ++ spec/features/users_auth_spec.rb | 15 ++++ 8 files changed, 49 insertions(+), 105 deletions(-) delete mode 100644 config/locales/devise.security_extension.de.yml delete mode 100644 config/locales/devise.security_extension.en.yml delete mode 100644 config/locales/devise.security_extension.it.yml diff --git a/app/models/user.rb b/app/models/user.rb index 633610d9d..1c9ace227 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -57,6 +57,8 @@ before_validation :clean_document_number + before_create :set_password_changed_at + # Get the existing user by email if the provider gives us a verified email. def self.first_or_initialize_for_oauth(auth) oauth_email = auth.info.email @@ -240,6 +242,10 @@ true end + def set_password_changed_at + set_password_changed_at = created_at + end + def ability @ability ||= Ability.new(self) end diff --git a/config/initializers/devise_security_extension.rb b/config/initializers/devise_security_extension.rb index 83b88200e..b38393b75 100644 --- a/config/initializers/devise_security_extension.rb +++ b/config/initializers/devise_security_extension.rb @@ -10,7 +10,7 @@ Devise.setup do |config| # config.password_regex = /(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])/ # How many passwords to keep in archive - # config.password_archiving_count = 5 + #config.password_archiving_count = 5 # Deny old password (true, false, count) # config.deny_old_passwords = true @@ -18,7 +18,6 @@ Devise.setup do |config| # enable email validation for :secure_validatable. (true, false, validation_options) # dependency: need an email validator like rails_email_validator # config.email_validation = true - # captcha integration for recover form # config.captcha_for_recover = true @@ -42,20 +41,22 @@ module Devise module Models module PasswordExpirable def need_change_password? - if self.administrator? - #is administrator - if self.expire_password_after.is_a? Fixnum or self.expire_password_after.is_a? Float - self.password_changed_at.nil? or self.password_changed_at < self.expire_password_after.ago - else - #not change password - false - end - else - #It is not an administrator - false + if password_change? + password_expired? + else + false end end - end + + def password_change? + self.administrator? && password_expired? + end + + def password_expired? + self.password_changed_at < self.expire_password_after.ago + end + + end #module PasswordExpirable module SecureValidatable def self.included(base) @@ -63,24 +64,11 @@ module Devise assert_secure_validations_api!(base) base.class_eval do - # validate login in a strict way if not yet validated - unless devise_validation_enabled? - validates :email, :presence => true, :if => :email_required? - validates :email, :uniqueness => true, :allow_blank => true, :if => :email_changed? # check uniq for email ever - validates :password, :presence => true, :length => password_length, :confirmation => true, :if => :password_required? - end - - # extra validations - #validates :password, :format => { :with => password_regex, :message => :password_format }, :if => :password_required? - # don't allow use same password + validate :current_equal_password_validation end end - def self.assert_secure_validations_api!(base) - raise "Could not use SecureValidatable on #{base}" unless base.respond_to?(:validates) - end - def current_equal_password_validation if !self.new_record? && !self.encrypted_password_change.nil? && !self.erased? dummy = self.class.new @@ -90,38 +78,7 @@ module Devise end end - protected + end #module SecureValidatable - # Checks whether a password is needed or not. For validations only. - # Passwords are always required if it's a new record, or if the password - # or confirmation are being set somewhere. - def password_required? - !persisted? || !password.nil? || !password_confirmation.nil? - end - - def email_required? - true - end - - module ClassMethods - Devise::Models.config(self, :password_regex, :password_length, :email_validation) - - private - def has_uniqueness_validation_of_login? - validators.any? do |validator| - validator.kind_of?(ActiveRecord::Validations::UniquenessValidator) && - validator.attributes.include?(login_attribute) - end - end - - def login_attribute - authentication_keys[0] - end - - def devise_validation_enabled? - self.ancestors.map(&:to_s).include? 'Devise::Models::Validatable' - end - end - end - end -end \ No newline at end of file + end #module Models +end #module Devise \ No newline at end of file diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index f031d62b5..29bc0a620 100755 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -4,7 +4,7 @@ en: devise: password_expired: expire_password: "Password expired" - change_required: "Your password has expired" + change_required: "Your password is expired" change_password: "Change your password" new_password: "New password" updated: "Password successfully updated" diff --git a/config/locales/devise.security_extension.de.yml b/config/locales/devise.security_extension.de.yml deleted file mode 100644 index cad39d9b7..000000000 --- a/config/locales/devise.security_extension.de.yml +++ /dev/null @@ -1,16 +0,0 @@ -de: - errors: - messages: - taken_in_past: "wurde bereits in der Vergangenheit verwendet!" - equal_to_current_password: "darf nicht dem aktuellen Passwort entsprechen!" - password_format: "müssen große, kleine Buchstaben und Ziffern enthalten" - devise: - invalid_captcha: "Die Captchaeingabe ist nicht gültig!" - paranoid_verify: - code_required: "Bitte geben Sie den Code unser Support-Team zur Verfügung gestellt" - password_expired: - updated: "Das neue Passwort wurde übernommen." - change_required: "Ihr Passwort ist abgelaufen. Bitte vergeben sie ein neues Passwort!" - failure: - session_limited: 'Ihre Anmeldedaten wurden in einem anderen Browser genutzt. Bitte melden Sie sich erneut an, um in diesem Browser fortzufahren.' - expired: 'Ihr Account ist aufgrund zu langer Inaktiviät abgelaufen. Bitte kontaktieren Sie den Administrator.' diff --git a/config/locales/devise.security_extension.en.yml b/config/locales/devise.security_extension.en.yml deleted file mode 100644 index e73d6e245..000000000 --- a/config/locales/devise.security_extension.en.yml +++ /dev/null @@ -1,16 +0,0 @@ -en: - errors: - messages: - taken_in_past: "was used previously." - equal_to_current_password: "must be different than the current password." - password_format: "must contain big, small letters and digits" - devise: - invalid_captcha: "The captcha input was invalid." - paranoid_verify: - code_required: "Please enter the code our support team provided" - password_expired: - updated: "Your new password is saved." - change_required: "Your password is expired. Please renew your password." - failure: - session_limited: 'Your login credentials were used in another browser. Please sign in again to continue in this browser.' - expired: 'Your account has expired due to inactivity. Please contact the site administrator.' diff --git a/config/locales/devise.security_extension.it.yml b/config/locales/devise.security_extension.it.yml deleted file mode 100644 index 646ae4ea0..000000000 --- a/config/locales/devise.security_extension.it.yml +++ /dev/null @@ -1,10 +0,0 @@ -it: - errors: - messages: - taken_in_past: "e' stata gia' utilizzata in passato!" - equal_to_current_password: " deve essere differente dalla password corrente!" - devise: - invalid_captcha: "Il captcha inserito non e' valido!" - password_expired: - updated: "La tua nuova password e' stata salvata." - change_required: "La tua password e' scaduta. Si prega di rinnovarla!" \ No newline at end of file diff --git a/lib/tasks/users.rake b/lib/tasks/users.rake index efbeeaa94..98fcae5d2 100644 --- a/lib/tasks/users.rake +++ b/lib/tasks/users.rake @@ -76,4 +76,12 @@ namespace :users do task remove_erased_identities: :environment do Identity.joins(:user).where('users.erased_at IS NOT NULL').destroy_all end + + desc "Update password changed at for existing users" + task update_password_changed_at: :environment do + User.all.each do |user| + user.update(password_changed_at:user.created_at) + end + end + end diff --git a/spec/features/users_auth_spec.rb b/spec/features/users_auth_spec.rb index 1b365f16d..f16772fd4 100644 --- a/spec/features/users_auth_spec.rb +++ b/spec/features/users_auth_spec.rb @@ -322,4 +322,19 @@ feature 'Users' do expect(page).to_not have_content "Your password is expired" end + scenario 'Admin with password expired trying to use same password' do + user = create(:user, password_changed_at: Time.now - 1.year, password: '123456789') + admin = create(:administrator, user: user) + login_as(admin.user) + visit root_path + expect(page).to have_content "Your password is expired" + fill_in 'user_current_password', with: 'judgmentday' + fill_in 'user_password', with: '123456789' + fill_in 'user_password_confirmation', with: '123456789' + click_button 'Change your password' + expect(page).to have_content "must be different than the current password." + #expect(page).to have_content "You can not use the same password. Please choose another one." + end + + end From 1e87810593c37659a2b3d388a4c1760c175b0569 Mon Sep 17 00:00:00 2001 From: Marcia Date: Thu, 29 Sep 2016 14:34:37 +0200 Subject: [PATCH 5/7] improves expired password view --- app/models/user.rb | 4 ++-- app/views/devise/password_expired/show.html.erb | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 1c9ace227..75367dbe1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,8 +2,8 @@ include Verification - devise :password_expirable, :secure_validatable, :database_authenticatable, :registerable, :confirmable, - :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :async + devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, + :trackable, :validatable, :omniauthable, :async, :password_expirable, :secure_validatable acts_as_voter acts_as_paranoid column: :hidden_at diff --git a/app/views/devise/password_expired/show.html.erb b/app/views/devise/password_expired/show.html.erb index 0c750dd1e..33dc82f71 100644 --- a/app/views/devise/password_expired/show.html.erb +++ b/app/views/devise/password_expired/show.html.erb @@ -1,7 +1,6 @@

<%= t("devise.password_expired.expire_password") %>

<%= form_for(resource, :as => resource_name, :url => [resource_name, :password_expired], :html => { :method => :put }) do |f| %> - <%= devise_error_messages! %> <%= f.password_field :current_password %>

From 1f72c5cc74ea1cfcb147e1bb58e5bf495a21af00 Mon Sep 17 00:00:00 2001 From: Marcia Date: Fri, 30 Sep 2016 10:10:20 +0200 Subject: [PATCH 6/7] improves expired password view --- config/initializers/devise_security_extension.rb | 13 ++++--------- db/migrate/20160901104320_add_password_expired.rb~ | 4 ---- spec/features/users_auth_spec.rb | 1 - 3 files changed, 4 insertions(+), 14 deletions(-) delete mode 100644 db/migrate/20160901104320_add_password_expired.rb~ diff --git a/config/initializers/devise_security_extension.rb b/config/initializers/devise_security_extension.rb index b38393b75..5b13fc905 100644 --- a/config/initializers/devise_security_extension.rb +++ b/config/initializers/devise_security_extension.rb @@ -55,16 +55,13 @@ module Devise def password_expired? self.password_changed_at < self.expire_password_after.ago end - - end #module PasswordExpirable + end module SecureValidatable def self.included(base) base.extend ClassMethods assert_secure_validations_api!(base) - base.class_eval do - validate :current_equal_password_validation end end @@ -77,8 +74,6 @@ module Devise self.errors.add(:password, :equal_to_current_password) if dummy.valid_password?(self.password) end end - - end #module SecureValidatable - - end #module Models -end #module Devise \ No newline at end of file + end + end +end \ No newline at end of file diff --git a/db/migrate/20160901104320_add_password_expired.rb~ b/db/migrate/20160901104320_add_password_expired.rb~ deleted file mode 100644 index ac3a48f49..000000000 --- a/db/migrate/20160901104320_add_password_expired.rb~ +++ /dev/null @@ -1,4 +0,0 @@ -class AddPasswordExpired < ActiveRecord::Migration - def change - end -end diff --git a/spec/features/users_auth_spec.rb b/spec/features/users_auth_spec.rb index f16772fd4..bd58eabe0 100644 --- a/spec/features/users_auth_spec.rb +++ b/spec/features/users_auth_spec.rb @@ -333,7 +333,6 @@ feature 'Users' do fill_in 'user_password_confirmation', with: '123456789' click_button 'Change your password' expect(page).to have_content "must be different than the current password." - #expect(page).to have_content "You can not use the same password. Please choose another one." end From 680c0636edf554ee579212ed6ce9fd3d3530320f Mon Sep 17 00:00:00 2001 From: Marcia Date: Thu, 6 Oct 2016 14:19:48 +0200 Subject: [PATCH 7/7] expired password last --- app/models/user.rb | 8 +------- config/initializers/devise_security_extension.rb | 10 +--------- lib/tasks/users.rake | 2 +- spec/features/users_auth_spec.rb | 8 ++++++-- 4 files changed, 9 insertions(+), 19 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 75367dbe1..916c3d0b9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,4 @@ - class User < ActiveRecord::Base +class User < ActiveRecord::Base include Verification @@ -57,8 +57,6 @@ before_validation :clean_document_number - before_create :set_password_changed_at - # Get the existing user by email if the provider gives us a verified email. def self.first_or_initialize_for_oauth(auth) oauth_email = auth.info.email @@ -242,10 +240,6 @@ true end - def set_password_changed_at - set_password_changed_at = created_at - end - def ability @ability ||= Ability.new(self) end diff --git a/config/initializers/devise_security_extension.rb b/config/initializers/devise_security_extension.rb index 5b13fc905..6e691acbe 100644 --- a/config/initializers/devise_security_extension.rb +++ b/config/initializers/devise_security_extension.rb @@ -41,16 +41,8 @@ module Devise module Models module PasswordExpirable def need_change_password? - if password_change? - password_expired? - else - false - end - end - - def password_change? self.administrator? && password_expired? - end + end def password_expired? self.password_changed_at < self.expire_password_after.ago diff --git a/lib/tasks/users.rake b/lib/tasks/users.rake index 98fcae5d2..ccb749f9a 100644 --- a/lib/tasks/users.rake +++ b/lib/tasks/users.rake @@ -80,7 +80,7 @@ namespace :users do desc "Update password changed at for existing users" task update_password_changed_at: :environment do User.all.each do |user| - user.update(password_changed_at:user.created_at) + user.update(password_changed_at: user.created_at) end end diff --git a/spec/features/users_auth_spec.rb b/spec/features/users_auth_spec.rb index bd58eabe0..5b3c157d7 100644 --- a/spec/features/users_auth_spec.rb +++ b/spec/features/users_auth_spec.rb @@ -319,21 +319,25 @@ feature 'Users' do login_as(user) visit root_path + expect(page).to_not have_content "Your password is expired" end scenario 'Admin with password expired trying to use same password' do user = create(:user, password_changed_at: Time.now - 1.year, password: '123456789') admin = create(:administrator, user: user) + login_as(admin.user) - visit root_path + visit root_path + expect(page).to have_content "Your password is expired" + fill_in 'user_current_password', with: 'judgmentday' fill_in 'user_password', with: '123456789' fill_in 'user_password_confirmation', with: '123456789' click_button 'Change your password' + expect(page).to have_content "must be different than the current password." end - end