mejoras de codigo

This commit is contained in:
Marcia
2016-09-15 11:45:03 +02:00
parent c84108ae60
commit 8d883b1e17
17 changed files with 257 additions and 3 deletions

View File

@@ -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'

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,14 @@
<h2><%= t("devise.password_expired.expire_password") %></h2>
<%= form_for(resource, :as => resource_name, :url => [resource_name, :password_expired], :html => { :method => :put }) do |f| %>
<%= devise_error_messages! %>
<%= f.password_field :current_password %></p>
<%= f.label t("devise.password_expired.new_password") %>
<%= f.password_field :password, label: false %></p>
<%= f.password_field :password_confirmation %></p>
<p><%= f.submit t("devise.password_expired.change_password") %></p>
<% end %>

View File

@@ -101,6 +101,7 @@ ignore_missing:
- 'errors.messages.taken'
- 'devise.failure.invalid'
- 'devise.registrations.destroyed'
- 'devise.password_expired.*'
## Consider these keys used:
ignore_unused:

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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."

View File

@@ -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"

View File

@@ -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.'

View File

@@ -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.'

View File

@@ -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!"

View File

@@ -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

View File

@@ -0,0 +1,4 @@
class AddPasswordExpired < ActiveRecord::Migration
def change
end
end

View File

@@ -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

View File

@@ -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