diff --git a/app/assets/stylesheets/new_design/motivation.scss b/app/assets/stylesheets/new_design/motivation.scss index 5adcbc78b..a8244b1f4 100644 --- a/app/assets/stylesheets/new_design/motivation.scss +++ b/app/assets/stylesheets/new_design/motivation.scss @@ -43,4 +43,12 @@ padding-left: $default-spacer * 1.5; } } + + .optional-justificatif { + margin-bottom: 16px; + } + + input[data-direct-upload-url] { + width: 100%; + } } diff --git a/app/controllers/gestionnaires/dossiers_controller.rb b/app/controllers/gestionnaires/dossiers_controller.rb index ded7aaba6..a96616bfe 100644 --- a/app/controllers/gestionnaires/dossiers_controller.rb +++ b/app/controllers/gestionnaires/dossiers_controller.rb @@ -93,16 +93,17 @@ module Gestionnaires def terminer motivation = params[:dossier] && params[:dossier][:motivation] + justificatif = params[:dossier] && params[:dossier][:justificatif_motivation] case params[:process_action] when "refuser" - dossier.refuser!(current_gestionnaire, motivation) + dossier.refuser!(current_gestionnaire, motivation, justificatif) flash.notice = "Dossier considéré comme refusé." when "classer_sans_suite" - dossier.classer_sans_suite!(current_gestionnaire, motivation) + dossier.classer_sans_suite!(current_gestionnaire, motivation, justificatif) flash.notice = "Dossier considéré comme sans suite." when "accepter" - dossier.accepter!(current_gestionnaire, motivation) + dossier.accepter!(current_gestionnaire, motivation, justificatif) flash.notice = "Dossier traité avec succès." end diff --git a/app/controllers/new_administrateur/procedure_administrateurs_controller.rb b/app/controllers/new_administrateur/procedure_administrateurs_controller.rb new file mode 100644 index 000000000..b2fa6811d --- /dev/null +++ b/app/controllers/new_administrateur/procedure_administrateurs_controller.rb @@ -0,0 +1,46 @@ +module NewAdministrateur + class ProcedureAdministrateursController < AdministrateurController + before_action :retrieve_procedure + + def index + end + + def create + email = params.require(:administrateur)[:email]&.strip&.downcase + + # Find the admin + administrateur = Administrateur.find_by(email: email) + if administrateur.nil? + flash.alert = "L’administrateur « #{email} » n’existe pas. Invitez-le à demander un compte administrateur à l’addresse #{new_demande_url}." + return + end + + # Prevent duplicates (also enforced in the database in administrateurs_procedures) + if @procedure.administrateurs.include?(administrateur) + flash.alert = "L’administrateur « #{administrateur.email} » est déjà administrateur de « #{@procedure.libelle} »." + return + end + + # Actually add the admin + @procedure.administrateurs << administrateur + @administrateur = administrateur + flash.notice = "L’administrateur « #{administrateur.email} » a été ajouté à la démarche « #{@procedure.libelle} »." + end + + def destroy + administrateur = @procedure.administrateurs.find(params[:id]) + + # Prevent self-removal (Also enforced in the UI) + if administrateur == current_administrateur + flash.error = "Vous ne pouvez pas vous retirer vous-même d’une démarche." + end + + # Actually remove the admin + @procedure.administrateurs.delete(administrateur) + @administrateur = administrateur + flash.notice = "L’administrateur \« #{administrateur.email} » a été retiré de la démarche « #{@procedure.libelle} »." + rescue ActiveRecord::ActiveRecordError => e + flash.alert = e.message + end + end +end diff --git a/app/javascript/new_design/state-button.js b/app/javascript/new_design/state-button.js index c30fa64a2..01ac323cc 100644 --- a/app/javascript/new_design/state-button.js +++ b/app/javascript/new_design/state-button.js @@ -11,3 +11,8 @@ export function motivationCancel() { document.querySelectorAll('.motivation').forEach(hide); show(document.querySelector('.dropdown-items')); } + +export function showImportJustificatif(name) { + show(document.querySelector('#justificatif_motivation_import_' + name)); + hide(document.querySelector('#justificatif_motivation_suggest_' + name)); +} diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 7f61db545..ba665e4bb 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -29,7 +29,11 @@ import '../new_design/champs/repetition'; import { toggleCondidentielExplanation } from '../new_design/avis'; import { scrollMessagerie } from '../new_design/messagerie'; -import { showMotivation, motivationCancel } from '../new_design/state-button'; +import { + showMotivation, + motivationCancel, + showImportJustificatif +} from '../new_design/state-button'; import { toggleChart } from '../new_design/toggle-chart'; import { replaceSemicolonByComma } from '../new_design/avis'; @@ -40,6 +44,7 @@ const DS = { scrollMessagerie, showMotivation, motivationCancel, + showImportJustificatif, toggleChart, replaceSemicolonByComma }; diff --git a/app/javascript/shared/activestorage/ujs.js b/app/javascript/shared/activestorage/ujs.js index 43beef778..55432abf7 100644 --- a/app/javascript/shared/activestorage/ujs.js +++ b/app/javascript/shared/activestorage/ujs.js @@ -10,8 +10,12 @@ addEventListener(INITIALIZE_EVENT, ({ target, detail: { id, file } }) => { ProgressBar.init(target, id, file); }); -addEventListener(START_EVENT, ({ detail: { id } }) => { +addEventListener(START_EVENT, ({ target, detail: { id } }) => { ProgressBar.start(id); + const button = target.form.querySelector('button.primary'); + if (button) { + delete button.dataset.confirm; + } }); addEventListener(PROGRESS_EVENT, ({ detail: { id, progress } }) => { diff --git a/app/models/champs/pays_champ.rb b/app/models/champs/pays_champ.rb index d088d2880..78de3277f 100644 --- a/app/models/champs/pays_champ.rb +++ b/app/models/champs/pays_champ.rb @@ -2,4 +2,8 @@ class Champs::PaysChamp < Champs::TextChamp def self.pays ApiGeo::API.pays.pluck(:nom) end + + def self.disabled_options + pays.select { |v| (v =~ /^--.*--$/).present? } + end end diff --git a/app/models/concerns/tags_substitution_concern.rb b/app/models/concerns/tags_substitution_concern.rb index 390555775..6be66c76a 100644 --- a/app/models/concerns/tags_substitution_concern.rb +++ b/app/models/concerns/tags_substitution_concern.rb @@ -59,7 +59,13 @@ module TagsSubstitutionConcern { libelle: 'lien attestation', description: '', - lambda: -> (d) { external_link(attestation_dossier_url(d)) }, + lambda: -> (d) { + links = [external_link(attestation_dossier_url(d))] + if d.justificatif_motivation.attached? + links.push external_link("Télécharger le justificatif", url_for_justificatif_motivation(d)) + end + links.join "
\n" + }, available_for_states: [Dossier.states.fetch(:accepte)] } ] @@ -138,8 +144,14 @@ module TagsSubstitutionConcern end end - def external_link(url) - link_to(url, url, target: '_blank', rel: 'noopener') + def external_link(url, title = nil) + link_to(title || url, url, target: '_blank', rel: 'noopener') + end + + def url_for_justificatif_motivation(dossier) + if dossier.justificatif_motivation.attached? + Rails.application.routes.url_helpers.url_for(dossier.justificatif_motivation) + end end def dossier_tags diff --git a/app/models/dossier.rb b/app/models/dossier.rb index 1b8516399..39dc2c7ae 100644 --- a/app/models/dossier.rb +++ b/app/models/dossier.rb @@ -20,6 +20,8 @@ class Dossier < ApplicationRecord has_one :attestation, dependent: :destroy has_many :pieces_justificatives, dependent: :destroy + has_one_attached :justificatif_motivation + has_many :champs, -> { root.public_only.ordered }, dependent: :destroy has_many :champs_private, -> { root.private_only.ordered }, class_name: 'Champ', dependent: :destroy has_many :commentaires, dependent: :destroy @@ -296,10 +298,12 @@ class Dossier < ApplicationRecord log_dossier_operation(gestionnaire, :repasser_en_construction) end - def accepter!(gestionnaire, motivation) + def accepter!(gestionnaire, motivation, justificatif = nil) self.motivation = motivation self.en_instruction_at ||= Time.zone.now - + if justificatif + self.justificatif_motivation.attach(justificatif) + end accepte! if attestation.nil? @@ -330,20 +334,24 @@ class Dossier < ApplicationRecord DeletedDossier.create_from_dossier(self) end - def refuser!(gestionnaire, motivation) + def refuser!(gestionnaire, motivation, justificatif = nil) self.motivation = motivation self.en_instruction_at ||= Time.zone.now - + if justificatif + self.justificatif_motivation.attach(justificatif) + end refuse! NotificationMailer.send_refused_notification(self).deliver_later log_dossier_operation(gestionnaire, :refuser) end - def classer_sans_suite!(gestionnaire, motivation) + def classer_sans_suite!(gestionnaire, motivation, justificatif = nil) self.motivation = motivation self.en_instruction_at ||= Time.zone.now - + if justificatif + self.justificatif_motivation.attach(justificatif) + end sans_suite! NotificationMailer.send_without_continuation_notification(self).deliver_later diff --git a/app/models/procedure.rb b/app/models/procedure.rb index 485ca86c8..0cfe9a71d 100644 --- a/app/models/procedure.rb +++ b/app/models/procedure.rb @@ -1,6 +1,8 @@ require Rails.root.join('lib', 'percentile') class Procedure < ApplicationRecord + self.ignored_columns = [:administrateur_id] + MAX_DUREE_CONSERVATION = 36 has_many :types_de_piece_justificative, -> { ordered }, dependent: :destroy @@ -17,7 +19,7 @@ class Procedure < ApplicationRecord has_many :assign_to, dependent: :destroy has_many :administrateurs_procedures - has_many :administrateurs, through: :administrateurs_procedures + has_many :administrateurs, through: :administrateurs_procedures, after_remove: -> (procedure, _admin) { procedure.validate! } has_many :gestionnaires, through: :assign_to has_one :initiated_mail, class_name: "Mails::InitiatedMail", dependent: :destroy @@ -57,6 +59,7 @@ class Procedure < ApplicationRecord validates :libelle, presence: true, allow_blank: false, allow_nil: false validates :description, presence: true, allow_blank: false, allow_nil: false + validates :administrateurs, presence: true validate :check_juridique validates :path, format: { with: /\A[a-z0-9_\-]{3,50}\z/ }, uniqueness: { scope: :aasm_state, case_sensitive: false }, presence: true, allow_blank: false, allow_nil: true # FIXME: remove duree_conservation_required flag once all procedures are converted to the new style diff --git a/app/serializers/dossier_serializer.rb b/app/serializers/dossier_serializer.rb index 536c418cf..876b2dcaf 100644 --- a/app/serializers/dossier_serializer.rb +++ b/app/serializers/dossier_serializer.rb @@ -22,6 +22,7 @@ class DossierSerializer < ActiveModel::Serializer has_many :champs_private has_many :pieces_justificatives has_many :types_de_piece_justificative + has_one :justificatif_motivation has_many :champs, serializer: ChampSerializer @@ -52,6 +53,12 @@ class DossierSerializer < ActiveModel::Serializer PiecesJustificativesService.serialize_champs_as_pjs(object) end + def justificatif_motivation + if object.justificatif_motivation.attached? + Rails.application.routes.url_helpers.url_for(object.justificatif_motivation) + end + end + def types_de_piece_justificative ActiveModelSerializers::SerializableResource.new(object.types_de_piece_justificative).serializable_hash + PiecesJustificativesService.serialize_types_de_champ_as_type_pj(object) diff --git a/app/views/gestionnaires/dossiers/_state_button.html.haml b/app/views/gestionnaires/dossiers/_state_button.html.haml index 2c6580c6b..17986293e 100644 --- a/app/views/gestionnaires/dossiers/_state_button.html.haml +++ b/app/views/gestionnaires/dossiers/_state_button.html.haml @@ -60,6 +60,7 @@ - if dossier.motivation.present? %h4 Motivation %p.dossier-motivation= dossier.motivation + = render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: dossier } - if dossier.attestation.present? %h4 Attestation diff --git a/app/views/gestionnaires/dossiers/_state_button_motivation.html.haml b/app/views/gestionnaires/dossiers/_state_button_motivation.html.haml index 58e5a1d0f..f0310eb6a 100644 --- a/app/views/gestionnaires/dossiers/_state_button_motivation.html.haml +++ b/app/views/gestionnaires/dossiers/_state_button_motivation.html.haml @@ -28,6 +28,10 @@ %li= unspecified_annotations_privee.libelle - else = text_area :dossier, :motivation, class: 'motivation-text-area', placeholder: placeholder, required: true + .optional-justificatif{ id: "justificatif_motivation_suggest_#{popup_class}", onclick: "DS.showImportJustificatif('#{popup_class}');" } + .button Ajouter un justificatif (optionnel) + .hidden{ id: "justificatif_motivation_import_#{popup_class}" } + = file_field :dossier, :justificatif_motivation, direct_upload: true .text-right - if title == 'Accepter' && dossier.procedure.attestation_template&.activated? = link_to "Voir l'attestation", apercu_attestation_gestionnaire_dossier_path(dossier.procedure, dossier), target: '_blank', rel: 'noopener', class: 'button', title: "Voir l'attestation qui sera envoyée au demandeur" diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index af74cd8c7..5a0486f44 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -19,7 +19,7 @@ = stylesheet_link_tag 'new_design/new_application', media: 'all', 'data-turbolinks-track': 'reload' = stylesheet_link_tag 'new_design/print', media: 'print', 'data-turbolinks-track': 'reload' - = Gon::Base.render_data(camel_case: true, init: true) + = Gon::Base.render_data(camel_case: true, init: true, nonce: request.content_security_policy_nonce) - if Rails.env.development? = stylesheet_link_tag :xray diff --git a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml index 3fa249331..9a139b589 100644 --- a/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml +++ b/app/views/layouts/left_panels/_left_panel_admin_procedurescontroller_navbar.html.haml @@ -27,6 +27,10 @@ - if @procedure.missing_steps.include?(:service) %p.missing-steps (à compléter) + %a#onglet-administrateurs{ href: url_for(procedure_administrateurs_path(@procedure)) } + .procedure-list-element{ class: ('active' if active == 'Administrateurs') } + Administrateurs + %a#onglet-instructeurs{ href: url_for(admin_procedure_instructeurs_path(@procedure)) } .procedure-list-element{ class: ('active' if active == 'Instructeurs') } Instructeurs diff --git a/app/views/new_administrateur/procedure_administrateurs/_administrateur.html.haml b/app/views/new_administrateur/procedure_administrateurs/_administrateur.html.haml new file mode 100644 index 000000000..65354f432 --- /dev/null +++ b/app/views/new_administrateur/procedure_administrateurs/_administrateur.html.haml @@ -0,0 +1,13 @@ +%tr{ id: "procedure-#{@procedure.id}-administrateur-#{administrateur.id}" } + %td= administrateur.email + %td= administrateur.created_at.strftime('%d/%m/%Y %H:%M') + %td= administrateur.registration_state + %td + - if administrateur == current_administrateur + C’est vous ! + - else + = link_to 'Retirer', + [@procedure, administrateur], + method: :delete, + 'data-confirm': "Retirer « #{administrateur.email} » des administrateurs de « #{@procedure.libelle} » ?", + remote: true diff --git a/app/views/new_administrateur/procedure_administrateurs/create.js.haml b/app/views/new_administrateur/procedure_administrateurs/create.js.haml new file mode 100644 index 000000000..9e4ff843c --- /dev/null +++ b/app/views/new_administrateur/procedure_administrateurs/create.js.haml @@ -0,0 +1,6 @@ += render_flash(sticky: true) +- if @administrateur + = append_to_element("#procedure-#{@procedure.id}-administrateurs", + partial: 'administrateur', + locals: { administrateur: @administrateur }) + document.getElementById('procedure-#{@procedure.id}-new_administrateur').reset() diff --git a/app/views/new_administrateur/procedure_administrateurs/destroy.js.haml b/app/views/new_administrateur/procedure_administrateurs/destroy.js.haml new file mode 100644 index 000000000..b56486490 --- /dev/null +++ b/app/views/new_administrateur/procedure_administrateurs/destroy.js.haml @@ -0,0 +1,4 @@ += render_flash(sticky: true) +- if @administrateur + = remove_element("#procedure-#{@procedure.id}-administrateur-#{@administrateur.id}") + diff --git a/app/views/new_administrateur/procedure_administrateurs/index.html.haml b/app/views/new_administrateur/procedure_administrateurs/index.html.haml new file mode 100644 index 000000000..ea54e04c5 --- /dev/null +++ b/app/views/new_administrateur/procedure_administrateurs/index.html.haml @@ -0,0 +1,26 @@ += render partial: 'new_administrateur/breadcrumbs', + locals: { steps: [link_to('Démarches', admin_procedures_path), + link_to(@procedure.libelle, admin_procedure_path(@procedure)), + 'Administrateurs'], preview: false } + +.container + %h1 Administrateurs de « #{@procedure.libelle} » + %table.table + %thead + %th= 'Adresse email' + %th= 'Enregistré le' + %th= 'État' + %tbody{ id: "procedure-#{@procedure.id}-administrateurs" } + = render partial: 'administrateur', collection: @procedure.administrateurs.order(:email) + %tfoot + %tr + %th{ colspan: 4 } + = form_for @procedure.administrateurs.new, + url: { controller: 'procedure_administrateurs' }, + html: { class: 'form', id: "procedure-#{@procedure.id}-new_administrateur" } , + remote: true do |f| + = f.label :email do + Ajouter un administrateur + %span.notice= "Renseignez l’email d’un administrateur déjà enregistré sur demarches-simplifiees.fr pour lui permettre de modifier « #{@procedure.libelle} »." + = f.email_field :email, placeholder: 'marie.dupont@exemple.fr', required: true + = f.submit 'Ajouter comme administrateur', class: 'button primary send' diff --git a/app/views/new_user/dossiers/show/_download_justificatif.html.haml b/app/views/new_user/dossiers/show/_download_justificatif.html.haml new file mode 100644 index 000000000..509c594e0 --- /dev/null +++ b/app/views/new_user/dossiers/show/_download_justificatif.html.haml @@ -0,0 +1,7 @@ +- if dossier.present? + - justificatif = dossier.justificatif_motivation + - if dossier.justificatif_motivation.attached? and dossier.justificatif_motivation.virus_scanner.safe? + .action + = link_to (justificatif), target: '_blank', class: 'button primary' do + %span.icon.download + Télécharger le justificatif diff --git a/app/views/shared/dossiers/_demande.html.haml b/app/views/shared/dossiers/_demande.html.haml index 8eef11616..78ff2fb67 100644 --- a/app/views/shared/dossiers/_demande.html.haml +++ b/app/views/shared/dossiers/_demande.html.haml @@ -1,7 +1,7 @@ .container - if dossier.en_construction_at.present? .card - = render partial: "shared/dossiers/horodatage", locals: { dossier: dossier } + = render partial: "shared/dossiers/infos_generales", locals: { dossier: dossier } .tab-title Identité du demandeur .card diff --git a/app/views/shared/dossiers/_horodatage.html.haml b/app/views/shared/dossiers/_horodatage.html.haml deleted file mode 100644 index c97b9c601..000000000 --- a/app/views/shared/dossiers/_horodatage.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%table.table.vertical.dossier-champs - %tbody - %tr - %th.libelle Déposé le : - %td= l(dossier.en_construction_at, format: '%d %B %Y') diff --git a/app/views/shared/dossiers/_infos_generales.html.haml b/app/views/shared/dossiers/_infos_generales.html.haml new file mode 100644 index 000000000..9d2748899 --- /dev/null +++ b/app/views/shared/dossiers/_infos_generales.html.haml @@ -0,0 +1,11 @@ +%table.table.vertical.dossier-champs + %tbody + %tr + %th.libelle Déposé le : + %td= l(dossier.en_construction_at, format: '%d %B %Y') + - if dossier.justificatif_motivation.attached? + %tr + %th.libelle Justificatif : + %td + .action + = render partial: 'shared/piece_jointe/pj_link', locals: { object: dossier, pj: dossier.justificatif_motivation } diff --git a/app/views/shared/dossiers/editable_champs/_pays.html.haml b/app/views/shared/dossiers/editable_champs/_pays.html.haml index af0ee8b74..13062d8e5 100644 --- a/app/views/shared/dossiers/editable_champs/_pays.html.haml +++ b/app/views/shared/dossiers/editable_champs/_pays.html.haml @@ -1,4 +1,5 @@ = form.select :value, Champs::PaysChamp.pays, + disabled: Champs::PaysChamp.disabled_options, include_blank: true, required: champ.mandatory? diff --git a/app/views/users/dossiers/show/_status_overview.html.haml b/app/views/users/dossiers/show/_status_overview.html.haml index ceb07234c..e2d16b5db 100644 --- a/app/views/users/dossiers/show/_status_overview.html.haml +++ b/app/views/users/dossiers/show/_status_overview.html.haml @@ -50,12 +50,15 @@ %h3 Motif de l’acceptation %blockquote= dossier.motivation + = render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: dossier } + - if dossier.attestation.present? .action = link_to attestation_dossier_path(dossier), target: '_blank', rel: 'noopener', class: 'button primary' do %span.icon.download Télécharger l’attestation + - elsif dossier.refuse? .refuse %p.decision @@ -68,6 +71,7 @@ %h3 Motif du refus %blockquote= dossier.motivation + = render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: @dossier } .action = link_to 'Envoyer un message à l’administration', messagerie_dossier_url(dossier, anchor: 'new_commentaire'), class: 'button' @@ -79,6 +83,8 @@ = succeed '.' do %strong sans suite + = render partial: 'new_user/dossiers/show/download_justificatif', locals: { dossier: @dossier } + - if dossier.motivation.present? %h3 Motif du classement sans suite %blockquote= dossier.motivation diff --git a/config/environments/development.rb b/config/environments/development.rb index 9a178ab8c..1d51b0cd3 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -59,6 +59,9 @@ Rails.application.configure do port: 3000 } + # Use Content-Security-Policy-Report-Only instead of Content-Security-Policy + config.content_security_policy_report_only = true + # Raises error for missing translations # config.action_view.raise_on_missing_translations = true diff --git a/config/environments/production.rb b/config/environments/production.rb index f2474f93d..befbe17aa 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -109,5 +109,7 @@ Rails.application.configure do host: ENV['APP_HOST'] } + config.content_security_policy_report_only = true + config.lograge.enabled = ENV['LOGRAGE_ENABLED'] == 'enabled' end diff --git a/config/environments/test.rb b/config/environments/test.rb index 1b9f30460..2a1fb8e0f 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -45,6 +45,9 @@ Rails.application.configure do protocol: :http } + # Use Content-Security-Policy-Report-Only instead of Content-Security-Policy + config.content_security_policy_report_only = true + config.active_job.queue_adapter = :test config.active_storage.service = :test diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 000000000..b58d8af0e --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,23 @@ +Rails.application.config.content_security_policy do |policy| + # En cas de non respect d'une des règles, faire un POST sur cette URL + policy.report_uri "https://e30e0ed9c14194254481124271b34a72.report-uri.com/r/d/csp/reportOnly" + # Whitelist image + policy.img_src :self, "https://*.openstreetmap.org" + # Whitelist JS: nous, sendinblue et matomo, et… miniprofiler :( + if Rails.env.development? + # https://github.com/MiniProfiler/rack-mini-profiler/issues/327 + policy.script_src :self, "https://sibautomation.com", "//stats.data.gouv.fr", :unsafe_eval, :unsafe_inline + else + policy.script_src :self, "https://sibautomation.com", "//stats.data.gouv.fr" + end + # Génération d'un nonce pour les balises script inline qu'on maitrise (Gon) + Rails.application.config.content_security_policy_nonce_generator = -> _request { SecureRandom.base64(16) } + + # Pour les CSS, on a beaucoup de style inline et quelques balises