Merge branch 'dev'

This commit is contained in:
Frederic Merizen 2018-05-02 11:14:00 +02:00
commit 4494d0ab58
25 changed files with 275 additions and 58 deletions

View file

@ -203,7 +203,7 @@ GEM
ffi (1.9.23)
fission (0.5.0)
CFPropertyList (~> 2.2)
flipflop (2.3.1)
flipflop (2.4.0)
activesupport (>= 4.0)
fog (1.42.0)
fog-aliyun (>= 0.1.0)

View file

@ -23,7 +23,7 @@ class Admin::GestionnairesController < AdminController
procedure_id = params[:procedure_id]
if @gestionnaire.nil?
new_gestionnaire!
invite_gestionnaire(params[:gestionnaire][:email])
else
assign_gestionnaire!
end
@ -42,22 +42,23 @@ class Admin::GestionnairesController < AdminController
private
def new_gestionnaire!
attributes = params.require(:gestionnaire).permit(:email)
.merge(password: SecureRandom.hex(5))
def invite_gestionnaire(email)
password = SecureRandom.hex
@gestionnaire = Gestionnaire.create(
attributes.merge(
administrateurs: [current_administrateur]
)
email: email,
password: password,
password_confirmation: password,
administrateurs: [current_administrateur]
)
if @gestionnaire.errors.messages.empty?
@gestionnaire.invite!
if User.exists?(email: @gestionnaire.email)
GestionnaireMailer.user_to_gestionnaire(@gestionnaire.email).deliver_now!
else
User.create(attributes)
GestionnaireMailer.new_gestionnaire(@gestionnaire.email, @gestionnaire.password).deliver_now!
User.create(email: email, password: password)
end
flash.notice = 'Accompagnateur ajouté'
else

View file

@ -1,10 +1,13 @@
class ApplicationController < ActionController::Base
MAINTENANCE_MESSAGE = 'Le site est actuellement en maintenance. Il sera à nouveau disponible dans un court instant.'
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :load_navbar_left_pannel_partial_url
before_action :set_raven_context
before_action :authorize_request_for_profiler
before_action :reject, if: -> { Flipflop.maintenance_mode? }
before_action :staging_authenticate
@ -135,4 +138,23 @@ class ApplicationController < ActionController::Base
)
# END OF FIXME
end
def reject
authorized_request =
request.path_info == '/' ||
request.path_info.start_with?('/manager') ||
request.path_info.start_with?('/administrations')
api_request = request.path_info.start_with?('/api/')
if administration_signed_in? || authorized_request
flash.now.alert = MAINTENANCE_MESSAGE
elsif api_request
render json: { error: MAINTENANCE_MESSAGE }.to_json, status: :service_unavailable
else
%i(user gestionnaire administrateur).each { |role| sign_out(role) }
flash[:alert] = MAINTENANCE_MESSAGE
redirect_to root_path
end
end
end

View file

@ -0,0 +1,47 @@
class Gestionnaires::ActivateController < ApplicationController
layout "new_application"
def new
@gestionnaire = Gestionnaire.with_reset_password_token(params[:token])
if !@gestionnaire
flash.alert = "Le lien de validation du compte accompagnateur a expiré, contactez-nous à contact@demarches-simplifiees.fr pour obtenir un nouveau lien."
redirect_to root_path
end
end
def create
password = create_gestionnaire_params[:password]
gestionnaire = Gestionnaire.reset_password_by_token({
password: password,
password_confirmation: password,
reset_password_token: create_gestionnaire_params[:reset_password_token]
})
if gestionnaire && gestionnaire.errors.empty?
sign_in(gestionnaire, scope: :gestionnaire)
try_to_authenticate(User, gestionnaire.email, password)
try_to_authenticate(Administrateur, gestionnaire.email, password)
flash.notice = "Mot de passe enregistré"
redirect_to gestionnaire_procedures_path
else
flash.alert = gestionnaire.errors.full_messages
redirect_to gestionnaire_activate_path(token: create_gestionnaire_params[:reset_password_token])
end
end
private
def create_gestionnaire_params
params.require(:gestionnaire).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

