#3928 administrator new & edit pwd pages

This commit is contained in:
maatinito 2019-06-19 12:36:50 -10:00 committed by Pierre de La Morinerie
parent 220b38ad2a
commit 8d3e3baabc
32 changed files with 187 additions and 77 deletions

View file

@ -1,5 +1,3 @@
require 'zxcvbn'
class Administrateurs::ActivateController < ApplicationController class Administrateurs::ActivateController < ApplicationController
include TrustedDeviceConcern include TrustedDeviceConcern
@ -34,10 +32,6 @@ class Administrateurs::ActivateController < ApplicationController
end end
end end
def test_password_strength
@score = Zxcvbn.test(params[:administrateur][:password], [], ZXCVBN_DICTIONNARIES).score
end
private private
def update_administrateur_params def update_administrateur_params

View file

@ -0,0 +1,69 @@
class Administrateurs::PasswordsController < Devise::PasswordsController
after_action :try_to_authenticate_user, only: [:update]
after_action :try_to_authenticate_gestionnaire, only: [:update]
# GET /resource/password/new
# def new
# super
# end
# POST /resource/password
# def create
# super
# end
# GET /resource/password/edit?reset_password_token=abcdef
# def edit
# super
# end
# PUT /resource/password
# def update
# # params[:user][:password_confirmation] = params[:user][:password]
# super
# end
# protected
# def after_resetting_password_path_for(resource)
# super(resource)
# end
# The path used after sending reset password instructions
# def after_sending_reset_password_instructions_path_for(resource_name)
# super(resource_name)
# end
def try_to_authenticate_user
if administrateur_signed_in?
user = User.find_by(email: current_administrateur.email)
if user
sign_in user
end
end
end
def try_to_authenticate_gestionnaire
if administrateur_signed_in?
gestionnaire = Gestionnaire.find_by(email: current_administrateur.email)
if gestionnaire
sign_in gestionnaire
end
end
end
def test_strength
@score, @words, @length = ZxcvbnService.new(password_params[:password]).complexity
@min_length = PASSWORD_MIN_LENGTH
@min_complexity = PASSWORD_COMPLEXITY_FOR_ADMIN
render 'shared/password/test_strength'
end
private
def password_params
params.require(:administrateur).permit(:reset_password_token, :password)
end
end

View file

@ -14,10 +14,10 @@ class Users::PasswordsController < Devise::PasswordsController
if Administrateur.find_by(email: email) if Administrateur.find_by(email: email)
@devise_mapping = Devise.mappings[:administrateur] @devise_mapping = Devise.mappings[:administrateur]
params[:administrateur] = params[:user] params[:administrateur] = params[:user]
# uncomment to check password complexity for Gestionnaire # uncomment to check password complexity for Gestionnaire
# elsif Gestionnaire.find_by(email: email) # elsif Gestionnaire.find_by(email: email)
# @devise_mapping = Devise.mappings[:gestionnaire] # @devise_mapping = Devise.mappings[:gestionnaire]
# params[:gestionnaire] = params[:user] # params[:gestionnaire] = params[:user]
end end
super super
end end

View file

@ -1,5 +0,0 @@
#strength-bar.password-strength{ class: "strength-#{score}" }
- if score < 4
Mot de passe pas assez complexe
- else
Mot de passe suffisamment complexe

View file

@ -3,27 +3,4 @@
- content_for :footer do - content_for :footer do
= render partial: "root/footer" = render partial: "root/footer"
.container.devise-container = render 'shared/password/new', object: @administrateur, controller: 'administrateurs/activate', test_password_strength: administrateurs_password_test_strength_path
.one-column-centered
= form_for @administrateur, url: { controller: 'administrateurs/activate', action: :create }, html: { class: "form" } do |f|
%br
%h1
Choix du mot de passe
= f.label :email, "Email"
= f.text_field :email, disabled: true
= f.label :password do
Mot de passe
= f.password_field :password, placeholder: 'Mot de passe', data: { remote: true, url: admin_activate_test_password_strength_path }
#strength-bar.password-strength
&nbsp;
.explication
%strong Aide :
Une courte phrase peut être un mot de passe très sécurisé.
= f.hidden_field :reset_password_token, value: params[:token]
= f.submit 'Continuer', class: 'button large primary expand', id: "submit-password", data: { disable_with: "Envoi..." }

View file

@ -1 +0,0 @@
<%= render_to_element('#strength-bar', partial: 'password_strength', outer: true, locals: { score: @score }) %>

View file

