Merge pull request #4199 from betagouv/dev

2019-08-13-01
This commit is contained in:
LeSim 2019-08-13 17:46:00 +02:00 committed by GitHub
commit e07a5c3efc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 332 additions and 637 deletions

View file

@ -68,7 +68,7 @@ En local, un utilisateur de test est créé automatiquement, avec les identifian
FindDubiousProceduresJob.set(cron: "0 0 * * *").perform_later FindDubiousProceduresJob.set(cron: "0 0 * * *").perform_later
Administrateurs::ActivateBeforeExpirationJob.set(cron: "0 8 * * *").perform_later Administrateurs::ActivateBeforeExpirationJob.set(cron: "0 8 * * *").perform_later
WarnExpiringDossiersJob.set(cron: "0 0 1 * *").perform_later WarnExpiringDossiersJob.set(cron: "0 0 1 * *").perform_later
GestionnaireEmailNotificationJob.set(cron: "0 10 * * 1,2,3,4,5,6").perform_later InstructeurEmailNotificationJob.set(cron: "0 10 * * 1,2,3,4,5,6").perform_later
PurgeUnattachedBlobsJob.set(cron: "0 0 * * *").perform_later PurgeUnattachedBlobsJob.set(cron: "0 0 * * *").perform_later
OperationsSignatureJob.set(cron: "0 6 * * *").perform_later OperationsSignatureJob.set(cron: "0 6 * * *").perform_later

View file