@ -19,6 +19,20 @@ module Manager
redirect_to manager_administrateur_path(params[:id])
end
def enable_feature
administrateur = Administrateur.find(params[:id])
params[:features].each do |key, enable|
if enable
administrateur.enable_feature(key.to_sym)
else
administrateur.disable_feature(key.to_sym)
end
end
head :ok
end
private
def create_administrateur_params

View file

@ -237,9 +237,10 @@ module NewGestionnaire
when 'user', 'etablissement', 'entreprise'
if filter['column'] == 'date_creation'
date = filter['value'].to_date rescue nil
dossiers
.includes(filter['table'])
.where("#{filter['table'].pluralize}.#{filter['column']} = ?", filter['value'].to_date)
.where("#{filter['table'].pluralize}.#{filter['column']} = ?", date)
else
dossiers
.includes(filter['table'])

View file

@ -15,6 +15,7 @@ class AdministrateurDashboard < Administrate::BaseDashboard
procedures: Field::HasMany.with_options(limit: 20),
registration_state: Field::String.with_options(searchable: false),
current_sign_in_at: Field::DateTime,
features: FeaturesField
}.freeze
# COLLECTION_ATTRIBUTES
@ -38,6 +39,7 @@ class AdministrateurDashboard < Administrate::BaseDashboard
:updated_at,
:registration_state,
:current_sign_in_at,
:features,
:procedures,
].freeze

View file

@ -0,0 +1,4 @@
require "administrate/field/base"
class FeaturesField < Administrate::Field::Base
end

View file

@ -11,29 +11,12 @@ module Flipflop::Strategies
def enabled?(feature)
# Can only check features if we have the user's session.
if request?
legacy_enabled?(feature) || find_current_administrateur&.feature_enabled?(feature)
find_current_administrateur&.feature_enabled?(feature)
end
end
private
def legacy_enabled?(feature)
if self.class.legacy_features_map.present?
ids = self.class.legacy_features_map["#{feature}_allowed_for_admin_ids"]
ids.present? && find_current_administrateur&.id&.in?(ids)
end
end
LEGACY_CONFIG_FILE = Rails.root.join("config", "initializers", "features.yml")
def self.legacy_features_map
@@legacy_features_map = begin
if File.exist?(LEGACY_CONFIG_FILE)
YAML.load_file(LEGACY_CONFIG_FILE)
end
end
end
def find_current_administrateur
if request.session["warden.user.administrateur.key"]
administrateur_id = request.session["warden.user.administrateur.key"][0][0]

View file

@ -1,8 +1,12 @@
class GestionnaireMailer < ApplicationMailer
layout 'mailers/layout'
def new_gestionnaire(email, password)
send_mail(email, password, "Vous avez été nommé accompagnateur sur demarches-simplifiees.fr")
def invite_gestionnaire(gestionnaire, reset_password_token)
@reset_password_token = reset_password_token
@gestionnaire = gestionnaire
mail(to: gestionnaire.email,
subject: "demarches-simplifiees.fr - Activez votre compte accompagnateur",
reply_to: "contact@demarches-simplifiees.fr")
end
def user_to_gestionnaire(email)

View file

@ -144,6 +144,12 @@ class Gestionnaire < ApplicationRecord
Follow.where(gestionnaire: self, dossier: dossier).update_all(attributes)
end
def invite!
reset_password_token = set_reset_password_token
GestionnaireMailer.invite_gestionnaire(self, reset_password_token).deliver_now!
end
private
def valid_couple_table_attr?(table, column)

View file

@ -1,5 +1,15 @@
class ChampSerializer < ActiveModel::Serializer
include Rails.application.routes.url_helpers
attributes :value
has_one :type_de_champ
def value
if object.piece_justificative_file.attached?
url_for(object.piece_justificative_file)
else
object.value
end
end
end

View file