@ -0,0 +1,6 @@
- content_for(:title, 'Changement de mot de passe')
- content_for :footer do
= render partial: 'root/footer'
= render 'shared/password/edit', test_password_strength: administrateurs_password_test_strength_path

View file

@ -0,0 +1,21 @@
- content_for(:title, 'Changement de mot de passe')
- content_for :footer do
= render partial: 'root/footer'
.container.devise-container
.one-column-centered
= devise_error_messages!
= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :patch, class: 'form' }) do |f|
%h1 Changement de mot de passe
= f.hidden_field :reset_password_token, value: params[:token]
= f.label 'Nouveau mot de passe'
= render partial 'shared/password/edit_password', locals: { form: f, controller: controller }
= f.submit 'Changer le mot de passe', class: 'button large primary expand', id: "submit-password", data: { disable_with: "Envoi..." }

View file

@ -0,0 +1,10 @@
= form.password_field :password, autofocus: true, autocomplete: 'off', placeholder: 'Mot de passe', data: { remote: true, url: url_for(controller: controller, action: 'test_strength') }
#strength-bar.password-strength
&nbsp;
.explication
#strength-label{ style: 'font-weight: bold' }
Inscrivez un mot de passe.
Une courte phrase avec ponctuation peut être un mot de passe très sécurisé.

View file

@ -0,0 +1,18 @@
.container.devise-container
.one-column-centered
- controller_action = defined?(action) ? action : :create
= form_for object, url: { controller: controller, action: controller_action }, html: { class: "form" } do |f|
%br
%h1
Choix du mot de passe
= f.hidden_field :reset_password_token, value: object.reset_password_token
= f.label :email, "Email"
= f.text_field :email, disabled: true
= f.label :password do
Mot de passe
= render partial: 'shared/password/edit_password', locals: { form: f, controller: controller }
= f.submit 'Continuer', class: 'button large primary expand', id: "submit-password", data: { disable_with: "Envoi..." }

View file

@ -0,0 +1 @@
#strength-bar.password-strength{ class: "strength-#{@length < @min_length ? @score/2 : @score}" }

View file

@ -0,0 +1,16 @@
#strength-label{ style: 'font-weight: bold' }
- if @length > 0
- if @length < @min_length
Le mot de passe doit faire au moins #{@min_length} caractères.
- else
- case @score
- when 0..1
Mot de passe très vulnérable.
- when 2...@min_complexity
Mot de passe vulnérable.
- when @min_complexity...4
Mot de passe acceptable. Vous pouvez valider...<br> ou améliorer votre mot de passe.
- else
Félicitations ! Mot de passe suffisamment fort et sécurisé.
- else
Inscrivez un mot de passe.

View file

@ -0,0 +1,3 @@
<%= render_to_element('#strength-label', partial: 'shared/password/password_strength_label', outer: true) %>
<%= render_to_element('#strength-bar', partial: 'shared/password/password_strength', outer: true) %>
<%= raw("document.querySelector('#submit-password').disabled = #{@score < @min_complexity || @length < @min_length};") %>

View file

@ -80,6 +80,6 @@
"note": "`column` and `order` come from the model, which is validated to prevent injection attacks. Furthermore, the sql injection attack on `column` would need to survive the `to_i`" "note": "`column` and `order` come from the model, which is validated to prevent injection attacks. Furthermore, the sql injection attack on `column` would need to survive the `to_i`"
} }
], ],
"updated": "2019-07-17 16:03:11 +0200", "updated": "2019-07-02 11:23:21 -1000",
"brakeman_version": "4.3.1" "brakeman_version": "4.3.1"
} }

View file