@ -19,7 +19,7 @@
margin-bottom: 2 * $default-padding; margin-bottom: 2 * $default-padding;
} }
.mixed-buttons-bar { .header-actions {
flex-shrink: 0; flex-shrink: 0;
.button { .button {

View file

@ -37,26 +37,24 @@ class Admin::InstructeursController < AdminController
private private
def invite_instructeur(email) def invite_instructeur(email)
password = SecureRandom.hex user = User.find_by(email: email)
@instructeur = Instructeur.create( if user.nil?
email: email, user = User.create(
password: password, email: email,
password_confirmation: password, password: SecureRandom.hex,
administrateurs: [current_administrateur] confirmed_at: Time.zone.now
) )
end
if @instructeur.errors.messages.empty? if user.errors.empty?
@instructeur.invite! @instructeur = Instructeur.create(email: email, administrateurs: [current_administrateur])
user.update!(instructeur: @instructeur)
if User.exists?(email: @instructeur.email) user.invite!
InstructeurMailer.user_to_instructeur(@instructeur.email).deliver_later
else
User.create(email: email, password: password, confirmed_at: Time.zone.now)
end
flash.notice = 'Instructeur ajouté' flash.notice = 'Instructeur ajouté'
else else
flash.alert = @instructeur.errors.full_messages flash.alert = user.errors.full_messages
end end
end end

View file

@ -76,10 +76,7 @@ class Admin::ProceduresController < AdminController
render 'new' render 'new'
else else
flash.notice = 'Démarche enregistrée.' flash.notice = 'Démarche enregistrée.'
instructeur = Instructeur.find_by(email: current_administrateur.email) current_administrateur.instructeur.assign_to_procedure(@procedure)
if instructeur
instructeur.assign_to_procedure(@procedure)
end
redirect_to champs_procedure_path(@procedure) redirect_to champs_procedure_path(@procedure)
end end

View file

@ -24,7 +24,6 @@ class Administrateurs::ActivateController < ApplicationController
if administrateur && administrateur.errors.empty? if administrateur && administrateur.errors.empty?
sign_in(administrateur, scope: :administrateur) sign_in(administrateur, scope: :administrateur)
try_to_authenticate(User, administrateur.email, password) try_to_authenticate(User, administrateur.email, password)
try_to_authenticate(Instructeur, administrateur.email, password)
flash.notice = "Mot de passe enregistré" flash.notice = "Mot de passe enregistré"
redirect_to admin_procedures_path redirect_to admin_procedures_path
else else

View file

@ -1,59 +1,4 @@
class Administrateurs::PasswordsController < Devise::PasswordsController class Administrateurs::PasswordsController < ApplicationController
after_action :try_to_authenticate_user, only: [:update]
after_action :try_to_authenticate_instructeur, 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_instructeur
if administrateur_signed_in?
instructeur = Instructeur.find_by(email: current_administrateur.email)
if instructeur
sign_in instructeur
end
end
end
def test_strength def test_strength
@score, @words, @length = ZxcvbnService.new(password_params[:password]).complexity @score, @words, @length = ZxcvbnService.new(password_params[:password]).complexity
@min_length = PASSWORD_MIN_LENGTH @min_length = PASSWORD_MIN_LENGTH
@ -64,6 +9,6 @@ class Administrateurs::PasswordsController < Devise::PasswordsController
private private
def password_params def password_params
params.require(:administrateur).permit(:reset_password_token, :password) params.require(:administrateur).permit(:password)
end end
end end

View file

@ -1,13 +0,0 @@
class Administrateurs::SessionsController < Sessions::SessionsController
def new
redirect_to new_user_session_path
end
def create
super
end
def after_sign_in_path_for(resource)
admin_procedures_path
end
end

View file

@ -18,6 +18,8 @@ class ApplicationController < ActionController::Base
before_action :set_active_storage_host before_action :set_active_storage_host
before_action :setup_tracking before_action :setup_tracking
helper_method :logged_in?, :multiple_devise_profile_connect?, :instructeur_signed_in?, :current_instructeur
def staging_authenticate def staging_authenticate
if StagingAuthService.enabled? && !authenticate_with_http_basic { |username, password| StagingAuthService.authenticate(username, password) } if StagingAuthService.enabled? && !authenticate_with_http_basic { |username, password| StagingAuthService.authenticate(username, password) }
request_http_basic_authentication request_http_basic_authentication
@ -42,7 +44,11 @@ class ApplicationController < ActionController::Base
logged_user.present? logged_user.present?
end end
helper_method :logged_in? def multiple_devise_profile_connect?
user_signed_in? && instructeur_signed_in? ||
instructeur_signed_in? && administrateur_signed_in? ||
user_signed_in? && administrateur_signed_in?
end
def pundit_user def pundit_user
{ {
@ -52,6 +58,14 @@ class ApplicationController < ActionController::Base
}.compact }.compact
end end
def current_instructeur
current_user&.instructeur
end
def instructeur_signed_in?
user_signed_in? && current_user&.instructeur.present?
end
protected protected
def authenticate_logged_user! def authenticate_logged_user!
@ -65,9 +79,7 @@ class ApplicationController < ActionController::Base
end end
def authenticate_instructeur! def authenticate_instructeur!
if instructeur_signed_in? if !instructeur_signed_in?
super
else
redirect_to new_user_session_path redirect_to new_user_session_path
end end
end end

View file

@ -1,50 +0,0 @@
class Instructeurs::ActivateController < ApplicationController
include TrustedDeviceConcern
def new
@instructeur = Instructeur.with_reset_password_token(params[:token])
if @instructeur
# the instructeur activates its account from an email
trust_device(Time.zone.now)
else
flash.alert = "Le lien de validation du compte instructeur a expiré, #{helpers.contact_link('contactez-nous', tags: 'lien expiré')} pour obtenir un nouveau lien."
redirect_to root_path
end
end
def create
password = create_instructeur_params[:password]
instructeur = Instructeur.reset_password_by_token({
password: password,
password_confirmation: password,
reset_password_token: create_instructeur_params[:reset_password_token]
})
if instructeur && instructeur.errors.empty?
sign_in(instructeur, scope: :instructeur)
try_to_authenticate(User, instructeur.email, password)
try_to_authenticate(Administrateur, instructeur.email, password)
flash.notice = "Mot de passe enregistré"
redirect_to instructeur_procedures_path
else
flash.alert = instructeur.errors.full_messages
redirect_to instructeur_activate_path(token: create_instructeur_params[:reset_password_token])
end
end
private
def create_instructeur_params
params.require(:instructeur).permit(:reset_password_token, :password)
end
def try_to_authenticate(klass, email, password)
resource = klass.find_for_database_authentication(email: email)
if resource&.valid_password?(password)
sign_in resource
resource.force_sync_credentials
end
end
end

View file

@ -83,21 +83,26 @@ module Instructeurs
email = params[:email] email = params[:email]
password = params['instructeur']['password'] password = params['instructeur']['password']
instructeur = Instructeur.new(email: email, password: password) user = User.find_by(email: email)
if instructeur.save if user.nil?
user = User.find_by(email: email) user = User.create(
if user.blank? email: email,
user = User.create(email: email, password: password, confirmed_at: Time.zone.now) password: password,
end confirmed_at: Time.zone.now
)
end
if user.errors.empty?
instructeur = Instructeur.create(email: email)
user.update!(instructeur: instructeur)
sign_in(user) sign_in(user)
sign_in(instructeur, scope: :instructeur)
Avis.link_avis_to_instructeur(instructeur) Avis.link_avis_to_instructeur(instructeur)
redirect_to url_for(instructeur_avis_index_path) redirect_to url_for(instructeur_avis_index_path)
else else
flash[:alert] = instructeur.errors.full_messages flash[:alert] = user.errors.full_messages
redirect_to url_for(sign_up_instructeur_avis_path(params[:id], email)) redirect_to url_for(sign_up_instructeur_avis_path(params[:id], email))
end end
end end
@ -119,7 +124,7 @@ module Instructeurs
elsif avis.instructeur&.email == params[:email] elsif avis.instructeur&.email == params[:email]
# the avis instructeur has already signed up and it sould sign in # the avis instructeur has already signed up and it sould sign in
redirect_to new_instructeur_session_url redirect_to new_user_session_url
end end
end end

View file

@ -1,55 +0,0 @@
class Instructeurs::PasswordsController < Devise::PasswordsController
after_action :try_to_authenticate_user, only: [:update]
after_action :try_to_authenticate_administrateur, 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
# 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 instructeur_signed_in?
user = User.find_by(email: current_instructeur.email)
if user
sign_in user
end
end
end
def try_to_authenticate_administrateur
if instructeur_signed_in?
administrateur = Administrateur.find_by(email: current_instructeur.email)
if administrateur
sign_in administrateur
end
end
end
end

View file

@ -1,9 +0,0 @@
class Instructeurs::SessionsController < Sessions::SessionsController
def new
redirect_to new_user_session_path
end
def create
super
end
end

View file

@ -0,0 +1,49 @@
class Users::ActivateController < ApplicationController
include TrustedDeviceConcern
def new
@user = User.with_reset_password_token(params[:token])
if @user
# the user activates its account from an email
trust_device(Time.zone.now)
else
flash.alert = "Le lien de validation du compte instructeur a expiré, #{helpers.contact_link('contactez-nous', tags: 'lien expiré')} pour obtenir un nouveau lien."
redirect_to root_path
end
end
def create
password = create_user_params[:password]
user = User.reset_password_by_token({
password: password,
password_confirmation: password,
reset_password_token: create_user_params[:reset_password_token]
})
if user && user.errors.empty?
sign_in(user, scope: :user)
try_to_authenticate(Administrateur, user.email, password)
flash.notice = "Mot de passe enregistré"
redirect_to instructeur_procedures_path
else
flash.alert = user.errors.full_messages
redirect_to users_activate_path(token: create_user_params[:reset_password_token])
end
end
private
def create_user_params
params.require(:user).permit(:reset_password_token, :password)
end
def try_to_authenticate(klass, email, password)
resource = klass.find_for_database_authentication(email: email)
if resource&.valid_password?(password)
sign_in resource
resource.force_sync_credentials
end
end
end

View file

@ -49,7 +49,7 @@ class Users::PasswordsController < Devise::PasswordsController
instructeur = Instructeur.find_by(email: current_user.email) instructeur = Instructeur.find_by(email: current_user.email)
if instructeur if instructeur
sign_in instructeur sign_in(instructeur.user)
end end
end end
end end

View file

@ -17,7 +17,6 @@ class Users::SessionsController < Sessions::SessionsController
remember_me = params[:user][:remember_me] == '1' remember_me = params[:user][:remember_me] == '1'
if resource_locked?(try_to_authenticate(User, remember_me)) || if resource_locked?(try_to_authenticate(User, remember_me)) ||
resource_locked?(try_to_authenticate(Instructeur, remember_me)) ||
resource_locked?(try_to_authenticate(Administrateur, remember_me)) resource_locked?(try_to_authenticate(Administrateur, remember_me))
flash.alert = 'Votre compte est verrouillé.' flash.alert = 'Votre compte est verrouillé.'
new new

View file

@ -2,16 +2,6 @@
class InstructeurMailer < ApplicationMailer class InstructeurMailer < ApplicationMailer
layout 'mailers/layout' layout 'mailers/layout'
def invite_instructeur(instructeur, reset_password_token)
@reset_password_token = reset_password_token
@instructeur = instructeur
subject = "Activez votre compte instructeur"
mail(to: instructeur.email,
subject: subject,
reply_to: CONTACT_EMAIL)
end
def user_to_instructeur(email) def user_to_instructeur(email)
@email = email @email = email
subject = "Vous avez été nommé instructeur" subject = "Vous avez été nommé instructeur"

View file

@ -16,4 +16,14 @@ class UserMailer < ApplicationMailer
mail(to: requested_email, subject: @subject) mail(to: requested_email, subject: @subject)
end end
def invite_instructeur(user, reset_password_token)
@reset_password_token = reset_password_token
@user = user
subject = "Activez votre compte instructeur"
mail(to: user.email,
subject: subject,
reply_to: CONTACT_EMAIL)
end
end end

View file

@ -20,7 +20,7 @@ class Administration < ApplicationRecord
AdministrationMailer.new_admin_email(administrateur, self).deliver_later AdministrationMailer.new_admin_email(administrateur, self).deliver_later
administrateur.invite!(id) administrateur.invite!(id)
User.create({ user = User.create({
email: email, email: email,
password: password, password: password,
confirmed_at: Time.zone.now confirmed_at: Time.zone.now
@ -28,7 +28,7 @@ class Administration < ApplicationRecord
Instructeur.create({ Instructeur.create({
email: email, email: email,
password: password user: user
}) })
end end

View file

@ -2,9 +2,6 @@ class Instructeur < ApplicationRecord
include CredentialsSyncableConcern include CredentialsSyncableConcern
include EmailSanitizableConcern include EmailSanitizableConcern
devise :database_authenticatable, :registerable, :async,
:recoverable, :rememberable, :trackable, :validatable, :lockable
has_and_belongs_to_many :administrateurs has_and_belongs_to_many :administrateurs
before_validation -> { sanitize_email(:email) } before_validation -> { sanitize_email(:email) }
@ -24,6 +21,8 @@ class Instructeur < ApplicationRecord
has_many :dossiers_from_avis, through: :avis, source: :dossier has_many :dossiers_from_avis, through: :avis, source: :dossier
has_many :trusted_device_tokens has_many :trusted_device_tokens
has_one :user
def visible_procedures def visible_procedures
procedures.merge(Procedure.avec_lien.or(Procedure.archivees)) procedures.merge(Procedure.avec_lien.or(Procedure.archivees))
end end
@ -178,12 +177,6 @@ class Instructeur < ApplicationRecord
Follow.where(instructeur: self, dossier: dossier).update_all(attributes) Follow.where(instructeur: self, dossier: dossier).update_all(attributes)
end end
def invite!
reset_password_token = set_reset_password_token
InstructeurMailer.invite_instructeur(self, reset_password_token).deliver_later
end
def feature_enabled?(feature) def feature_enabled?(feature)
Flipflop.feature_set.feature(feature) Flipflop.feature_set.feature(feature)
features[feature.to_s] features[feature.to_s]

View file

@ -17,6 +17,7 @@ class User < ApplicationRecord
has_many :dossiers_invites, through: :invites, source: :dossier has_many :dossiers_invites, through: :invites, source: :dossier
has_many :feedbacks, dependent: :destroy has_many :feedbacks, dependent: :destroy
has_one :france_connect_information, dependent: :destroy has_one :france_connect_information, dependent: :destroy
belongs_to :instructeur
accepts_nested_attributes_for :france_connect_information accepts_nested_attributes_for :france_connect_information
@ -39,6 +40,10 @@ class User < ApplicationRecord
owns?(dossier) || invite?(dossier.id) owns?(dossier) || invite?(dossier.id)
end end
def invite!
UserMailer.invite_instructeur(self, set_reset_password_token).deliver_later
end
private private
def link_invites! def link_invites!

View file

@ -1,25 +0,0 @@
class SwitchDeviseProfileService
def initialize(warden)
@warden = warden
end
def multiple_devise_profile_connect?
user_signed_in? && instructeur_signed_in? ||
instructeur_signed_in? && administrateur_signed_in? ||
user_signed_in? && administrateur_signed_in?
end
private
def user_signed_in?
@warden.authenticate(:scope => :user).present?
end
def instructeur_signed_in?
@warden.authenticate(:scope => :instructeur).present?
end
def administrateur_signed_in?
@warden.authenticate(:scope => :administrateur).present?
end
end

View file

@ -6,8 +6,9 @@
», créer un compte, et référencer là démarche que vous venez de publier. », créer un compte, et référencer là démarche que vous venez de publier.
%p %p
Dès que vous avez effectué le référencement de celle-ci, vous pouvez suivre le guide dintégration du bouton que vous trouverez à Vous pouvez
%a{ :href => "https://monavis.numerique.gouv.fr/Aide/Int%C3%A9gration%20du%20bouton%20MonAvis" } ladresse suivante. %a{ :href => "https://doc.demarches-simplifiees.fr/tutoriels/integration-du-bouton-mon-avis" } consulter notre tutoriel complet
pour intégrer le bouton « MonAvis » sur demarches-simplifiees.fr.
%p Une fois en possession du code généré sur le site MonAvis, vous pouvez le coller dans le champ ci-dessous : %p Une fois en possession du code généré sur le site MonAvis, vous pouvez le coller dans le champ ci-dessous :

View file

@ -9,30 +9,8 @@
= dossier.procedure.libelle.truncate_words(10) = dossier.procedure.libelle.truncate_words(10)
%li %li
= "Dossier nº #{dossier.id}" = "Dossier nº #{dossier.id}"
.mixed-buttons-bar .header-actions
%span.dropdown.print-menu-opener = render partial: 'instructeurs/dossiers/header_actions', locals: { dossier: dossier }
%button.button.dropdown-button.icon-only
%span.icon.printer
%ul.print-menu.dropdown-content
%li
= link_to "Tout le dossier", print_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
%li
= link_to "Uniquement cet onglet", "#", onclick: "window.print()", class: "menu-item menu-link"
- if Flipflop.download_as_zip_enabled? && !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty?
%span.dropdown.print-menu-opener
%button.button.dropdown-button.icon-only
%span.icon.attachment
%ul.print-menu.dropdown-content
%li
- if PiecesJustificativesService.pieces_justificatives_total_size(dossier) < Dossier::TAILLE_MAX_ZIP
= link_to "Télécharger toutes les pièces jointes", telecharger_pjs_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
- else
%p.menu-item Le téléchargement des pièces jointes est désactivé pour les dossiers de plus de #{number_to_human_size Dossier::TAILLE_MAX_ZIP}.
= render partial: "instructeurs/procedures/dossier_actions", locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: current_instructeur&.follow?(dossier) }
%span.state-button
= render partial: "state_button", locals: { dossier: dossier }
%ul.tabs %ul.tabs
- notifications_summary = current_instructeur.notifications_for_dossier(dossier) - notifications_summary = current_instructeur.notifications_for_dossier(dossier)

View file

@ -0,0 +1,24 @@
%span.dropdown.print-menu-opener
%button.button.dropdown-button.icon-only
%span.icon.printer
%ul.print-menu.dropdown-content
%li
= link_to "Tout le dossier", print_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
%li
= link_to "Uniquement cet onglet", "#", onclick: "window.print()", class: "menu-item menu-link"
- if Flipflop.download_as_zip_enabled? && !PiecesJustificativesService.liste_pieces_justificatives(dossier).empty?
%span.dropdown.print-menu-opener
%button.button.dropdown-button.icon-only
%span.icon.attachment
%ul.print-menu.dropdown-content
%li
- if PiecesJustificativesService.pieces_justificatives_total_size(dossier) < Dossier::TAILLE_MAX_ZIP
= link_to "Télécharger toutes les pièces jointes", telecharger_pjs_instructeur_dossier_path(dossier.procedure, dossier), target: "_blank", rel: "noopener", class: "menu-item menu-link"
- else
%p.menu-item Le téléchargement des pièces jointes est désactivé pour les dossiers de plus de #{number_to_human_size Dossier::TAILLE_MAX_ZIP}.
= render partial: "instructeurs/procedures/dossier_actions", locals: { procedure: dossier.procedure, dossier: dossier, dossier_is_followed: current_instructeur&.follow?(dossier) }
%span.state-button
= render partial: "state_button", locals: { dossier: dossier }

View file

@ -1,5 +1,5 @@
<%= render_flash %> <%= render_flash %>
<%= render_to_element('.state-button', partial: "state_button", locals: { dossier: dossier }) %> <%= render_to_element('.header-actions', partial: 'header_actions', locals: { dossier: dossier }) %>
<% attachment = dossier.justificatif_motivation.attachment %> <% attachment = dossier.justificatif_motivation.attachment %>
<% if attachment && attachment.virus_scanner.pending? %> <% if attachment && attachment.virus_scanner.pending? %>

View file

@ -10,7 +10,7 @@
= link_to manager_root_path, class: "menu-item menu-link" do = link_to manager_root_path, class: "menu-item menu-link" do
= image_tag "icons/super-admin.svg" = image_tag "icons/super-admin.svg"
Passer en super-admin Passer en super-admin
- if SwitchDeviseProfileService.new(warden).multiple_devise_profile_connect? - if multiple_devise_profile_connect?
- if user_signed_in? && nav_bar_profile != :user - if user_signed_in? && nav_bar_profile != :user
%li %li
= link_to dossiers_path, class: "menu-item menu-link" do = link_to dossiers_path, class: "menu-item menu-link" do

View file

@ -9,8 +9,9 @@
.col-xs-10.no-padding .col-xs-10.no-padding
#navbar-body #navbar-body
.row .row
%div{ style: "vertical-align: middle;float:left;position:absolute;line-height: 60px;z-index:2;" } %div{ style: "max-width: 50%; position: absolute; display: flex; flex-direction: column; justify-content: center; height: 60px; line-height: 20px; z-index: 2;" }
👉Besoin d'aide? Contactez-nous par <a style="cursor:hand" onclick="$crisp.push(['do', 'chat:open'])">chat</a>, <a href="#{contact_admin_path}" target="_blank" rel="noopener">email</a> ou <a target="_blank" rel="noopener" href="https://calendly.com/demarches-simplifiees/accompagnement-administrateur-demarches-simplifiees-fr">prenez RDV</a> %span
👉 Besoin daide ? Consultez <a target="_blank" rel="noopener" href="https://doc.demarches-simplifiees.fr/">la documentation</a> et <a target="_blank" rel="noopener" href="https://faq.demarches-simplifiees.fr/">laide</a>, contactez-nous par <a style="cursor:hand" onclick="$crisp.push(['do', 'chat:open'])">chat</a> ou <a href="#{contact_admin_path}" target="_blank" rel="noopener">email</a>, ou <a target="_blank" rel="noopener" href="https://calendly.com/demarches-simplifiees/accompagnement-administrateur-demarches-simplifiees-fr">prenez RDV</a>.
-# BEST WTF EVER -# BEST WTF EVER
-# this begin rescue hides potentials bugs by displaying another navbar -# this begin rescue hides potentials bugs by displaying another navbar
- begin - begin

View file

@ -1,4 +1,4 @@
- if SwitchDeviseProfileService.new(warden).multiple_devise_profile_connect? - if multiple_devise_profile_connect?
%ul#switch-menu %ul#switch-menu
%li %li
Changer de rôle Changer de rôle

View file

@ -7,8 +7,8 @@
Vous venez d'être nommé instructeur sur demarches-simplifiees.fr. Vous venez d'être nommé instructeur sur demarches-simplifiees.fr.
%p %p
Votre compte a été créé pour l'adresse email #{@instructeur.email}. Pour lactiver, je vous invite à cliquer sur le lien suivant :  Votre compte a été créé pour l'adresse email #{@user.email}. Pour lactiver, je vous invite à cliquer sur le lien suivant : 
= link_to(instructeur_activate_url(token: @reset_password_token), instructeur_activate_url(token: @reset_password_token)) = link_to(users_activate_url(token: @reset_password_token), users_activate_url(token: @reset_password_token))
%p %p
Par ailleurs, nous vous invitons à prendre quelques minutes pour consulter notre tutoriel à destination des nouveaux instructeurs : Par ailleurs, nous vous invitons à prendre quelques minutes pour consulter notre tutoriel à destination des nouveaux instructeurs :

View file

@ -1,7 +1,7 @@
.container .container
= form_for @instructeur, url: { controller: 'instructeurs/activate', action: :create }, html: { class: "form" } do |f| = form_for @user, url: { controller: 'users/activate', action: :create }, html: { class: "form" } do |f|
%br %br
%h1= @instructeur.email %h1= @user.email
= f.password_field :password, placeholder: 'Mot de passe' = f.password_field :password, placeholder: 'Mot de passe'
= f.hidden_field :reset_password_token, value: params[:token] = f.hidden_field :reset_password_token, value: params[:token]
= f.submit 'Définir le mot de passe', class: 'button large primary expand' = f.submit 'Définir le mot de passe', class: 'button large primary expand'

View file

@ -2,7 +2,7 @@
# valid values are from 0 to 4, 0 means very simple, 4 means high level of complexity. # valid values are from 0 to 4, 0 means very simple, 4 means high level of complexity.
if !defined?(PASSWORD_MIN_LENGTH) if !defined?(PASSWORD_MIN_LENGTH)
# PASSWORD_COMPLEXITY_FOR_USER = ENV.fetch('PASSWORD_COMPLEXITY_FOR_USER', '2').to_i # PASSWORD_COMPLEXITY_FOR_USER = ENV.fetch('PASSWORD_COMPLEXITY_FOR_USER', '2').to_i
# PASSWORD_COMPLEXITY_FOR_GESTIONNAIRE = ENV.fetch('PASSWORD_COMPLEXITY_FOR_GESTIONNAIRE', '3').to_i # PASSWORD_COMPLEXITY_FOR_INSTRUCTEUR = ENV.fetch('PASSWORD_COMPLEXITY_FOR_INSTRUCTEUR', '3').to_i
PASSWORD_COMPLEXITY_FOR_ADMIN = ENV.fetch('PASSWORD_COMPLEXITY_FOR_ADMIN', '4').to_i PASSWORD_COMPLEXITY_FOR_ADMIN = ENV.fetch('PASSWORD_COMPLEXITY_FOR_ADMIN', '4').to_i
# password minimum length # password minimum length
PASSWORD_MIN_LENGTH = ENV.fetch('PASSWORD_MIN_LENGTH', '8').to_i PASSWORD_MIN_LENGTH = ENV.fetch('PASSWORD_MIN_LENGTH', '8').to_i

View file

@ -77,15 +77,7 @@ Rails.application.routes.draw do
omniauth_callbacks: 'administrations/omniauth_callbacks' omniauth_callbacks: 'administrations/omniauth_callbacks'
} }
devise_for :administrateurs, controllers: { devise_for :administrateurs, skip: :all
sessions: 'administrateurs/sessions',
passwords: 'administrateurs/passwords'
}, skip: [:registrations]
devise_for :instructeurs, controllers: {
sessions: 'instructeurs/sessions',
passwords: 'instructeurs/passwords'
}, skip: [:registrations]
devise_for :users, controllers: { devise_for :users, controllers: {
sessions: 'users/sessions', sessions: 'users/sessions',
@ -95,20 +87,12 @@ Rails.application.routes.draw do
} }
devise_scope :user do devise_scope :user do
get '/users/sign_in/demo' => redirect("/users/sign_in")
get '/users/no_procedure' => 'users/sessions#no_procedure' get '/users/no_procedure' => 'users/sessions#no_procedure'
get 'connexion-par-jeton/:id' => 'users/sessions#sign_in_by_link', as: 'sign_in_by_link' get 'connexion-par-jeton/:id' => 'users/sessions#sign_in_by_link', as: 'sign_in_by_link'
get 'lien-envoye/:email' => 'users/sessions#link_sent', constraints: { email: /.*/ }, as: 'link_sent' get 'lien-envoye/:email' => 'users/sessions#link_sent', constraints: { email: /.*/ }, as: 'link_sent'
end end
devise_scope :instructeur do
get '/instructeurs/sign_in/demo' => redirect("/users/sign_in")
get '/instructeurs/edit' => 'instructeurs/registrations#edit', :as => 'edit_instructeurs_registration'
put '/instructeurs' => 'instructeurs/registrations#update', :as => 'instructeurs_registration'
end
devise_scope :administrateur do devise_scope :administrateur do
get '/administrateurs/sign_in/demo' => redirect("/users/sign_in")
get '/administrateurs/password/test_strength' => 'administrateurs/passwords#test_strength' get '/administrateurs/password/test_strength' => 'administrateurs/passwords#test_strength'
end end
@ -168,17 +152,14 @@ Rails.application.routes.draw do
get 'dossiers', to: redirect('/dossiers') get 'dossiers', to: redirect('/dossiers')
get 'dossiers/:id/recapitulatif', to: redirect('/dossiers/%{id}') get 'dossiers/:id/recapitulatif', to: redirect('/dossiers/%{id}')
get 'dossiers/invites/:id', to: redirect(path: '/invites/%{id}') get 'dossiers/invites/:id', to: redirect(path: '/invites/%{id}')
end
namespace :instructeur do get 'activate' => '/users/activate#new'
get 'activate' => '/instructeurs/activate#new' patch 'activate' => '/users/activate#create'
patch 'activate' => '/instructeurs/activate#create'
end end
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 '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'
get 'procedures/path_list' => 'procedures#path_list' get 'procedures/path_list' => 'procedures#path_list'
@ -297,7 +278,7 @@ Rails.application.routes.draw do
end end
# #
# Gestionnaire # Instructeur
# #
scope module: 'instructeurs', as: 'instructeur' do scope module: 'instructeurs', as: 'instructeur' do

View file

@ -0,0 +1,6 @@
class LinkUserAndInstructeur < ActiveRecord::Migration[5.2]
def change
add_reference :users, :instructeur, index: true
add_foreign_key :users, :instructeurs
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_08_05_140346) do ActiveRecord::Schema.define(version: 2019_08_08_144607) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -596,8 +596,10 @@ ActiveRecord::Schema.define(version: 2019_08_05_140346) do
t.string "unlock_token" t.string "unlock_token"
t.datetime "locked_at" t.datetime "locked_at"
t.text "unconfirmed_email" t.text "unconfirmed_email"
t.bigint "instructeur_id"
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["instructeur_id"], name: "index_users_on_instructeur_id"
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true
end end
@ -642,5 +644,6 @@ ActiveRecord::Schema.define(version: 2019_08_05_140346) do
add_foreign_key "services", "administrateurs" add_foreign_key "services", "administrateurs"
add_foreign_key "trusted_device_tokens", "instructeurs" add_foreign_key "trusted_device_tokens", "instructeurs"
add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id" add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id"
add_foreign_key "users", "instructeurs"
add_foreign_key "without_continuation_mails", "procedures" add_foreign_key "without_continuation_mails", "procedures"
end end

View file

@ -11,5 +11,5 @@ default_password = "this is a very complicated password !"
puts "Create test user '#{default_user}'" puts "Create test user '#{default_user}'"
Administration.create!(email: default_user, password: default_password) Administration.create!(email: default_user, password: default_password)
Administrateur.create!(email: default_user, password: default_password) Administrateur.create!(email: default_user, password: default_password)
Gestionnaire.create!(email: default_user, password: default_password) Instructeur.create!(email: default_user, password: default_password)
User.create!(email: default_user, password: default_password, confirmed_at: Time.zone.now) User.create!(email: default_user, password: default_password, confirmed_at: Time.zone.now)

View file

@ -0,0 +1,10 @@
namespace :after_party do
desc 'Deployment task: populate_user_instructeur_ids'
task populate_user_instructeur_ids: :environment do
Instructeur.find_each do |instructeur|
User.where(email: instructeur.email).update(instructeur_id: instructeur.id)
end
AfterParty::TaskRecord.create version: '20190808145006'
end
end

View file

@ -1,7 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<title>The page you were looking for doesn't exist (404)</title> <title>La page que vous cherchez nexiste pas (erreur 404)</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<style> <style>
body { body {
@ -58,10 +59,9 @@
<!-- This file lives in public/404.html --> <!-- This file lives in public/404.html -->
<div class="dialog"> <div class="dialog">
<div> <div>
<h1>The page you were looking for doesn't exist.</h1> <h1>La page que vous cherchez nexiste pas (erreur 404).</h1>
<p>You may have mistyped the address or the page may have moved.</p> <p>La page que vous cherchez a sans doute changé dadresse, ou vous navez pas les droits nécessaires pour y accéder.</p>
</div> </div>
<p>If you are the application owner check the logs for more information.</p>
</div> </div>
</body> </body>
</html> </html>

View file

@ -149,30 +149,10 @@ describe Admin::InstructeursController, type: :controller do
context 'Email notification' do context 'Email notification' do
it 'Notification email is sent when instructeur is create' do it 'Notification email is sent when instructeur is create' do
expect_any_instance_of(Instructeur).to receive(:invite!) expect_any_instance_of(User).to receive(:invite!)
subject subject
end end
end end
context 'unified login' do
before do
subject
end
it "creates associated user with same credentials" do
instructeur = controller.instance_variable_get(:@instructeur)
user = User.find_by(email: instructeur.email)
expect(user.valid_password?(instructeur.password)).to be(true)
end
context 'invalid email' do
let(:email) { 'fail' }
it "won't create associated user" do
expect(User.where(email: email).exists?).to be(false)
end
end
end
end end
describe 'DELETE #destroy' do describe 'DELETE #destroy' do

View file

@ -1,11 +0,0 @@
require 'spec_helper'
describe Administrateurs::SessionsController, type: :controller do
before do
@request.env["devise.mapping"] = Devise.mappings[:administrateur]
end
describe '#create' do
it { expect(described_class).to be < Sessions::SessionsController }
end
end

View file

@ -11,7 +11,7 @@ describe Instructeurs::AvisController, type: :controller do
let!(:avis_without_answer) { Avis.create(dossier: dossier, claimant: claimant, instructeur: instructeur) } let!(:avis_without_answer) { Avis.create(dossier: dossier, claimant: claimant, instructeur: instructeur) }
let!(:avis_with_answer) { Avis.create(dossier: dossier, claimant: claimant, instructeur: instructeur, answer: 'yop') } let!(:avis_with_answer) { Avis.create(dossier: dossier, claimant: claimant, instructeur: instructeur, answer: 'yop') }
before { sign_in(instructeur) } before { sign_in(instructeur.user) }
describe '#index' do describe '#index' do
before { get :index } before { get :index }
@ -217,7 +217,7 @@ describe Instructeurs::AvisController, type: :controller do
context 'when the instructeur is authenticated' do context 'when the instructeur is authenticated' do
before do before do
sign_in instructeur sign_in(instructeur.user)
get :sign_up, params: { id: avis.id, email: invited_email } get :sign_up, params: { id: avis.id, email: invited_email }
end end
@ -229,7 +229,7 @@ describe Instructeurs::AvisController, type: :controller do
get :sign_up, params: { id: avis.id, email: invited_email } get :sign_up, params: { id: avis.id, email: invited_email }
end end
it { is_expected.to redirect_to new_instructeur_session_url } it { is_expected.to redirect_to new_user_session_url }
end end
end end
@ -238,7 +238,7 @@ describe Instructeurs::AvisController, type: :controller do
let!(:avis) { create(:avis, email: invited_email, dossier: dossier) } let!(:avis) { create(:avis, email: invited_email, dossier: dossier) }
before do before do
sign_in instructeur sign_in(instructeur.user)
get :sign_up, params: { id: avis.id, email: invited_email } get :sign_up, params: { id: avis.id, email: invited_email }
end end
@ -282,7 +282,7 @@ describe Instructeurs::AvisController, type: :controller do
context 'when the email belongs to the invitation' do context 'when the email belongs to the invitation' do
context 'when the instructeur creation succeeds' do context 'when the instructeur creation succeeds' do
it { expect(created_instructeur).to be_present } it { expect(created_instructeur).to be_present }
it { expect(created_instructeur.valid_password?(password)).to be true } it { expect(created_instructeur.user.valid_password?(password)).to be true }
it { expect(Avis).to have_received(:link_avis_to_instructeur) } it { expect(Avis).to have_received(:link_avis_to_instructeur) }

View file

@ -11,7 +11,7 @@ describe Instructeurs::DossiersController, type: :controller do
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
let(:fake_justificatif) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') } let(:fake_justificatif) { Rack::Test::UploadedFile.new("./spec/fixtures/files/piece_justificative_0.pdf", 'application/pdf') }
before { sign_in(instructeur) } before { sign_in(instructeur.user) }
describe '#attestation' do describe '#attestation' do
context 'when a dossier has an attestation' do context 'when a dossier has an attestation' do
@ -110,14 +110,14 @@ describe Instructeurs::DossiersController, type: :controller do
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
before do before do
sign_in instructeur sign_in(instructeur.user)
post :passer_en_instruction, params: { procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' post :passer_en_instruction, params: { procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js'
end end
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) }
it { expect(instructeur.follow?(dossier)).to be true } it { expect(instructeur.follow?(dossier)).to be true }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
it { expect(response.body).to include('.state-button') } it { expect(response.body).to include('.header-actions') }
context 'when the dossier has already been put en_instruction' do context 'when the dossier has already been put en_instruction' do
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
@ -133,7 +133,7 @@ describe Instructeurs::DossiersController, type: :controller do
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
before do before do
sign_in instructeur sign_in(instructeur.user)
post :repasser_en_construction, post :repasser_en_construction,
params: { procedure_id: procedure.id, dossier_id: dossier.id }, params: { procedure_id: procedure.id, dossier_id: dossier.id },
format: 'js' format: 'js'
@ -141,7 +141,7 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_construction)) }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
it { expect(response.body).to include('.state-button') } it { expect(response.body).to include('.header-actions') }
context 'when the dossier has already been put en_construction' do context 'when the dossier has already been put en_construction' do
let(:dossier) { create(:dossier, :en_construction, procedure: procedure) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure) }
@ -155,7 +155,7 @@ describe Instructeurs::DossiersController, type: :controller do
describe '#repasser_en_instruction' do describe '#repasser_en_instruction' do
let(:dossier) { create(:dossier, :refuse, procedure: procedure) } let(:dossier) { create(:dossier, :refuse, procedure: procedure) }
let(:current_user) { instructeur } let(:current_user) { instructeur.user }
before do before do
sign_in current_user sign_in current_user
@ -166,7 +166,7 @@ describe Instructeurs::DossiersController, type: :controller do
it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) } it { expect(dossier.reload.state).to eq(Dossier.states.fetch(:en_instruction)) }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
it { expect(response.body).to include('.state-button') } it { expect(response.body).to include('.header-actions') }
context 'when the dossier has already been put en_instruction' do context 'when the dossier has already been put en_instruction' do
let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) } let(:dossier) { create(:dossier, :en_instruction, procedure: procedure) }
@ -205,7 +205,7 @@ describe Instructeurs::DossiersController, type: :controller do
context "with refuser" do context "with refuser" do
before do before do
dossier.en_instruction! dossier.en_instruction!
sign_in instructeur sign_in(instructeur.user)
end end
context 'simple refusal' do context 'simple refusal' do
@ -239,14 +239,14 @@ describe Instructeurs::DossiersController, type: :controller do
expect(dossier.justificatif_motivation).to be_attached expect(dossier.justificatif_motivation).to be_attached
end end
it { expect(subject.body).to include('.state-button') } it { expect(subject.body).to include('.header-actions') }
end end
end end
context "with classer_sans_suite" do context "with classer_sans_suite" do
before do before do
dossier.en_instruction! dossier.en_instruction!
sign_in instructeur sign_in(instructeur.user)
end end
context 'without attachment' do context 'without attachment' do
subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' } subject { post :terminer, params: { process_action: "classer_sans_suite", procedure_id: procedure.id, dossier_id: dossier.id }, format: 'js' }
@ -267,7 +267,7 @@ describe Instructeurs::DossiersController, type: :controller do
subject subject
end end
it { expect(subject.body).to include('.state-button') } it { expect(subject.body).to include('.header-actions') }
end end
context 'with attachment' do context 'with attachment' do
@ -281,14 +281,14 @@ describe Instructeurs::DossiersController, type: :controller do
expect(dossier.justificatif_motivation).to be_attached expect(dossier.justificatif_motivation).to be_attached
end end
it { expect(subject.body).to include('.state-button') } it { expect(subject.body).to include('.header-actions') }
end end
end end
context "with accepter" do context "with accepter" do
before do before do
dossier.en_instruction! dossier.en_instruction!
sign_in instructeur sign_in(instructeur.user)
expect(NotificationMailer).to receive(:send_closed_notification) expect(NotificationMailer).to receive(:send_closed_notification)
.with(dossier) .with(dossier)
@ -322,14 +322,14 @@ describe Instructeurs::DossiersController, type: :controller do
end end
it 'The instructeur is sent back to the dossier page' do it 'The instructeur is sent back to the dossier page' do
expect(subject.body).to include('.state-button') expect(subject.body).to include('.header-actions')
end end
context 'and the dossier has already an attestation' do context 'and the dossier has already an attestation' do
it 'should not crash' do it 'should not crash' do
dossier.attestation = Attestation.new dossier.attestation = Attestation.new
dossier.save dossier.save
expect(subject.body).to include('.state-button') expect(subject.body).to include('.header-actions')
end end
end end
end end
@ -372,7 +372,7 @@ describe Instructeurs::DossiersController, type: :controller do
expect(dossier.justificatif_motivation).to be_attached expect(dossier.justificatif_motivation).to be_attached
end end
it { expect(subject.body).to include('.state-button') } it { expect(subject.body).to include('.header-actions') }
end end
end end

