diff --git a/Gemfile b/Gemfile index e210be466..24f8c36c9 100644 --- a/Gemfile +++ b/Gemfile @@ -59,7 +59,6 @@ gem 'openid_connect' gem 'pg' gem 'phonelib' gem 'prawn-rails' # PDF Generation -gem 'prawn-svg' gem 'premailer-rails' gem 'puma' # Use Puma as the app server gem 'pundit' diff --git a/Gemfile.lock b/Gemfile.lock index b91f5e62a..0faaa9859 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -431,7 +431,7 @@ GEM ruby2_keywords (~> 0.0.1) netrc (0.11.0) nio4r (2.5.8) - nokogiri (1.13.3) + nokogiri (1.13.4) mini_portile2 (~> 2.8.0) racc (~> 1.4) open4 (1.3.4) @@ -459,9 +459,6 @@ GEM prawn prawn-table rails (>= 3.1.0) - prawn-svg (0.31.0) - css_parser (~> 1.6) - prawn (>= 0.11.1, < 3) prawn-table (0.2.2) prawn (>= 1.3.0, < 3.0.0) premailer (1.14.2) @@ -840,7 +837,6 @@ DEPENDENCIES pg phonelib prawn-rails - prawn-svg premailer-rails pry-byebug puma diff --git a/app/assets/images/header/logo-ds-wide.png b/app/assets/images/header/logo-ds-wide.png new file mode 100644 index 000000000..a0ac0f353 Binary files /dev/null and b/app/assets/images/header/logo-ds-wide.png differ diff --git a/app/controllers/agent_connect/agent_controller.rb b/app/controllers/agent_connect/agent_controller.rb index c550284e4..18a781235 100644 --- a/app/controllers/agent_connect/agent_controller.rb +++ b/app/controllers/agent_connect/agent_controller.rb @@ -1,16 +1,26 @@ # doc: https://github.com/france-connect/Documentation-AgentConnect class AgentConnect::AgentController < ApplicationController before_action :redirect_to_login_if_fc_aborted, only: [:callback] + before_action :check_state, only: [:callback] + + STATE_COOKIE_NAME = :agentConnect_state + NONCE_COOKIE_NAME = :agentConnect_nonce def index end def login - redirect_to AgentConnectService.authorization_uri + uri, state, nonce = AgentConnectService.authorization_uri + + cookies.encrypted[STATE_COOKIE_NAME] = state + cookies.encrypted[NONCE_COOKIE_NAME] = nonce + + redirect_to uri end def callback - user_info = AgentConnectService.user_info(params[:code]) + user_info = AgentConnectService.user_info(params[:code], cookies.encrypted[NONCE_COOKIE_NAME]) + cookies.encrypted[NONCE_COOKIE_NAME] = nil instructeur = Instructeur.find_by(agent_connect_id: user_info['sub']) @@ -50,4 +60,13 @@ class AgentConnect::AgentController < ApplicationController flash.alert = t('errors.messages.france_connect.connexion') redirect_to(new_user_session_path) end + + def check_state + if cookies.encrypted[STATE_COOKIE_NAME] != params[:state] + flash.alert = t('errors.messages.france_connect.connexion') + redirect_to(new_user_session_path) + else + cookies.encrypted[STATE_COOKIE_NAME] = nil + end + end end diff --git a/app/graphql/api/v2/context.rb b/app/graphql/api/v2/context.rb index 080170698..26151a80e 100644 --- a/app/graphql/api/v2/context.rb +++ b/app/graphql/api/v2/context.rb @@ -1,13 +1,14 @@ class API::V2::Context < GraphQL::Query::Context - def has_fragment?(name) - if self["has_fragment_#{name}"] - true - else - visitor = HasFragment.new(self.query.selected_operation, name) + # This method is used to check if a given fragment is used in the given query. + # We need that in order to maintain backward compatibility for Types de Champ + # that we extended in later iterations of our schema. + def has_fragment?(fragment_name) + self[:has_fragment] ||= Hash.new do |hash, fragment_name| + visitor = HasFragment.new(query.document, fragment_name) visitor.visit - self["has_fragment_#{name}"] = visitor.found - self["has_fragment_#{name}"] + hash[fragment_name] = visitor.found end + self[:has_fragment][fragment_name] end def internal_use? @@ -36,17 +37,28 @@ class API::V2::Context < GraphQL::Query::Context self[:authorized][demarche.id] end + # This is a query AST visitor that we use to check + # if a fragment with a given name is used in the given document. + # We check for both inline and standalone fragments. class HasFragment < GraphQL::Language::Visitor - def initialize(document, name) + def initialize(document, fragment_name) super(document) - @name = name.to_s + @fragment_name = fragment_name.to_s @found = false end attr_reader :found def on_inline_fragment(node, parent) - if node.type.name == @name + if node.type.name == @fragment_name + @found = true + end + + super + end + + def on_fragment_definition(node, parent) + if node.type.name == @fragment_name @found = true end diff --git a/app/javascript/components/TypesDeChampEditor/Flash.js b/app/javascript/components/TypesDeChampEditor/Flash.ts similarity index 61% rename from app/javascript/components/TypesDeChampEditor/Flash.js rename to app/javascript/components/TypesDeChampEditor/Flash.ts index 945c6efd8..302c88c9f 100644 --- a/app/javascript/components/TypesDeChampEditor/Flash.js +++ b/app/javascript/components/TypesDeChampEditor/Flash.ts @@ -1,6 +1,14 @@ -export default class Flash { - constructor(isAnnotation) { - this.element = document.querySelector('#flash_messages'); +import invariant from 'tiny-invariant'; + +export class Flash { + element: HTMLDivElement; + isAnnotation: boolean; + timeout?: number; + + constructor(isAnnotation: boolean) { + const element = document.querySelector('#flash_messages'); + invariant(element, 'Flash element is required'); + this.element = element; this.isAnnotation = isAnnotation; } success() { @@ -10,13 +18,13 @@ export default class Flash { this.add('Formulaire enregistré.'); } } - error(message) { + error(message: string) { this.add(message, true); } clear() { this.element.innerHTML = ''; } - add(message, isError) { + add(message: string, isError = false) { const html = `
@@ -20,10 +27,3 @@ function DescriptionInput({ isVisible, handler }) { } return null; } - -DescriptionInput.propTypes = { - isVisible: PropTypes.bool, - handler: PropTypes.object -}; - -export default DescriptionInput; diff --git a/app/javascript/components/TypesDeChampEditor/components/LibelleInput.jsx b/app/javascript/components/TypesDeChampEditor/components/LibelleInput.tsx similarity index 66% rename from app/javascript/components/TypesDeChampEditor/components/LibelleInput.jsx rename to app/javascript/components/TypesDeChampEditor/components/LibelleInput.tsx index f1ca7c07c..c7490cec6 100644 --- a/app/javascript/components/TypesDeChampEditor/components/LibelleInput.jsx +++ b/app/javascript/components/TypesDeChampEditor/components/LibelleInput.tsx @@ -1,7 +1,14 @@ import React from 'react'; -import PropTypes from 'prop-types'; -function LibelleInput({ isVisible, handler }) { +import type { Handler } from '../types'; + +export function LibelleInput({ + isVisible, + handler +}: { + isVisible: boolean; + handler: Handler; +}) { if (isVisible) { return (
@@ -19,10 +26,3 @@ function LibelleInput({ isVisible, handler }) { } return null; } - -LibelleInput.propTypes = { - handler: PropTypes.object, - isVisible: PropTypes.bool -}; - -export default LibelleInput; diff --git a/app/javascript/components/TypesDeChampEditor/components/MandatoryInput.jsx b/app/javascript/components/TypesDeChampEditor/components/MandatoryInput.tsx similarity index 66% rename from app/javascript/components/TypesDeChampEditor/components/MandatoryInput.jsx rename to app/javascript/components/TypesDeChampEditor/components/MandatoryInput.tsx index 436a23345..9a13cc161 100644 --- a/app/javascript/components/TypesDeChampEditor/components/MandatoryInput.jsx +++ b/app/javascript/components/TypesDeChampEditor/components/MandatoryInput.tsx @@ -1,7 +1,14 @@ import React from 'react'; -import PropTypes from 'prop-types'; -function MandatoryInput({ isVisible, handler }) { +import type { Handler } from '../types'; + +export function MandatoryInput({ + isVisible, + handler +}: { + isVisible: boolean; + handler: Handler; +}) { if (isVisible) { return (
@@ -19,10 +26,3 @@ function MandatoryInput({ isVisible, handler }) { } return null; } - -MandatoryInput.propTypes = { - handler: PropTypes.object, - isVisible: PropTypes.bool -}; - -export default MandatoryInput; diff --git a/app/javascript/components/TypesDeChampEditor/components/MoveButton.jsx b/app/javascript/components/TypesDeChampEditor/components/MoveButton.tsx similarity index 56% rename from app/javascript/components/TypesDeChampEditor/components/MoveButton.jsx rename to app/javascript/components/TypesDeChampEditor/components/MoveButton.tsx index c8ec442f9..15796767a 100644 --- a/app/javascript/components/TypesDeChampEditor/components/MoveButton.jsx +++ b/app/javascript/components/TypesDeChampEditor/components/MoveButton.tsx @@ -1,8 +1,17 @@ -import React from 'react'; -import PropTypes from 'prop-types'; +import React, { MouseEventHandler } from 'react'; import { ArrowDownIcon, ArrowUpIcon } from '@heroicons/react/solid'; -function MoveButton({ isEnabled, icon, title, onClick }) { +export function MoveButton({ + isEnabled, + icon, + title, + onClick +}: { + isEnabled: boolean; + icon: string; + title: string; + onClick: MouseEventHandler; +}) { return (