@ -7,11 +7,11 @@
%span{ "aria-hidden" => "true" } ×
%h4#myModalLabel.modal-title
Transférer la procédure à un autre administrateur
Envoyer une copie de cette procédure à un autre administrateur
.modal-body
%p
Cette fonctionnalité vous permet de transmettre un clone de votre procédure à un autre administrateur.
Cette fonctionnalité vous permet de d'envoyer une copie de votre procédure à un autre administrateur.
%div{ style:'margin-top:20px' }
= text_field_tag :email_admin, '', { class: 'form-control',

View file

@ -15,7 +15,7 @@
%a#transfer.btn.btn-small.btn-default{ "data-target" => "#transferModal", "data-toggle" => "modal", :type => "button", style: 'float: right; margin-top: 10px; margin-right: 10px;' }
%i.fa.fa-exchange
Transférer
Envoyer une copie
= render partial: '/admin/procedures/modal_transfer'

View file

@ -0,0 +1,26 @@
%table#features
- Flipflop.feature_set.features.each do |feature|
- if !feature.group || feature.group.key != :production
%tr
%td= feature.title
%td
= check_box_tag "enable-feature", "enable", field.data[feature.name], data: { url: enable_feature_manager_administrateur_path(field.resource.id), key: feature.key }
:javascript
window.onload = function() {
$('#features input[type=checkbox]').on('change', function(evt) {
let url = $(evt.target).data('url');
let key = $(evt.target).data('key');
let features = {};
features[key] = $(evt.target).prop('checked');
$.ajax(url, {
method: 'put',
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify({
features: features
})
});
});
};

View file

@ -0,0 +1,19 @@
- content_for(:title, 'Activation de votre compte accompagnateur')
Bonjour,
%br
%br
Vous venez d'être nommé accompagnateur sur demarches-simplifiees.fr.
%br
Votre compte a été créé pour l'adresse email #{@gestionnaire.email}. Pour lactiver, je vous invite à cliquer sur le lien suivant : 
= link_to(gestionnaire_activate_url(token: @reset_password_token), gestionnaire_activate_url(token: @reset_password_token))
%br
%br
Par ailleurs, notre site de documentation qui regroupe l'ensemble des informations relatives à demarches-simplifiees.fr ainsi que des tutoriels dutilisation est à votre disposition : 
= link_to('https://demarches-simplifiees.gitbook.io/demarches-simplifiees/', 'https://demarches-simplifiees.gitbook.io/demarches-simplifiees/')
%br
%br
Bonne journée,
%br
%br
L'équipe demarches-simplifiees.fr

View file

@ -1,11 +0,0 @@
Bienvenue sur demarches-simplifiees.fr,
Vous venez d'être nommé accompagnateur sur demarches-simplifiees.fr. Pour mémoire, voici quelques informations utiles :
URL : <%= new_gestionnaire_session_url %>
Login : <%= @email %>
Mot de passe : <%= @args %>
Bonne journée,
L'équipe demarches-simplifiees.fr

View file

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

View file

@ -31,6 +31,8 @@ Rails.application.configure do
# ActionMailer::Base.deliveries array.
config.action_mailer.delivery_method = :test
config.active_storage.service = :local
# Randomize the order test cases are executed.
config.active_support.test_order = :random

View file

@ -1,18 +1,26 @@
Flipflop.configure do
strategy :cookie
strategy :cookie,
secure: Rails.env.production?,
httponly: true
strategy :active_record
strategy :user_preference
strategy :default
group :champs do
feature :champ_pj
feature :champ_siret
feature :champ_pj,
title: "Champ pièce justificative"
feature :champ_siret,
title: "Champ SIRET"
end
feature :web_hook
group :production do
feature :remote_storage,
default: Rails.env.production? || Rails.env.staging?
feature :weekly_overview,
default: Rails.env.production?
end
feature :maintenance_mode
end

View file

@ -1,8 +0,0 @@
remote_storage: false
weekly_overview: false
champ_pj_allowed_for_admin_ids:
- 0
champ_siret_allowed_for_admin_ids:
- 0
web_hook_allowed_for_admin_ids:
- 0

View file

@ -8,6 +8,7 @@ Rails.application.routes.draw do
resources :administrateurs, only: [:index, :show, :new, :create] do
post 'reinvite', on: :member
put 'enable_feature', on: :member
end
resources :demandes, only: [:index]
@ -114,6 +115,11 @@ Rails.application.routes.draw do
resource :dossiers
end
namespace :gestionnaire do
get 'activate' => '/gestionnaires/activate#new'
patch 'activate' => '/gestionnaires/activate#create'
end
namespace :admin do
get 'activate' => '/administrateurs/activate#new'
patch 'activate' => '/administrateurs/activate#create'

View file

@ -149,8 +149,7 @@ describe Admin::GestionnairesController, type: :controller do
context 'Email notification' do
it 'Notification email is sent when accompagnateur is create' do
expect(GestionnaireMailer).to receive(:new_gestionnaire).and_return(GestionnaireMailer)
expect(GestionnaireMailer).to receive(:deliver_now!)
expect_any_instance_of(Gestionnaire).to receive(:invite!)
subject
end
end

View file

@ -82,4 +82,57 @@ describe ApplicationController, type: :controller do
end
end
end
describe 'reject before action' do
let(:path_info) { '/one_path' }
before do
allow(@controller).to receive(:redirect_to)
allow(@controller).to receive(:sign_out)
allow(@controller).to receive(:render)
@request.path_info = path_info
end
context 'when no administration is logged in' do
before { @controller.send(:reject) }
it { expect(@controller).to have_received(:sign_out).with(:user) }
it { expect(@controller).to have_received(:sign_out).with(:gestionnaire) }
it { expect(@controller).to have_received(:sign_out).with(:administrateur) }
it { expect(flash[:alert]).to eq(ApplicationController::MAINTENANCE_MESSAGE) }
it { expect(@controller).to have_received(:redirect_to).with(root_path) }
context 'when the path is safe' do
%w(/ /manager /administrations).each do |path|
let(:path_info) { path }
it { expect(@controller).not_to have_received(:sign_out) }
it { expect(@controller).not_to have_received(:redirect_to) }
it { expect(flash.alert).to eq(ApplicationController::MAINTENANCE_MESSAGE) }
end
end
context 'when the path is api related' do
let(:path_info) { '/api/some-stuff' }
let(:json_error) { { error: ApplicationController::MAINTENANCE_MESSAGE }.to_json }
it { expect(@controller).not_to have_received(:sign_out) }
it { expect(@controller).not_to have_received(:redirect_to) }
it { expect(flash.alert).to be_nil }
it { expect(@controller).to have_received(:render).with({ json: json_error, status: :service_unavailable }) }
end
end
context 'when a administration is logged in' do
let(:current_administration) { create(:administration) }
before do
sign_in(current_administration)
@controller.send(:reject)
end
it { expect(@controller).not_to have_received(:sign_out) }
it { expect(@controller).not_to have_received(:redirect_to) }
it { expect(flash[:alert]).to eq(ApplicationController::MAINTENANCE_MESSAGE) }
end
end
end

View file

@ -0,0 +1,22 @@
describe ChampSerializer do
describe '#attributes' do
subject { ChampSerializer.new(champ).serializable_hash }
context 'when type champ is piece justificative' do
include Rails.application.routes.url_helpers
let(:champ) { create(:champ, type_de_champ: create(:type_de_champ_piece_justificative)) }
before { champ.piece_justificative_file.attach({ filename: __FILE__, io: File.open(__FILE__) }) }
after { champ.piece_justificative_file.purge }
it { is_expected.to include(value: url_for(champ.piece_justificative_file)) }
end
context 'when type champ is not piece justificative' do
let(:champ) { create(:champ, value: "blah") }
it { is_expected.to include(value: "blah") }
end
end
end