View file

@ -1,45 +0,0 @@
require "spec_helper"
describe Instructeurs::PasswordsController, type: :controller do
before do
@request.env["devise.mapping"] = Devise.mappings[:instructeur]
end
describe "update" do
context "unified login" do
let(:user) { create(:user, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
let(:administrateur) { create(:administrateur, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
let(:instructeur) { administrateur.instructeur }
before do
@token = instructeur.send(:set_reset_password_token)
user # make sure it's created
administrateur # make sure it's created
end
it "also signs user in" do
put :update, params: {
instructeur: {
reset_password_token: @token,
password: "démarches-simplifiées-pwd",
password_confirmation: "démarches-simplifiées-pwd"
}
}
expect(subject.current_instructeur).to eq(instructeur)
expect(subject.current_user).to eq(user)
end
it "also signs administrateur in" do
put :update, params: {
instructeur: {
reset_password_token: @token,
password: "démarches-simplifiées-pwd",
password_confirmation: "démarches-simplifiées-pwd"
}
}
expect(subject.current_administrateur).to eq(administrateur)
expect(subject.current_user).to eq(user)
end
end
end
end

View file

@ -93,7 +93,7 @@ describe Instructeurs::ProceduresController, type: :controller do
end end
context "when logged in" do context "when logged in" do
before { sign_in(instructeur) } before { sign_in(instructeur.user) }
it { expect(response).to have_http_status(:ok) } it { expect(response).to have_http_status(:ok) }
@ -171,7 +171,7 @@ describe Instructeurs::ProceduresController, type: :controller do
context "when logged in" do context "when logged in" do
before do before do
sign_in(instructeur) sign_in(instructeur.user)
end end
context "without anything" do context "without anything" do
@ -314,7 +314,7 @@ describe Instructeurs::ProceduresController, type: :controller do
context "when logged in" do context "when logged in" do
before do before do
sign_in(instructeur) sign_in(instructeur.user)
end end
context "csv" do context "csv" do
@ -342,7 +342,7 @@ describe Instructeurs::ProceduresController, type: :controller do
let!(:procedure) { create(:procedure, instructeurs: [instructeur]) } let!(:procedure) { create(:procedure, instructeurs: [instructeur]) }
context "when logged in" do context "when logged in" do
before { sign_in(instructeur) } before { sign_in(instructeur.user) }
it { expect(instructeur.procedures_with_email_notifications).to be_empty } it { expect(instructeur.procedures_with_email_notifications).to be_empty }

View file

@ -8,7 +8,7 @@ describe Instructeurs::RechercheController, type: :controller do
before { instructeur.procedures << dossier2.procedure } before { instructeur.procedures << dossier2.procedure }
describe 'GET #index' do describe 'GET #index' do
before { sign_in instructeur } before { sign_in(instructeur.user) }
subject { get :index, params: { q: query } } subject { get :index, params: { q: query } }

View file

@ -1,11 +0,0 @@
require 'spec_helper'
describe Instructeurs::SessionsController, type: :controller do
before do
@request.env["devise.mapping"] = Devise.mappings[:instructeur]
end
describe '#create' do
it { expect(described_class).to be < Sessions::SessionsController }
end
end

View file

@ -14,7 +14,7 @@ describe InvitesController, type: :controller do
subject { post :create, params: { dossier_id: dossier.id, invite_email: email } } subject { post :create, params: { dossier_id: dossier.id, invite_email: email } }
context "when instructeur is signed_in" do context "when instructeur is signed_in" do
let(:signed_in_profile) { create(:instructeur) } let(:signed_in_profile) { create(:instructeur).user }
shared_examples_for "he can not create invitation" do shared_examples_for "he can not create invitation" do
it { expect { subject rescue nil }.to change(Invite, :count).by(0) } it { expect { subject rescue nil }.to change(Invite, :count).by(0) }
@ -25,14 +25,14 @@ describe InvitesController, type: :controller do
end end
context 'when instructeur is invited for avis on dossier' do context 'when instructeur is invited for avis on dossier' do
before { Avis.create(instructeur: signed_in_profile, claimant: create(:instructeur), dossier: dossier) } before { Avis.create(instructeur: signed_in_profile.instructeur, claimant: create(:instructeur), dossier: dossier) }
it_behaves_like "he can not create invitation" it_behaves_like "he can not create invitation"
end end
context 'when instructeur has access to dossier' do context 'when instructeur has access to dossier' do
before do before do
signed_in_profile.procedures << dossier.procedure signed_in_profile.instructeur.procedures << dossier.procedure
end end
it_behaves_like "he can not create invitation" it_behaves_like "he can not create invitation"

View file

@ -18,7 +18,7 @@ describe RootController, type: :controller do
before do before do
instructeur.procedures << procedure instructeur.procedures << procedure
sign_in instructeur sign_in(instructeur.user)
end end
it { expect(subject).to redirect_to(instructeur_procedures_path) } it { expect(subject).to redirect_to(instructeur_procedures_path) }

View file

@ -35,20 +35,6 @@ describe Sessions::SessionsController, type: :controller do
end end
end end
describe '#create with instructeur connected' do
before do
@request.env["devise.mapping"] = Devise.mappings[:instructeur]
allow_any_instance_of(described_class).to receive(:instructeur_signed_in?).and_return(true)
allow_any_instance_of(described_class).to receive(:current_instructeur).and_return(instructeur)
end
it 'calls sign out for instructeur' do
expect_any_instance_of(described_class).to receive(:sign_out).with(:instructeur)
post :create
end
end
describe '#create with administrateur connected' do describe '#create with administrateur connected' do
before do before do
@request.env["devise.mapping"] = Devise.mappings[:administrateur] @request.env["devise.mapping"] = Devise.mappings[:administrateur]

View file

@ -1,7 +1,7 @@
describe Instructeurs::ActivateController, type: :controller do describe Users::ActivateController, type: :controller do
describe '#new' do describe '#new' do
let(:instructeur) { create(:instructeur) } let(:user) { create(:user) }
let(:token) { instructeur.send(:set_reset_password_token) } let(:token) { user.send(:set_reset_password_token) }
before { allow(controller).to receive(:trust_device) } before { allow(controller).to receive(:trust_device) }

View file

@ -7,8 +7,8 @@ describe Users::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: 'mot de passe complexe') }
let(:administrateur) { create(:administrateur, email: 'unique@plop.com', password: 'mot de passe complexe') } let(:administrateur) { create(:administrateur, email: 'unique@plop.com', password: 'mot de passe complexe') }
let(:user) { administrateur.instructeur.user }
before do before do
@token = user.send(:set_reset_password_token) @token = user.send(:set_reset_password_token)

View file

@ -12,6 +12,7 @@ describe Users::SessionsController, type: :controller do
context "when the user is also a instructeur and an administrateur" do context "when the user is also a instructeur and an administrateur" do
let!(:administrateur) { create(:administrateur, email: email, password: password) } let!(:administrateur) { create(:administrateur, email: email, password: password) }
let(:instructeur) { administrateur.instructeur } let(:instructeur) { administrateur.instructeur }
let(:user) { instructeur.user }
let(:trusted_device) { true } let(:trusted_device) { true }
let(:send_password) { password } let(:send_password) { password }
@ -29,14 +30,15 @@ describe Users::SessionsController, type: :controller do
context 'when the device is not trusted' do context 'when the device is not trusted' do
let(:trusted_device) { false } let(:trusted_device) { false }
it 'redirects to the root path' do it 'redirects to the send_linked_path' do
subject subject
expect(controller).to redirect_to(root_path) expect(controller).to redirect_to(link_sent_path(email: user.email))
expect(controller.current_user).to eq(user) expect(controller.current_user).to eq(user)
expect(controller.current_instructeur).to eq(instructeur) expect(controller.current_instructeur).to eq(instructeur)
expect(controller.current_administrateur).to eq(administrateur) # WTF?
# expect(controller.current_administrateur).to eq(administrateur)
expect(user.loged_in_with_france_connect).to eq(nil) expect(user.loged_in_with_france_connect).to eq(nil)
end end
end end
@ -73,6 +75,8 @@ describe Users::SessionsController, type: :controller do
end end
describe '#destroy' do describe '#destroy' do
let!(:user) { create(:user, email: email, password: password, loged_in_with_france_connect: loged_in_with_france_connect) }
before do before do
sign_in user sign_in user
delete :destroy delete :destroy
@ -103,47 +107,11 @@ describe Users::SessionsController, type: :controller do
end end
end end
context "when associated instructeur" do
let(:user) { create(:user, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
let(:instructeur) { create(:instructeur, email: 'unique@plop.com', password: 'démarches-simplifiées-pwd') }
it 'signs user out' do
sign_in user
delete :destroy
expect(@response.redirect?).to be(true)
expect(subject.current_user).to be(nil)
end
it 'signs instructeur out' do
sign_in instructeur
delete :destroy
expect(@response.redirect?).to be(true)
expect(subject.current_instructeur).to be(nil)
end
it 'signs user + instructeur out' do
sign_in user
sign_in instructeur
delete :destroy
expect(@response.redirect?).to be(true)
expect(subject.current_user).to be(nil)
expect(subject.current_instructeur).to be(nil)
end
it 'signs user out from france connect' do
user.update(loged_in_with_france_connect: User.loged_in_with_france_connects.fetch(:particulier))
sign_in user
delete :destroy
expect(@response.headers["Location"]).to eq(FRANCE_CONNECT[:particulier][:logout_endpoint])
end
end
context "when associated administrateur" do context "when associated administrateur" do
let(:administrateur) { create(:administrateur, email: 'unique@plop.com') } let(:administrateur) { create(:administrateur, user: user) }
it 'signs user + instructeur + administrateur out' do it 'signs user + instructeur + administrateur out' do
sign_in user sign_in user
sign_in administrateur.instructeur
sign_in administrateur sign_in administrateur
delete :destroy delete :destroy
expect(@response.redirect?).to be(true) expect(@response.redirect?).to be(true)
@ -181,7 +149,7 @@ describe Users::SessionsController, type: :controller do
before do before do
if logged if logged
sign_in instructeur sign_in(instructeur.user)
end end
allow(controller).to receive(:trust_device) allow(controller).to receive(:trust_device)
allow(controller).to receive(:send_login_token_or_bufferize) allow(controller).to receive(:send_login_token_or_bufferize)

View file

@ -4,8 +4,16 @@ FactoryBot.define do
email { generate(:administrateur_email) } email { generate(:administrateur_email) }
password { 'mon chien aime les bananes' } password { 'mon chien aime les bananes' }
after(:create) do |admin| transient do
create(:instructeur, email: admin.email, password: admin.password) user { nil }
end
after(:create) do |admin, evaluator|
if evaluator.user.present?
create(:instructeur, email: admin.email, password: admin.password, user: evaluator.user)
else
create(:instructeur, email: admin.email, password: admin.password)
end
end end
end end

View file

@ -1,7 +1,21 @@
FactoryBot.define do FactoryBot.define do
sequence(:instructeur_email) { |n| "gest#{n}@gest.com" } sequence(:instructeur_email) { |n| "inst#{n}@inst.com" }
factory :instructeur do factory :instructeur do
email { generate(:instructeur_email) } email { generate(:instructeur_email) }
password { 'démarches-simplifiées-pwd' }
transient do
password { 'somethingverycomplated!' }
end
after(:create) do |instructeur, evaluator|
if evaluator.user.present?
user = evaluator.user
else
user = create(:user, email: instructeur.email, password: evaluator.password)
end
instructeur.update!(user: user)
end
end end
end end

View file

@ -1,67 +0,0 @@
require 'spec_helper'
feature 'Administrator connection' do
include ActiveJob::TestHelper
let(:email) { 'admin1@admin.com' }
let(:password) { 'mon chien aime les bananes' }
let!(:admin) { create(:administrateur, :with_procedure, email: email, password: password) }
let!(:user) { create(:user, email: email, password: password) }
before do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
visit new_administrateur_session_path
end
scenario 'administrator is on sign in page' do
expect(page).to have_css('#new_user')
end
context "admin fills form and log in" do
before do
sign_in_with(email, password, true)
end
scenario 'a menu button is available' do
expect(page).to have_css('#admin_menu')
end
context 'when he click on the menu' do
before do
page.find_by_id('admin_menu').click
end
scenario 'it displays the menu' do
expect(page).to have_css('a#profile')
expect(page).to have_css('#sign-out')
end
context 'when clicking on sign-out' do
before do
stub_request(:get, "https://api.github.com/repos/betagouv/tps/releases/latest")
.to_return(:status => 200, :body => '{"tag_name": "plip", "body": "blabla", "published_at": "2016-02-09T16:46:47Z"}', :headers => {})
page.find_by_id('sign-out').find('a').click
end
scenario 'admin is redireted to home page' do
expect(page).to have_css('.landing')
end
end
context 'when clicking on profile' do
before do
page.find_by_id('profile').click
end
scenario 'it redirects to profile page' do
expect(page).to have_css('#profil-page')
end
context 'when clicking on procedure' do
before do
page.click_on('Tableau de bord').click
end
scenario 'it redirects to procedure page' do
expect(page).to have_content('Démarches')
end
end
end
end
end
end

View file

@ -81,7 +81,7 @@ feature 'Getting help:' do
let(:instructeur) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
before do before do
login_as instructeur, scope: :instructeur login_as instructeur.user, scope: :user
end end
scenario 'a Help menu is visible on signed-in pages' do scenario 'a Help menu is visible on signed-in pages' do

View file

@ -0,0 +1,30 @@
require 'spec_helper'
feature 'As an instructeur', js: true do
let(:administrateur) { create(:administrateur, :with_procedure) }
let(:procedure) { administrateur.procedures.first }
let(:instructeur_email) { 'new_instructeur@gouv.fr' }
before do
login_as administrateur, scope: :administrateur
visit admin_procedure_assigns_path(procedure)
fill_in :instructeur_email, with: instructeur_email
perform_enqueued_jobs do
click_button 'Valider'
end
end
scenario 'I can register' do
confirmation_email = open_email(instructeur_email)
token_params = confirmation_email.body.match(/token=[^"]+/)
visit "users/activate?#{token_params}"
fill_in :user_password, with: 'démarches-simplifiées-pwd'
click_button 'Définir le mot de passe'
expect(page).to have_content 'Mot de passe enregistré'
end
end

View file

@ -13,9 +13,7 @@ feature 'The instructeur part' do
Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true) Flipflop::FeatureSet.current.test!.switch!(:enable_email_login_token, true)
end end
context 'when the instructeur is also a user' do context 'the instructeur is also a user' do
let!(:user) { create(:user, email: instructeur.email, password: password) }
scenario 'a instructeur can fill a dossier' do scenario 'a instructeur can fill a dossier' do
visit commencer_path(path: procedure.path) visit commencer_path(path: procedure.path)
click_on 'Jai déjà un compte' click_on 'Jai déjà un compte'
@ -23,7 +21,10 @@ feature 'The instructeur part' do
expect(page).to have_current_path new_user_session_path expect(page).to have_current_path new_user_session_path
sign_in_with(instructeur.email, password, true) sign_in_with(instructeur.email, password, true)
expect(page).to have_current_path(commencer_path(path: procedure.path)) # connexion link erase user stored location
# expect(page).to have_current_path(commencer_path(path: procedure.path))
visit commencer_path(path: procedure.path)
click_on 'Commencer la démarche' click_on 'Commencer la démarche'
expect(page).to have_content('Identifier votre établissement') expect(page).to have_content('Identifier votre établissement')
@ -67,6 +68,7 @@ feature 'The instructeur part' do
end end
expect(page).to have_text('Dossier traité avec succès.') expect(page).to have_text('Dossier traité avec succès.')
expect(page).to have_link('Archiver le dossier')
dossier.reload dossier.reload
expect(dossier.state).to eq(Dossier.states.fetch(:accepte)) expect(dossier.state).to eq(Dossier.states.fetch(:accepte))

View file

@ -12,7 +12,7 @@ feature "procedure filters" do
before do before do
champ.update(value: "Mon champ rempli") champ.update(value: "Mon champ rempli")
champ_2.update(value: "Mon autre champ rempli différemment") champ_2.update(value: "Mon autre champ rempli différemment")
login_as instructeur, scope: :instructeur login_as(instructeur.user, scope: :user)
visit instructeur_procedure_path(procedure) visit instructeur_procedure_path(procedure)
end end

View file

@ -11,7 +11,7 @@ describe Administrateur, type: :model do
context 'unified login' do context 'unified login' do
it 'syncs credentials to associated user' do it 'syncs credentials to associated user' do
administrateur = create(:administrateur) administrateur = create(:administrateur)
user = create(:user, email: administrateur.email) user = administrateur.instructeur.user
administrateur.update(email: 'whoami@plop.com', password: 'voilà un super mdp') administrateur.update(email: 'whoami@plop.com', password: 'voilà un super mdp')
@ -28,7 +28,6 @@ describe Administrateur, type: :model do
instructeur.reload instructeur.reload
expect(instructeur.email).to eq('whoami@plop.com') expect(instructeur.email).to eq('whoami@plop.com')
expect(instructeur.valid_password?('et encore un autre mdp')).to be(true)
end end
end end

View file

@ -142,22 +142,11 @@ describe Instructeur, type: :model do
end end
context 'unified login' do context 'unified login' do
it 'syncs credentials to associated user' do
instructeur = create(:instructeur)
user = create(:user, email: instructeur.email)
instructeur.update(email: 'whoami@plop.com', password: 'démarches-simplifiées-pwd')
user.reload
expect(user.email).to eq('whoami@plop.com')
expect(user.valid_password?('démarches-simplifiées-pwd')).to be(true)
end
it 'syncs credentials to associated administrateur' do it 'syncs credentials to associated administrateur' do
admin = create(:administrateur) admin = create(:administrateur)
instructeur = admin.instructeur user = admin.instructeur.user
instructeur.update(password: 'démarches-simplifiées-pwd') user.update(password: 'démarches-simplifiées-pwd')
admin.reload admin.reload
expect(admin.valid_password?('démarches-simplifiées-pwd')).to be(true) expect(admin.valid_password?('démarches-simplifiées-pwd')).to be(true)

View file

@ -102,21 +102,9 @@ describe User, type: :model do
end end
context 'unified login' do context 'unified login' do
it 'syncs credentials to associated instructeur' do
user = create(:user)
instructeur = create(:instructeur, email: user.email)
user.update(email: 'whoami@plop.com', password: 'démarches-simplifiées2')
user.confirm
instructeur.reload
expect(instructeur.email).to eq('whoami@plop.com')
expect(instructeur.valid_password?('démarches-simplifiées2')).to be(true)
end
it 'syncs credentials to associated administrateur' do it 'syncs credentials to associated administrateur' do
user = create(:user) admin = create(:administrateur)
admin = create(:administrateur, email: user.email) user = admin.instructeur.user
user.update(email: 'whoami@plop.com', password: 'démarches-simplifiées2') user.update(email: 'whoami@plop.com', password: 'démarches-simplifiées2')
user.confirm user.confirm

View file

@ -28,6 +28,6 @@ describe 'admin/instructeurs/index.html.haml', type: :view do
array: true)) array: true))
render render
end end
it { expect(rendered).to match(/gest\d+@gest.com/) } it { expect(rendered).to match(/inst\d+@inst.com/) }
end end
end end

View file

@ -3,7 +3,8 @@ describe 'instructeurs/dossiers/show.html.haml', type: :view do
let(:dossier) { create(:dossier, :en_construction) } let(:dossier) { create(:dossier, :en_construction) }
before do before do
sign_in current_instructeur sign_in(current_instructeur.user)
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
assign(:dossier, dossier) assign(:dossier, dossier)
end end

View file

@ -6,36 +6,34 @@ describe 'layouts/_navbar.html.haml', type: :view do
let!(:procedure) { create(:procedure, administrateur: administrateur) } let!(:procedure) { create(:procedure, administrateur: administrateur) }
before do
allow(view).to receive(:instructeur_signed_in?).and_return(instructeur_signed_in)
allow(view).to receive(:administrateur_signed_in?).and_return(administrateur_signed_in)
end
describe 'navbar entries' do describe 'navbar entries' do
before { render }
subject { rendered }
context 'when disconnected' do context 'when disconnected' do
before do let(:instructeur_signed_in) { false }
render let(:administrateur_signed_in) { false }
end
subject { rendered }
it { is_expected.to match(/Connexion/) } it { is_expected.to match(/Connexion/) }
end end
context 'when administrateur is connected' do context 'when administrateur is connected' do
before do let(:instructeur_signed_in) { false }
@request.env["devise.mapping"] = Devise.mappings[:administrateur] let(:administrateur_signed_in) { true }
@current_user = administrateur
sign_in @current_user
render
end
subject { rendered }
it { is_expected.to match(/Déconnexion/) } it { is_expected.to match(/Déconnexion/) }
end end
context 'when instructeur is connected' do context 'when instructeur is connected' do
before do let(:instructeur_signed_in) { true }
@request.env["devise.mapping"] = Devise.mappings[:instructeur] let(:administrateur_signed_in) { false }
@current_user = instructeur
sign_in @current_user
render
end
subject { rendered }
it { is_expected.to match(/Déconnexion/) } it { is_expected.to match(/Déconnexion/) }
end end
end end

View file

@ -1,7 +1,13 @@
require 'spec_helper' require 'spec_helper'
describe 'layouts/_new_header.html.haml', type: :view do describe 'layouts/_new_header.html.haml', type: :view do
let(:current_instructeur) { nil }
before do before do
allow(view).to receive(:multiple_devise_profile_connect?).and_return(false)
allow(view).to receive(:instructeur_signed_in?).and_return((profile == :instructeur))
allow(view).to receive(:current_instructeur).and_return(current_instructeur)
if user if user
sign_in user sign_in user
allow(controller).to receive(:nav_bar_profile).and_return(profile) allow(controller).to receive(:nav_bar_profile).and_return(profile)
@ -46,8 +52,10 @@ describe 'layouts/_new_header.html.haml', type: :view do
end end
context 'when rendering for instructeur' do context 'when rendering for instructeur' do
let(:user) { create(:instructeur) } let(:instructeur) { create(:instructeur) }
let(:user) { instructeur.user }
let(:profile) { :instructeur } let(:profile) { :instructeur }
let(:current_instructeur) { instructeur }
it { is_expected.to have_css("a.header-logo[href=\"#{instructeur_procedures_path}\"]") } it { is_expected.to have_css("a.header-logo[href=\"#{instructeur_procedures_path}\"]") }

View file

@ -4,6 +4,10 @@ describe 'layouts/procedure_context.html.haml', type: :view do
let(:procedure) { create(:simple_procedure, :with_service) } let(:procedure) { create(:simple_procedure, :with_service) }
let(:dossier) { create(:dossier, procedure: procedure) } let(:dossier) { create(:dossier, procedure: procedure) }
before do
allow(view).to receive(:instructeur_signed_in?).and_return(false)
end
subject do subject do
render html: 'Column content', layout: 'layouts/procedure_context.html.haml' render html: 'Column content', layout: 'layouts/procedure_context.html.haml'
end end

View file

@ -6,7 +6,7 @@ describe 'shared/dossiers/demande.html.haml', type: :view do
let(:dossier) { create(:dossier, :en_construction, procedure: procedure, etablissement: etablissement, individual: individual) } let(:dossier) { create(:dossier, :en_construction, procedure: procedure, etablissement: etablissement, individual: individual) }
before do before do
sign_in current_instructeur sign_in(current_instructeur.user)
end end
subject! { render 'shared/dossiers/demande.html.haml', dossier: dossier, demande_seen_at: nil, profile: 'usager' } subject! { render 'shared/dossiers/demande.html.haml', dossier: dossier, demande_seen_at: nil, profile: 'usager' }