@ -78,8 +78,9 @@ Rails.application.routes.draw do
} }
devise_for :administrateurs, controllers: { devise_for :administrateurs, controllers: {
sessions: 'administrateurs/sessions' sessions: 'administrateurs/sessions',
}, skip: [:password, :registrations] passwords: 'administrateurs/passwords'
}, skip: [:registrations]
devise_for :gestionnaires, controllers: { devise_for :gestionnaires, controllers: {
sessions: 'gestionnaires/sessions', sessions: 'gestionnaires/sessions',
@ -108,6 +109,7 @@ Rails.application.routes.draw do
devise_scope :administrateur do devise_scope :administrateur do
get '/administrateurs/sign_in/demo' => redirect("/users/sign_in") get '/administrateurs/sign_in/demo' => redirect("/users/sign_in")
get '/administrateurs/password/test_strength' => 'administrateurs/passwords#test_strength'
end end
# #
@ -176,7 +178,6 @@ Rails.application.routes.draw do
namespace :admin do namespace :admin do
get 'activate' => '/administrateurs/activate#new' get 'activate' => '/administrateurs/activate#new'
patch 'activate' => '/administrateurs/activate#create' patch 'activate' => '/administrateurs/activate#create'
get 'activate/test_password_strength' => '/administrateurs/activate#test_password_strength'
get 'sign_in' => '/administrateurs/sessions#new' get 'sign_in' => '/administrateurs/sessions#new'
get 'procedures/archived' => 'procedures#archived' get 'procedures/archived' => 'procedures#archived'
get 'procedures/draft' => 'procedures#draft' get 'procedures/draft' => 'procedures#draft'

View file

@ -254,7 +254,7 @@ describe Gestionnaires::AvisController, type: :controller do
let(:dossier) { create(:dossier) } let(:dossier) { create(:dossier) }
let!(:avis) { create(:avis, email: invited_email, dossier: dossier) } let!(:avis) { create(:avis, email: invited_email, dossier: dossier) }
let(:avis_id) { avis.id } let(:avis_id) { avis.id }
let(:password) { '12345678' } let(:password) { 'démarches-simplifiées-pwd' }
let(:created_gestionnaire) { Gestionnaire.find_by(email: invited_email) } let(:created_gestionnaire) { Gestionnaire.find_by(email: invited_email) }
let(:invitations_email) { true } let(:invitations_email) { true }

View file

@ -7,8 +7,8 @@ describe Gestionnaires::PasswordsController, type: :controller do
describe "update" do describe "update" do
context "unified login" do context "unified login" do
let(:user) { create(:user, email: 'unique@plop.com', password: 'un super mot de passe') } let(:user) { create(:user, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
let(:administrateur) { create(:administrateur, email: 'unique@plop.com', password: 'un super mot de passe') } let(:administrateur) { create(:administrateur, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
let(:gestionnaire) { administrateur.gestionnaire } let(:gestionnaire) { administrateur.gestionnaire }
before do before do
@ -21,8 +21,8 @@ describe Gestionnaires::PasswordsController, type: :controller do
put :update, params: { put :update, params: {
gestionnaire: { gestionnaire: {
reset_password_token: @token, reset_password_token: @token,
password: "supersecret", password: "démarches-simplifiées-pwd",
password_confirmation: "supersecret" password_confirmation: "démarches-simplifiées-pwd"
} }
} }
expect(subject.current_gestionnaire).to eq(gestionnaire) expect(subject.current_gestionnaire).to eq(gestionnaire)
@ -33,8 +33,8 @@ describe Gestionnaires::PasswordsController, type: :controller do
put :update, params: { put :update, params: {
gestionnaire: { gestionnaire: {
reset_password_token: @token, reset_password_token: @token,
password: "supersecret", password: "démarches-simplifiées-pwd",
password_confirmation: "supersecret" password_confirmation: "démarches-simplifiées-pwd"
} }
} }
expect(subject.current_administrateur).to eq(administrateur) expect(subject.current_administrateur).to eq(administrateur)

View file

@ -3,7 +3,7 @@ describe Manager::AdministrateursController, type: :controller do
describe 'POST #create' do describe 'POST #create' do
let(:email) { 'plop@plop.com' } let(:email) { 'plop@plop.com' }
let(:password) { 'password' } let(:password) { 'démarches-simplifiées-pwd' }
before do before do
sign_in administration sign_in administration

View file

@ -1,6 +1,6 @@
describe Users::RegistrationsController, type: :controller do describe Users::RegistrationsController, type: :controller do
let(:email) { 'test@octo.com' } let(:email) { 'test@octo.com' }
let(:password) { 'password' } let(:password) { 'démarches-simplifiées-pwd' }
let(:user) { { email: email, password: password } } let(:user) { { email: email, password: password } }

View file

@ -1,6 +1,6 @@
describe Users::SessionsController, type: :controller do describe Users::SessionsController, type: :controller do
let(:email) { 'unique@plop.com' } let(:email) { 'unique@plop.com' }
let(:password) { 'un super mot de passe' } let(:password) { 'démarches-simplifiées-pwd' }
let(:loged_in_with_france_connect) { User.loged_in_with_france_connects.fetch(:particulier) } let(:loged_in_with_france_connect) { User.loged_in_with_france_connects.fetch(:particulier) }
let!(:user) { create(:user, email: email, password: password, loged_in_with_france_connect: loged_in_with_france_connect) } let!(:user) { create(:user, email: email, password: password, loged_in_with_france_connect: loged_in_with_france_connect) }
@ -104,8 +104,8 @@ describe Users::SessionsController, type: :controller do
end end
context "when associated gestionnaire" do context "when associated gestionnaire" do
let(:user) { create(:user, email: 'unique@plop.com', password: 'password') } let(:user) { create(:user, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
let(:gestionnaire) { create(:gestionnaire, email: 'unique@plop.com', password: 'password') } let(:gestionnaire) { create(:gestionnaire, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
it 'signs user out' do it 'signs user out' do
sign_in user sign_in user

View file

@ -2,6 +2,6 @@ FactoryBot.define do
sequence(:administration_email) { |n| "plop#{n}@plop.com" } sequence(:administration_email) { |n| "plop#{n}@plop.com" }
factory :administration do factory :administration do
email { generate(:administration_email) } email { generate(:administration_email) }
password { 'password' } password { 'démarches-simplifiées-pwd' }
end end
end end

View file

@ -2,6 +2,6 @@ FactoryBot.define do
sequence(:gestionnaire_email) { |n| "gest#{n}@gest.com" } sequence(:gestionnaire_email) { |n| "gest#{n}@gest.com" }
factory :gestionnaire do factory :gestionnaire do
email { generate(:gestionnaire_email) } email { generate(:gestionnaire_email) }
password { 'password' } password { 'démarches-simplifiées-pwd' }
end end
end end

View file

@ -2,7 +2,7 @@ FactoryBot.define do
sequence(:user_email) { |n| "user#{n}@user.com" } sequence(:user_email) { |n| "user#{n}@user.com" }
factory :user do factory :user do
email { generate(:user_email) } email { generate(:user_email) }
password { 'password' } password { 'démarches-simplifiées-pwd' }
confirmed_at { Time.zone.now } confirmed_at { Time.zone.now }
trait :unconfirmed do trait :unconfirmed do

View file

@ -3,7 +3,7 @@ require 'spec_helper'
feature 'The gestionnaire part' do feature 'The gestionnaire part' do
include ActiveJob::TestHelper include ActiveJob::TestHelper
let(:password) { 'secret_password' } let(:password) { 'démarches-simplifiées-pwd' }
let!(:gestionnaire) { create(:gestionnaire, password: password) } let!(:gestionnaire) { create(:gestionnaire, password: password) }
let!(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) } let!(:procedure) { create(:procedure, :published, gestionnaires: [gestionnaire]) }
@ -237,7 +237,7 @@ feature 'The gestionnaire part' do
def avis_sign_up(avis, email) def avis_sign_up(avis, email)
visit sign_up_gestionnaire_avis_path(avis, email) visit sign_up_gestionnaire_avis_path(avis, email)
fill_in 'gestionnaire_password', with: 'a good password' fill_in 'gestionnaire_password', with: 'démarches-simplifiées-pwd'
click_on 'Créer un compte' click_on 'Créer un compte'
expect(page).to have_current_path(gestionnaire_avis_index_path) expect(page).to have_current_path(gestionnaire_avis_index_path)
end end

View file

@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Signin in:' do feature 'Signin in:' do
let!(:user) { create(:user, password: password) } let!(:user) { create(:user, password: password) }
let(:password) { 'testpassword' } let(:password) { 'démarches-simplifiées-pwd' }
scenario 'an existing user can sign-in' do scenario 'an existing user can sign-in' do
visit root_path visit root_path

View file

@ -1,7 +1,7 @@
require 'rails_helper' require 'rails_helper'
feature 'The user' do feature 'The user' do
let(:password) { 'secret_password' } let(:password) { 'démarches-simplifiées-pwd' }
let!(:user) { create(:user, password: password) } let!(:user) { create(:user, password: password) }
let!(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs_mandatory) } let!(:procedure) { create(:procedure, :published, :for_individual, :with_all_champs_mandatory) }

View file

@ -27,7 +27,7 @@ feature 'Invitations' do
context 'when inviting someone without an existing account' do context 'when inviting someone without an existing account' do
let(:invite) { create(:invite, dossier: dossier, user: nil) } let(:invite) { create(:invite, dossier: dossier, user: nil) }
let(:user_password) { 'l33tus3r' } let(:user_password) { 'démarches-simplifiées-pwd' }
scenario 'an invited user can register using the registration link sent in the invitation email' do scenario 'an invited user can register using the registration link sent in the invitation email' do
# Click the invitation link # Click the invitation link

View file

@ -1,7 +1,7 @@
require 'spec_helper' require 'spec_helper'
feature 'linked dropdown lists' do feature 'linked dropdown lists' do
let(:password) { 'secret_password' } let(:password) { 'démarches-simplifiées-pwd' }
let!(:user) { create(:user, password: password) } let!(:user) { create(:user, password: password) }
let(:list_items) do let(:list_items) do

View file

@ -2,7 +2,7 @@ require 'spec_helper'
feature 'Signing up:' do feature 'Signing up:' do
let(:user_email) { generate :user_email } let(:user_email) { generate :user_email }
let(:user_password) { 'testpassword' } let(:user_password) { 'démarches-simplifiées-pwd' }
scenario 'a new user can sign-up' do scenario 'a new user can sign-up' do
visit root_path visit root_path

View file

@ -146,21 +146,21 @@ describe Gestionnaire, type: :model do
gestionnaire = create(:gestionnaire) gestionnaire = create(:gestionnaire)
user = create(:user, email: gestionnaire.email) user = create(:user, email: gestionnaire.email)
gestionnaire.update(email: 'whoami@plop.com', password: 'super secret') gestionnaire.update(email: 'whoami@plop.com', password: 'démarches-simplifiées-pwd')
user.reload user.reload
expect(user.email).to eq('whoami@plop.com') expect(user.email).to eq('whoami@plop.com')
expect(user.valid_password?('super secret')).to be(true) expect(user.valid_password?('démarches-simplifiées-pwd')).to be(true)
end end
it 'syncs credentials to associated administrateur' do it 'syncs credentials to associated administrateur' do
admin = create(:administrateur) admin = create(:administrateur)
gestionnaire = admin.gestionnaire gestionnaire = admin.gestionnaire
gestionnaire.update(password: 'super secret') gestionnaire.update(password: 'démarches-simplifiées-pwd')
admin.reload admin.reload
expect(admin.valid_password?('super secret')).to be(true) expect(admin.valid_password?('démarches-simplifiées-pwd')).to be(true)
end end
end end

View file

@ -3,12 +3,12 @@ require 'spec_helper'
describe User, type: :model do describe User, type: :model do
describe '#after_confirmation' do describe '#after_confirmation' do
let(:email) { 'mail@beta.gouv.fr' } let(:email) { 'mail@beta.gouv.fr' }
let!(:invite) { create(:invite, email: email) } let!(:invite) { create(:invite, email: email) }
let!(:invite2) { create(:invite, email: email) } let!(:invite2) { create(:invite, email: email) }
let(:user) do let(:user) do
create(:user, create(:user,
email: email, email: email,
password: 'a good password', password: 'démarches-simplifiées-pwd',
confirmation_token: '123', confirmation_token: '123',
confirmed_at: nil) confirmed_at: nil)
end end
@ -106,24 +106,24 @@ describe User, type: :model do
user = create(:user) user = create(:user)
gestionnaire = create(:gestionnaire, email: user.email) gestionnaire = create(:gestionnaire, email: user.email)
user.update(email: 'whoami@plop.com', password: 'super secret') user.update(email: 'whoami@plop.com', password: 'démarches-simplifiées2')
user.confirm user.confirm
gestionnaire.reload gestionnaire.reload
expect(gestionnaire.email).to eq('whoami@plop.com') expect(gestionnaire.email).to eq('whoami@plop.com')
expect(gestionnaire.valid_password?('super secret')).to be(true) expect(gestionnaire.valid_password?('démarches-simplifiées2')).to be(true)
end end
it 'syncs credentials to associated administrateur' do it 'syncs credentials to associated administrateur' do
user = create(:user) user = create(:user)
admin = create(:administrateur, email: user.email) admin = create(:administrateur, email: user.email)
user.update(email: 'whoami@plop.com', password: 'super secret') user.update(email: 'whoami@plop.com', password: 'démarches-simplifiées2')
user.confirm user.confirm
admin.reload admin.reload
expect(admin.email).to eq('whoami@plop.com') expect(admin.email).to eq('whoami@plop.com')
expect(admin.valid_password?('super secret')).to be(true) expect(admin.valid_password?('démarches-simplifiées2')).to be(true)
end end
end end
end end

View file

@ -35,7 +35,7 @@ module FeatureHelpers
end end
end end
def sign_up_with(email, password = 'testpassword') def sign_up_with(email, password = 'démarches-simplifiées-pwd')
fill_in :user_email, with: email fill_in :user_email, with: email
fill_in :user_password, with: password fill_in :user_password, with: password