diff --git a/app/assets/stylesheets/new_design/direct_uploads.scss b/app/assets/stylesheets/new_design/direct_uploads.scss
index eda8e75c7..febde1bd0 100644
--- a/app/assets/stylesheets/new_design/direct_uploads.scss
+++ b/app/assets/stylesheets/new_design/direct_uploads.scss
@@ -1,12 +1,13 @@
+@import "colors";
+
.direct-upload {
display: inline-block;
position: relative;
- padding: 2px 4px;
+ padding: 4px 15px;
margin: 0 3px 3px 0;
- border: 1px solid rgba(0, 0, 0, 0.3);
+ border: 1px solid $border-grey;
border-radius: 3px;
- font-size: 11px;
- line-height: 13px;
+ font-size: 14px;
}
.direct-upload--pending {
diff --git a/app/assets/stylesheets/new_design/forms.scss b/app/assets/stylesheets/new_design/forms.scss
index e2202ecad..0e6954664 100644
--- a/app/assets/stylesheets/new_design/forms.scss
+++ b/app/assets/stylesheets/new_design/forms.scss
@@ -111,6 +111,10 @@
}
}
+ .direct-upload {
+ margin-bottom: 2 * $default-padding;
+ }
+
.add-row {
margin-bottom: 2 * $default-padding;
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 3b1243d20..4bcaa901a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,4 +1,6 @@
class ApplicationController < ActionController::Base
+ include TrustedDeviceConcern
+
MAINTENANCE_MESSAGE = 'Le site est actuellement en maintenance. Il sera à nouveau disponible dans un court instant.'
# Prevent CSRF attacks by raising an exception.
@@ -6,6 +8,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception, if: -> { !Rails.env.test? }
before_action :load_navbar_left_pannel_partial_url
before_action :set_raven_context
+ before_action :redirect_if_untrusted
before_action :authorize_request_for_profiler
before_action :reject, if: -> { Flipflop.maintenance_mode? }
@@ -151,4 +154,34 @@ class ApplicationController < ActionController::Base
redirect_to root_path
end
end
+
+ def redirect_if_untrusted
+ if gestionnaire_signed_in? &&
+ sensitive_path &&
+ current_gestionnaire.feature_enabled?(:enable_email_login_token) &&
+ !trusted_device?
+
+ # return at this location
+ # after the device is trusted
+ store_location_for(:user, request.fullpath)
+
+ send_login_token_or_bufferize(current_gestionnaire)
+ redirect_to link_sent_path(email: current_gestionnaire.email)
+ end
+ end
+
+ def sensitive_path
+ path = request.path_info
+
+ if path == '/' ||
+ path == '/users/sign_out' ||
+ path.start_with?('/connexion-par-jeton') ||
+ path.start_with?('/api/') ||
+ path.start_with?('/lien-envoye')
+
+ false
+ else
+ true
+ end
+ end
end
diff --git a/app/controllers/manager/dossiers_controller.rb b/app/controllers/manager/dossiers_controller.rb
index 72858f2a0..4407d2323 100644
--- a/app/controllers/manager/dossiers_controller.rb
+++ b/app/controllers/manager/dossiers_controller.rb
@@ -29,6 +29,17 @@ module Manager
redirect_to manager_dossier_path(dossier)
end
+ def hide
+ dossier = Dossier.find(params[:id])
+ deleted_dossier = dossier.hide!(current_administration)
+
+ DossierMailer.notify_deletion_to_user(deleted_dossier, dossier.user.email).deliver_later
+ logger.info("Le dossier #{dossier.id} est supprimé par #{current_administration.email}")
+ flash[:notice] = "Le dossier #{dossier.id} est supprimé"
+
+ redirect_to manager_dossier_path(dossier)
+ end
+
private
def unfiltered_list?
diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb
index ee94aed4a..783043bcf 100644
--- a/app/controllers/users/sessions_controller.rb
+++ b/app/controllers/users/sessions_controller.rb
@@ -23,20 +23,7 @@ class Users::SessionsController < Sessions::SessionsController
current_user.update(loged_in_with_france_connect: nil)
end
- if gestionnaire_signed_in?
- if trusted_device? || !current_gestionnaire.feature_enabled?(:enable_email_login_token)
- set_flash_message :notice, :signed_in
- redirect_to after_sign_in_path_for(:user)
- else
- gestionnaire = current_gestionnaire
-
- send_login_token_or_bufferize(gestionnaire)
-
- [:user, :gestionnaire, :administrateur].each { |role| sign_out(role) }
-
- redirect_to link_sent_path(email: gestionnaire.email)
- end
- elsif user_signed_in?
+ if gestionnaire_signed_in? || user_signed_in?
set_flash_message :notice, :signed_in
redirect_to after_sign_in_path_for(:user)
else
@@ -83,32 +70,35 @@ class Users::SessionsController < Sessions::SessionsController
def sign_in_by_link
gestionnaire = Gestionnaire.find(params[:id])
- if gestionnaire&.login_token_valid?(params[:jeton])
- trust_device
- flash.notice = "Merci d’avoir confirmé votre connexion. Votre navigateur est maintenant authentifié pour #{TRUSTED_DEVICE_PERIOD.to_i / ActiveSupport::Duration::SECONDS_PER_DAY} jours."
+ trusted_device_token = gestionnaire
+ .trusted_device_tokens
+ .find_by(token: params[:jeton])
- user = User.find_by(email: gestionnaire.email)
- administrateur = Administrateur.find_by(email: gestionnaire.email)
- [user, gestionnaire, administrateur].compact.each { |resource| sign_in(resource) }
+ if trusted_device_token&.token_valid?
+ trust_device(trusted_device_token.created_at)
+
+ period = ((trusted_device_token.created_at + TRUSTED_DEVICE_PERIOD) - Time.zone.now).to_i / ActiveSupport::Duration::SECONDS_PER_DAY
+
+ flash.notice = "Merci d’avoir confirmé votre connexion. Votre navigateur est maintenant authentifié pour #{period} jours."
# redirect to procedure'url if stored by store_location_for(:user) in dossiers_controller
# redirect to root_path otherwise
- redirect_to after_sign_in_path_for(:user)
+
+ if gestionnaire_signed_in?
+ redirect_to after_sign_in_path_for(:user)
+ else
+ redirect_to new_user_session_path
+ end
else
- flash[:alert] = 'Votre lien est invalide ou expiré, veuillez-vous reconnecter.'
- redirect_to new_user_session_path
+ flash[:alert] = 'Votre lien est invalide ou expiré, un nouveau vient de vous être envoyé.'
+
+ send_login_token_or_bufferize(gestionnaire)
+ redirect_to link_sent_path(email: gestionnaire.email)
end
end
private
- def send_login_token_or_bufferize(gestionnaire)
- if !gestionnaire.young_login_token?
- login_token = gestionnaire.login_token!
- GestionnaireMailer.send_login_token(gestionnaire, login_token).deliver_later
- end
- end
-
def try_to_authenticate(klass, remember_me = false)
resource = klass.find_for_database_authentication(email: params[:user][:email])
diff --git a/app/fields/procedure_link_field.rb b/app/fields/procedure_link_field.rb
index a8669a15c..2862bab0e 100644
--- a/app/fields/procedure_link_field.rb
+++ b/app/fields/procedure_link_field.rb
@@ -1,6 +1,6 @@
require "administrate/field/base"
-class ProcedureLinkField < Administrate::Field::Base
+class ProcedureLinkField < Administrate::Field::String
def name
"Lien démarche"
end
diff --git a/app/javascript/new_design/administrateur/DraggableItem.js b/app/javascript/new_design/administrateur/DraggableItem.js
index ea6c1361b..df94177a5 100644
--- a/app/javascript/new_design/administrateur/DraggableItem.js
+++ b/app/javascript/new_design/administrateur/DraggableItem.js
@@ -1,5 +1,5 @@
import { getJSON, debounce } from '@utils';
-import { DirectUpload } from 'activestorage';
+import Uploader from '../../shared/activestorage/uploader';
export default {
props: ['state', 'index', 'item'],
@@ -181,7 +181,12 @@ export default {
const file = input.files[0];
if (file) {
this.isUploading = true;
- uploadFile(this.state.directUploadUrl, file).then(({ signed_id }) => {
+ const controller = new Uploader(
+ input,
+ file,
+ this.state.directUploadUrl
+ );
+ controller.start().then(signed_id => {
this.pieceJustificativeTemplate = signed_id;
this.isUploading = false;
this.debouncedSave();
@@ -247,17 +252,3 @@ const EXCLUDE_FROM_REPETITION = [
function castBoolean(value) {
return value && value != 0;
}
-
-function uploadFile(directUploadUrl, file) {
- const upload = new DirectUpload(file, directUploadUrl);
-
- return new Promise((resolve, reject) => {
- upload.create((error, blob) => {
- if (error) {
- reject(error);
- } else {
- resolve(blob);
- }
- });
- });
-}
diff --git a/app/javascript/packs/application-old.js b/app/javascript/packs/application-old.js
index 4a984df96..4e327c228 100644
--- a/app/javascript/packs/application-old.js
+++ b/app/javascript/packs/application-old.js
@@ -4,7 +4,7 @@ import Rails from 'rails-ujs';
import * as ActiveStorage from 'activestorage';
import jQuery from 'jquery';
-import '../shared/activestorage/progress';
+import '../shared/activestorage/ujs';
import '../shared/sentry';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround';
diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js
index 128b7c1b1..df838bc5a 100644
--- a/app/javascript/packs/application.js
+++ b/app/javascript/packs/application.js
@@ -5,7 +5,7 @@ import * as ActiveStorage from 'activestorage';
import Chartkick from 'chartkick';
import Highcharts from 'highcharts';
-import '../shared/activestorage/progress';
+import '../shared/activestorage/ujs';
import '../shared/sentry';
import '../shared/rails-ujs-fix';
import '../shared/safari-11-file-xhr-workaround';
diff --git a/app/javascript/shared/activestorage/progress-bar.js b/app/javascript/shared/activestorage/progress-bar.js
new file mode 100644
index 000000000..e08df104d
--- /dev/null
+++ b/app/javascript/shared/activestorage/progress-bar.js
@@ -0,0 +1,92 @@
+const PENDING_CLASS = 'direct-upload--pending';
+const ERROR_CLASS = 'direct-upload--error';
+const COMPLETE_CLASS = 'direct-upload--complete';
+
+/**
+ ProgressBar is and utility class responsible for
+ rendering upload progress bar. It is used to handle
+ direct-upload form ujs events but also in the
+ Uploader delegate used with uploads on json api.
+ */
+export default class ProgressBar {
+ static init(input, id, file) {
+ clearErrors(input);
+ const html = this.render(id, file.name);
+ input.insertAdjacentHTML('beforebegin', html);
+ }
+
+ static start(id) {
+ const element = getDirectUploadElement(id);
+
+ element.classList.remove(PENDING_CLASS);
+ }
+
+ static progress(id, progress) {
+ const element = getDirectUploadProgressElement(id);
+
+ element.style.width = `${progress}%`;
+ }
+
+ static error(id, error) {
+ const element = getDirectUploadElement(id);
+
+ element.classList.add(ERROR_CLASS);
+ element.setAttribute('title', error);
+ }
+
+ static end(id) {
+ const element = getDirectUploadElement(id);
+
+ element.classList.add(COMPLETE_CLASS);
+ }
+
+ static render(id, filename) {
+ return `
`;
+ }
+
+ constructor(input, id, file) {
+ this.constructor.init(input, id, file);
+ this.id = id;
+ }
+
+ start() {
+ this.constructor.start(this.id);
+ }
+
+ progress(progress) {
+ this.constructor.progress(this.id, progress);
+ }
+
+ error(error) {
+ this.constructor.error(this.id, error);
+ }
+
+ end() {
+ this.constructor.end(this.id);
+ }
+
+ destroy() {
+ const element = getDirectUploadElement(this.id);
+ element.remove();
+ }
+}
+
+function clearErrors(input) {
+ const errorElements = input.parentElement.querySelectorAll(`.${ERROR_CLASS}`);
+ for (let element of errorElements) {
+ element.remove();
+ }
+}
+
+function getDirectUploadElement(id) {
+ return document.getElementById(`direct-upload-${id}`);
+}
+
+function getDirectUploadProgressElement(id) {
+ return document.querySelector(
+ `#direct-upload-${id} .direct-upload__progress`
+ );
+}
diff --git a/app/javascript/shared/activestorage/progress.js b/app/javascript/shared/activestorage/progress.js
deleted file mode 100644
index efaf9349c..000000000
--- a/app/javascript/shared/activestorage/progress.js
+++ /dev/null
@@ -1,49 +0,0 @@
-addEventListener('direct-upload:initialize', event => {
- const target = event.target,
- detail = event.detail,
- id = detail.id,
- file = detail.file;
-
- target.insertAdjacentHTML(
- 'beforebegin',
- '\n\n
\n
' +
- file.name +
- '\n
\n'
- );
-});
-
-addEventListener('direct-upload:start', event => {
- const id = event.detail.id,
- element = document.getElementById('direct-upload-' + id);
-
- element.classList.remove('direct-upload--pending');
-});
-
-addEventListener('direct-upload:progress', event => {
- const id = event.detail.id,
- progress = event.detail.progress,
- progressElement = document.getElementById('direct-upload-progress-' + id);
-
- progressElement.style.width = `${progress} %`;
-});
-
-addEventListener('direct-upload:error', event => {
- event.preventDefault();
- const id = event.detail.id,
- error = event.detail.error,
- element = document.getElementById('direct-upload-' + id);
-
- element.classList.add('direct-upload--error');
- element.setAttribute('title', error);
-});
-
-addEventListener('direct-upload:end', event => {
- const id = event.detail.id,
- element = document.getElementById('direct-upload-' + id);
-
- element.classList.add('direct-upload--complete');
-});
diff --git a/app/javascript/shared/activestorage/ujs.js b/app/javascript/shared/activestorage/ujs.js
new file mode 100644
index 000000000..43beef778
--- /dev/null
+++ b/app/javascript/shared/activestorage/ujs.js
@@ -0,0 +1,27 @@
+import ProgressBar from './progress-bar';
+
+const INITIALIZE_EVENT = 'direct-upload:initialize';
+const START_EVENT = 'direct-upload:start';
+const PROGRESS_EVENT = 'direct-upload:progress';
+const ERROR_EVENT = 'direct-upload:error';
+const END_EVENT = 'direct-upload:end';
+
+addEventListener(INITIALIZE_EVENT, ({ target, detail: { id, file } }) => {
+ ProgressBar.init(target, id, file);
+});
+
+addEventListener(START_EVENT, ({ detail: { id } }) => {
+ ProgressBar.start(id);
+});
+
+addEventListener(PROGRESS_EVENT, ({ detail: { id, progress } }) => {
+ ProgressBar.progress(id, progress);
+});
+
+addEventListener(ERROR_EVENT, ({ detail: { id, error } }) => {
+ ProgressBar.error(id, error);
+});
+
+addEventListener(END_EVENT, ({ detail: { id } }) => {
+ ProgressBar.end(id);
+});
diff --git a/app/javascript/shared/activestorage/uploader.js b/app/javascript/shared/activestorage/uploader.js
new file mode 100644
index 000000000..e2ebf5b6e
--- /dev/null
+++ b/app/javascript/shared/activestorage/uploader.js
@@ -0,0 +1,43 @@
+import { DirectUpload } from 'activestorage';
+import ProgressBar from './progress-bar';
+
+/**
+ Uploader class is a delegate for DirectUpload instance
+ used to track lifecycle and progress of un upload.
+ */
+export default class Uploader {
+ constructor(input, file, directUploadUrl) {
+ this.directUpload = new DirectUpload(file, directUploadUrl, this);
+ this.progressBar = new ProgressBar(input, this.directUpload.id, file);
+ }
+
+ start() {
+ this.progressBar.start();
+
+ return new Promise((resolve, reject) => {
+ this.directUpload.create((error, attributes) => {
+ if (error) {
+ this.progressBar.error(error);
+ reject(error);
+ } else {
+ resolve(attributes.signed_id);
+ }
+ this.progressBar.end();
+ this.progressBar.destroy();
+ });
+ });
+ }
+
+ uploadRequestDidProgress(event) {
+ const progress = (event.loaded / event.total) * 100;
+ if (progress) {
+ this.progressBar.progress(progress);
+ }
+ }
+
+ directUploadWillStoreFileWithXHR(xhr) {
+ xhr.upload.addEventListener('progress', event =>
+ this.uploadRequestDidProgress(event)
+ );
+ }
+}
diff --git a/app/models/concerns/trusted_device_concern.rb b/app/models/concerns/trusted_device_concern.rb
index 9928adfa1..f7dfb8e92 100644
--- a/app/models/concerns/trusted_device_concern.rb
+++ b/app/models/concerns/trusted_device_concern.rb
@@ -4,10 +4,10 @@ module TrustedDeviceConcern
TRUSTED_DEVICE_COOKIE_NAME = :trusted_device
TRUSTED_DEVICE_PERIOD = 1.month
- def trust_device
+ def trust_device(start_at)
cookies.encrypted[TRUSTED_DEVICE_COOKIE_NAME] = {
- value: JSON.generate({ created_at: Time.zone.now }),
- expires: TRUSTED_DEVICE_PERIOD,
+ value: JSON.generate({ created_at: start_at }),
+ expires: start_at + TRUSTED_DEVICE_PERIOD,
httponly: true
}
end
@@ -17,6 +17,13 @@ module TrustedDeviceConcern
(Time.zone.now - TRUSTED_DEVICE_PERIOD) < trusted_device_cookie_created_at
end
+ def send_login_token_or_bufferize(gestionnaire)
+ if !gestionnaire.young_login_token?
+ login_token = gestionnaire.create_trusted_device_token
+ GestionnaireMailer.send_login_token(gestionnaire, login_token).deliver_later
+ end
+ end
+
private
def trusted_device_cookie_created_at
diff --git a/app/models/deleted_dossier.rb b/app/models/deleted_dossier.rb
index 31cffa793..9fba98749 100644
--- a/app/models/deleted_dossier.rb
+++ b/app/models/deleted_dossier.rb
@@ -1,3 +1,7 @@
class DeletedDossier < ApplicationRecord
belongs_to :procedure
+
+ def self.create_from_dossier(dossier)
+ DeletedDossier.create!(dossier_id: dossier.id, procedure: dossier.procedure, state: dossier.state, deleted_at: Time.now.utc)
+ end
end
diff --git a/app/models/dossier.rb b/app/models/dossier.rb
index 70f023e26..4c5833b30 100644
--- a/app/models/dossier.rb
+++ b/app/models/dossier.rb
@@ -258,9 +258,8 @@ class Dossier < ApplicationRecord
end
def delete_and_keep_track
- now = Time.zone.now
- deleted_dossier = DeletedDossier.create!(dossier_id: id, procedure: procedure, state: state, deleted_at: now)
- update(hidden_at: now)
+ deleted_dossier = DeletedDossier.create_from_dossier(self)
+ update(hidden_at: deleted_dossier.deleted_at)
if en_construction?
administration_emails = followers_gestionnaires.present? ? followers_gestionnaires.pluck(:email) : [procedure.administrateur.email]
@@ -318,6 +317,13 @@ class Dossier < ApplicationRecord
log_dossier_operation(nil, :accepter, automatic_operation: true)
end
+ def hide!(administration)
+ update(hidden_at: Time.zone.now)
+
+ log_administration_dossier_operation(administration, :supprimer)
+ DeletedDossier.create_from_dossier(self)
+ end
+
def refuser!(gestionnaire, motivation)
self.motivation = motivation
self.en_instruction_at ||= Time.zone.now
@@ -356,6 +362,13 @@ class Dossier < ApplicationRecord
)
end
+ def log_administration_dossier_operation(administration, operation)
+ dossier_operation_logs.create(
+ administration: administration,
+ operation: DossierOperationLog.operations.fetch(operation)
+ )
+ end
+
def update_state_dates
if en_construction? && !self.en_construction_at
self.en_construction_at = Time.zone.now
diff --git a/app/models/dossier_operation_log.rb b/app/models/dossier_operation_log.rb
index fd4e770a4..6dced86b6 100644
--- a/app/models/dossier_operation_log.rb
+++ b/app/models/dossier_operation_log.rb
@@ -4,9 +4,11 @@ class DossierOperationLog < ApplicationRecord
repasser_en_construction: 'repasser_en_construction',
accepter: 'accepter',
refuser: 'refuser',
- classer_sans_suite: 'classer_sans_suite'
+ classer_sans_suite: 'classer_sans_suite',
+ supprimer: 'supprimer'
}
belongs_to :dossier
belongs_to :gestionnaire
+ belongs_to :administration
end
diff --git a/app/models/gestionnaire.rb b/app/models/gestionnaire.rb
index 7ac39c9e5..82516d4de 100644
--- a/app/models/gestionnaire.rb
+++ b/app/models/gestionnaire.rb
@@ -1,10 +1,6 @@
class Gestionnaire < ApplicationRecord
include CredentialsSyncableConcern
include EmailSanitizableConcern
- include ActiveRecord::SecureToken
-
- LOGIN_TOKEN_VALIDITY = 45.minutes
- LOGIN_TOKEN_YOUTH = 15.minutes
devise :database_authenticatable, :registerable, :async,
:recoverable, :rememberable, :trackable, :validatable
@@ -20,6 +16,7 @@ class Gestionnaire < ApplicationRecord
has_many :followed_dossiers, through: :follows, source: :dossier
has_many :avis
has_many :dossiers_from_avis, through: :avis, source: :dossier
+ has_many :trusted_device_tokens
def visible_procedures
procedures.merge(Procedure.avec_lien.or(Procedure.archivees))
@@ -135,18 +132,9 @@ class Gestionnaire < ApplicationRecord
Dossier.where(id: dossiers_id_with_notifications(dossiers)).group(:procedure_id).count
end
- def login_token!
- login_token = Gestionnaire.generate_unique_secure_token
- encrypted_login_token = BCrypt::Password.create(login_token)
- update(encrypted_login_token: encrypted_login_token, login_token_created_at: Time.zone.now)
- login_token
- end
-
- def login_token_valid?(login_token)
- BCrypt::Password.new(encrypted_login_token) == login_token &&
- LOGIN_TOKEN_VALIDITY.ago < login_token_created_at
- rescue BCrypt::Errors::InvalidHash
- false
+ def create_trusted_device_token
+ trusted_device_token = trusted_device_tokens.create
+ trusted_device_token.token
end
def dossiers_id_with_notifications(dossiers)
@@ -213,8 +201,8 @@ class Gestionnaire < ApplicationRecord
end
def young_login_token?
- login_token_created_at.present? &&
- LOGIN_TOKEN_YOUTH.ago < login_token_created_at
+ trusted_device_token = trusted_device_tokens.order(created_at: :desc).first
+ trusted_device_token&.token_young?
end
private
diff --git a/app/models/service.rb b/app/models/service.rb
index 5305c2989..4f8cab66c 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -7,11 +7,10 @@ class Service < ApplicationRecord
enum type_organisme: {
administration_centrale: 'administration_centrale',
association: 'association',
- commune: 'commune',
- departement: 'departement',
+ collectivite_territoriale: 'collectivite_territoriale',
etablissement_enseignement: 'etablissement_enseignement',
- prefecture: 'prefecture',
- region: 'region',
+ operateur_d_etat: "operateur_d_etat",
+ service_deconcentre_de_l_etat: 'service_deconcentre_de_l_etat',
autre: 'autre'
}
diff --git a/app/models/trusted_device_token.rb b/app/models/trusted_device_token.rb
new file mode 100644
index 000000000..951ccbe26
--- /dev/null
+++ b/app/models/trusted_device_token.rb
@@ -0,0 +1,15 @@
+class TrustedDeviceToken < ApplicationRecord
+ LOGIN_TOKEN_VALIDITY = 1.week
+ LOGIN_TOKEN_YOUTH = 15.minutes
+
+ belongs_to :gestionnaire
+ has_secure_token
+
+ def token_valid?
+ LOGIN_TOKEN_VALIDITY.ago < created_at
+ end
+
+ def token_young?
+ LOGIN_TOKEN_YOUTH.ago < created_at
+ end
+end
diff --git a/app/views/admin/attestation_templates/edit.html.haml b/app/views/admin/attestation_templates/edit.html.haml
index 34b4572fe..ce16b0873 100644
--- a/app/views/admin/attestation_templates/edit.html.haml
+++ b/app/views/admin/attestation_templates/edit.html.haml
@@ -10,7 +10,11 @@
- else
%small Désactivée
- %p.notice Les attestations, si elles sont activées, sont délivrées par email aux usagers lorsque leurs dossiers sont acceptés, et sont également disponibles au téléchargement sur leur espace personnel.
+ %p.notice
+ L’attestation, si elle est activée, est émise au moment où un dossier est accepté.
+ %br
+ L’email d’accusé d’acceptation envoyé à l’usager comporte alors un lien vers l’attestation ;
+ celle-ci est également disponible au téléchargement depuis l’espace personnel de l’usager.
.image-upload
- if @attestation_template.logo.present?
diff --git a/app/views/administration_mailer/refuse_admin.html.haml b/app/views/administration_mailer/refuse_admin.html.haml
index 014875d10..fefcdce29 100644
--- a/app/views/administration_mailer/refuse_admin.html.haml
+++ b/app/views/administration_mailer/refuse_admin.html.haml
@@ -4,14 +4,20 @@
Bonjour,
%p
- Les comptes administrateurs sont destinés aux administrations publiques souhaitant mettre en place des démarches dématérialisées avec demarches-simplifiees.fr. Il ne semble pas que ce soit votre cas.
+ Les comptes administrateurs sont destinés aux administrations publiques souhaitant mettre en place sur leurs sites internet des démarches dématérialisées pour leurs usagers. Il ne semble pas que ce soit votre cas.
+
%p
- Pour les usagers qui souhaitent remplir une démarche, l’entrée dans demarches-simplifiees.fr se fait via un lien fourni par l’administration responsable, sur son propre site web. Ce lien vous permettra de créer un compte et de remplir le formulaire dans la foulée.
+ Pour les usagers ou les administrations publiques (collectivités, etc.) qui souhaitent remplir une démarche ou un déposer un dossier en ligne, l’entrée dans demarches-simplifiees.fr se fait via un lien fourni par l’administration responsable, sur son propre site web. Ce lien vous permettra de créer un compte et de remplir le formulaire dans la foulée.
%p
Si par contre vous rencontrez des problèmes lors de l'utilisation de demarches-simplifiees.fr en tant qu'usager, merci d'expliciter le problème rencontré sur notre
= link_to("formulaire de contact", contact_url)
\.
+%p
+ Si vous avez fait une demande de compte administrateur légitime avec une adresse email grand public (Orange, Wanadoo etc), merci de nous contacter sur notre
+ = link_to("formulaire de contact administrateur", contact_admin_url)
+ \.
+
= render partial: "layouts/mailers/signature"
diff --git a/app/views/manager/dossiers/show.html.erb b/app/views/manager/dossiers/show.html.erb
index 9da620ca8..636962cd6 100644
--- a/app/views/manager/dossiers/show.html.erb
+++ b/app/views/manager/dossiers/show.html.erb
@@ -23,7 +23,7 @@ as well as a link to its edit page.
<%= content_for(:title) %>
<% if dossier.hidden_at %>
- (SUPPRIMÉ)
+ (Supprimé)
<% end %>
@@ -31,6 +31,9 @@ as well as a link to its edit page.
<% if dossier.termine? %>
<%= link_to 'Repasser en instruction', change_state_to_instruction_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Repasser en instruction ?" } %>
<% end %>
+ <% if dossier.hidden_at.nil? %>
+ <%= link_to 'Supprimer le dossier', hide_manager_dossier_path(dossier), method: :post, class: 'button', data: { confirm: "Confirmez vous la suppression du dossier ?" } %>
+ <% end %>
diff --git a/app/views/manager/procedures/show.html.erb b/app/views/manager/procedures/show.html.erb
index e4a00a4d3..908b0e2bf 100644
--- a/app/views/manager/procedures/show.html.erb
+++ b/app/views/manager/procedures/show.html.erb
@@ -33,6 +33,8 @@ as well as a link to its edit page.
+ <%= link_to 'aperçu', apercu_procedure_path(procedure), class: 'button' %>
+
<% if !procedure.whitelisted? %>
<%= link_to 'whitelister', whitelist_manager_procedure_path(procedure), method: :post, class: 'button' %>
<% end %>
diff --git a/app/views/new_user/dossiers/show/_header.html.haml b/app/views/new_user/dossiers/show/_header.html.haml
index b49179440..e1d488610 100644
--- a/app/views/new_user/dossiers/show/_header.html.haml
+++ b/app/views/new_user/dossiers/show/_header.html.haml
@@ -5,7 +5,10 @@
.title-container
%span.icon.folder
%h1= dossier.procedure.libelle
- %h2 Dossier nº #{dossier.id} - Déposé le #{l(dossier.en_construction_at, format: '%d %B %Y')}
+ %h2
+ Dossier nº #{dossier.id}
+ - if dossier.en_construction_at.present?
+ = "- Déposé le #{l(dossier.en_construction_at, format: '%d %B %Y')}"
- if current_user.owns?(dossier)
.header-actions
diff --git a/app/views/shared/dossiers/_edit.html.haml b/app/views/shared/dossiers/_edit.html.haml
index 3d52cd697..1b8dbadc0 100644
--- a/app/views/shared/dossiers/_edit.html.haml
+++ b/app/views/shared/dossiers/_edit.html.haml
@@ -75,18 +75,18 @@
name: :save_draft,
value: true,
class: 'button send secondary',
- data: { disable: true }
+ data: { 'disable-with': "Envoi en cours…" }
- if dossier.can_transition_to_en_construction?
= f.button 'Soumettre le dossier',
class: 'button send primary',
disabled: !current_user.owns?(dossier),
- data: { disable: true }
+ data: { 'disable-with': "Envoi en cours…" }
- else
= f.button 'Enregistrer les modifications du dossier',
class: 'button send primary',
- data: { disable: true }
+ data: { 'disable-with': "Envoi en cours…" }
- if dossier.brouillon? && !current_user.owns?(dossier)
.send-notice.invite-cannot-submit
diff --git a/app/views/shared/dossiers/editable_champs/_departements.html.haml b/app/views/shared/dossiers/editable_champs/_departements.html.haml
index 9f7cc8a8b..e3084d148 100644
--- a/app/views/shared/dossiers/editable_champs/_departements.html.haml
+++ b/app/views/shared/dossiers/editable_champs/_departements.html.haml
@@ -1,3 +1,4 @@
= form.select :value,
Champs::DepartementChamp.departements,
+ include_blank: true,
required: champ.mandatory?
diff --git a/config/locales/models/service/fr.yml b/config/locales/models/service/fr.yml
index 4a573ecc4..68e032c8d 100644
--- a/config/locales/models/service/fr.yml
+++ b/config/locales/models/service/fr.yml
@@ -1,10 +1,9 @@
fr:
type_organisme:
- administration_centrale: 'administration centrale'
- association: 'association'
- commune: 'commune'
- departement: 'département'
- etablissement_enseignement: 'établissement d’enseignement'
- prefecture: 'préfecture'
- region: 'région'
- autre: 'autre'
+ administration_centrale: 'Administration centrale'
+ association: 'Association'
+ collectivite_territoriale: 'Collectivité territoriale'
+ etablissement_enseignement: 'Établissement d’enseignement'
+ operateur_d_etat: "Opérateur d'État"
+ service_deconcentre_de_l_etat: "Service déconcentré de l'État"
+ autre: 'Autre'
diff --git a/config/routes.rb b/config/routes.rb
index 6e0f65c98..ba28c63cd 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,6 +14,7 @@ Rails.application.routes.draw do
resources :dossiers, only: [:index, :show] do
post 'change_state_to_instruction', on: :member
+ post 'hide', on: :member
end
resources :administrateurs, only: [:index, :show, :new, :create] do
diff --git a/db/migrate/20190201164951_create_trusted_device_tokens.rb b/db/migrate/20190201164951_create_trusted_device_tokens.rb
new file mode 100644
index 000000000..c53bec685
--- /dev/null
+++ b/db/migrate/20190201164951_create_trusted_device_tokens.rb
@@ -0,0 +1,11 @@
+class CreateTrustedDeviceTokens < ActiveRecord::Migration[5.2]
+ def change
+ create_table :trusted_device_tokens do |t|
+ t.string :token, null: false
+ t.references :gestionnaire, foreign_key: true
+
+ t.timestamps
+ end
+ add_index :trusted_device_tokens, :token, unique: true
+ end
+end
diff --git a/db/migrate/20190213144145_add_administration_column_to_log_dossier_operation.rb b/db/migrate/20190213144145_add_administration_column_to_log_dossier_operation.rb
new file mode 100644
index 000000000..cfaf6aa1f
--- /dev/null
+++ b/db/migrate/20190213144145_add_administration_column_to_log_dossier_operation.rb
@@ -0,0 +1,5 @@
+class AddAdministrationColumnToLogDossierOperation < ActiveRecord::Migration[5.2]
+ def change
+ add_reference :dossier_operation_logs, :administration, foreign_key: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index b710d1d37..05ee04c74 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_01_10_163655) do
+ActiveRecord::Schema.define(version: 2019_02_13_144145) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -219,6 +219,8 @@ ActiveRecord::Schema.define(version: 2019_01_10_163655) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "automatic_operation", default: false, null: false
+ t.bigint "administration_id"
+ t.index ["administration_id"], name: "index_dossier_operation_logs_on_administration_id"
t.index ["dossier_id"], name: "index_dossier_operation_logs_on_dossier_id"
t.index ["gestionnaire_id"], name: "index_dossier_operation_logs_on_gestionnaire_id"
end
@@ -518,6 +520,15 @@ ActiveRecord::Schema.define(version: 2019_01_10_163655) do
t.string "version", null: false
end
+ create_table "trusted_device_tokens", force: :cascade do |t|
+ t.string "token", null: false
+ t.bigint "gestionnaire_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.index ["gestionnaire_id"], name: "index_trusted_device_tokens_on_gestionnaire_id"
+ t.index ["token"], name: "index_trusted_device_tokens_on_token", unique: true
+ end
+
create_table "types_de_champ", id: :serial, force: :cascade do |t|
t.string "libelle"
t.string "type_champ"
@@ -597,6 +608,7 @@ ActiveRecord::Schema.define(version: 2019_01_10_163655) do
add_foreign_key "champs", "champs", column: "parent_id"
add_foreign_key "closed_mails", "procedures"
add_foreign_key "commentaires", "dossiers"
+ add_foreign_key "dossier_operation_logs", "administrations"
add_foreign_key "dossier_operation_logs", "dossiers"
add_foreign_key "dossier_operation_logs", "gestionnaires"
add_foreign_key "dossiers", "users"
@@ -608,6 +620,7 @@ ActiveRecord::Schema.define(version: 2019_01_10_163655) do
add_foreign_key "received_mails", "procedures"
add_foreign_key "refused_mails", "procedures"
add_foreign_key "services", "administrateurs"
+ add_foreign_key "trusted_device_tokens", "gestionnaires"
add_foreign_key "types_de_champ", "types_de_champ", column: "parent_id"
add_foreign_key "without_continuation_mails", "procedures"
end
diff --git a/lib/tasks/2019_02_18_migrate_service_organisme.rake b/lib/tasks/2019_02_18_migrate_service_organisme.rake
new file mode 100644
index 000000000..369a85a5f
--- /dev/null
+++ b/lib/tasks/2019_02_18_migrate_service_organisme.rake
@@ -0,0 +1,17 @@
+namespace :after_party do
+ desc 'Deployment task: migrate service organisme'
+ task migrate_service_organisme: :environment do
+ table = {
+ 'commune': 'collectivite_territoriale',
+ 'departement': 'collectivite_territoriale',
+ 'region': 'collectivite_territoriale',
+ 'prefecture': 'service_deconcentre_de_l_etat'
+ }
+
+ table.each do |(old_name, new_name)|
+ Service.where(type_organisme: old_name).update_all(type_organisme: new_name)
+ end
+
+ AfterParty::TaskRecord.create version: '20190201121252'
+ end
+end
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 0545d786b..ce5777b40 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -9,6 +9,7 @@ describe ApplicationController, type: :controller do
.map(&:filter)
expect(before_actions).to include(:set_raven_context)
+ expect(before_actions).to include(:redirect_if_untrusted)
end
end
@@ -145,4 +146,56 @@ describe ApplicationController, type: :controller do
it { expect(flash[:alert]).to eq(ApplicationController::MAINTENANCE_MESSAGE) }
end
end
+
+ describe '#redirect_if_unstrusted' do
+ let(:current_gestionnaire) { create(:gestionnaire) }
+
+ before do
+ allow(current_gestionnaire).to receive(:feature_enabled?).and_return(feature_enabled)
+ allow(@controller).to receive(:current_gestionnaire).and_return(current_gestionnaire)
+
+ allow(@controller).to receive(:redirect_to)
+ allow(@controller).to receive(:trusted_device?).and_return(trusted_device)
+ allow(@controller).to receive(:gestionnaire_signed_in?).and_return(gestionnaire_signed_in)
+ allow(@controller).to receive(:sensitive_path).and_return(sensitive_path)
+ allow(@controller).to receive(:send_login_token_or_bufferize)
+ allow(@controller).to receive(:store_location_for)
+ end
+
+ subject { @controller.send(:redirect_if_untrusted) }
+
+ context 'when the path is sensitive' do
+ let(:sensitive_path) { true }
+
+ context 'when the gestionnaire is signed_in' do
+ let(:gestionnaire_signed_in) { true }
+
+ context 'when the feature is activated' do
+ let(:feature_enabled) { true }
+
+ context 'when the device is trusted' do
+ let(:trusted_device) { true }
+
+ before { subject }
+
+ it { expect(@controller).not_to have_received(:redirect_to) }
+ end
+ end
+
+ context 'when the feature is activated' do
+ let(:feature_enabled) { true }
+
+ context 'when the device is not trusted' do
+ let(:trusted_device) { false }
+
+ before { subject }
+
+ it { expect(@controller).to have_received(:redirect_to) }
+ it { expect(@controller).to have_received(:send_login_token_or_bufferize) }
+ it { expect(@controller).to have_received(:store_location_for) }
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/controllers/manager/dossiers_controller_spec.rb b/spec/controllers/manager/dossiers_controller_spec.rb
new file mode 100644
index 000000000..e3c5f3cad
--- /dev/null
+++ b/spec/controllers/manager/dossiers_controller_spec.rb
@@ -0,0 +1,14 @@
+describe Manager::DossiersController, type: :controller do
+ describe '#hide' do
+ let(:administration) { create :administration }
+ let!(:dossier) { create(:dossier) }
+
+ before do
+ sign_in administration
+ post :hide, params: { id: dossier.id }
+ dossier.reload
+ end
+
+ it { expect(dossier.hidden_at).not_to be_nil }
+ end
+end
diff --git a/spec/controllers/new_administrateur/services_controller_spec.rb b/spec/controllers/new_administrateur/services_controller_spec.rb
index 081603342..e020d9751 100644
--- a/spec/controllers/new_administrateur/services_controller_spec.rb
+++ b/spec/controllers/new_administrateur/services_controller_spec.rb
@@ -15,7 +15,7 @@ describe NewAdministrateur::ServicesController, type: :controller do
nom: 'super service',
organisme: 'organisme',
siret: '01234567891234',
- type_organisme: 'region',
+ type_organisme: 'association',
email: 'email@toto.com',
telephone: '1234',
horaires: 'horaires',
@@ -30,7 +30,7 @@ describe NewAdministrateur::ServicesController, type: :controller do
it { expect(Service.last.nom).to eq('super service') }
it { expect(Service.last.organisme).to eq('organisme') }
it { expect(Service.last.siret).to eq('01234567891234') }
- it { expect(Service.last.type_organisme).to eq(Service.type_organismes.fetch(:region)) }
+ it { expect(Service.last.type_organisme).to eq(Service.type_organismes.fetch(:association)) }
it { expect(Service.last.email).to eq('email@toto.com') }
it { expect(Service.last.telephone).to eq('1234') }
it { expect(Service.last.horaires).to eq('horaires') }
@@ -49,7 +49,7 @@ describe NewAdministrateur::ServicesController, type: :controller do
describe '#update' do
let!(:service) { create(:service, administrateur: admin) }
- let(:service_params) { { nom: 'nom', type_organisme: Service.type_organismes.fetch(:region) } }
+ let(:service_params) { { nom: 'nom', type_organisme: Service.type_organismes.fetch(:association) } }
before do
sign_in admin
@@ -65,12 +65,12 @@ describe NewAdministrateur::ServicesController, type: :controller do
it { expect(flash.alert).to be_nil }
it { expect(flash.notice).to eq('nom modifié') }
it { expect(Service.last.nom).to eq('nom') }
- it { expect(Service.last.type_organisme).to eq(Service.type_organismes.fetch(:region)) }
+ it { expect(Service.last.type_organisme).to eq(Service.type_organismes.fetch(:association)) }
it { expect(response).to redirect_to(services_path(procedure_id: procedure.id)) }
end
context 'when updating a service with invalid data' do
- let(:service_params) { { nom: '', type_organisme: Service.type_organismes.fetch(:region) } }
+ let(:service_params) { { nom: '', type_organisme: Service.type_organismes.fetch(:association) } }
it { expect(flash.alert).not_to be_nil }
it { expect(response).to render_template(:edit) }
diff --git a/spec/controllers/sessions/sessions_controller_spec.rb b/spec/controllers/sessions/sessions_controller_spec.rb
index c0f5efc14..5d8e5839d 100644
--- a/spec/controllers/sessions/sessions_controller_spec.rb
+++ b/spec/controllers/sessions/sessions_controller_spec.rb
@@ -40,6 +40,7 @@ describe Sessions::SessionsController, type: :controller do
@request.env["devise.mapping"] = Devise.mappings[:gestionnaire]
allow_any_instance_of(described_class).to receive(:gestionnaire_signed_in?).and_return(true)
+ allow_any_instance_of(described_class).to receive(:current_gestionnaire).and_return(gestionnaire)
end
it 'calls sign out for gestionnaire' do
diff --git a/spec/controllers/users/sessions_controller_spec.rb b/spec/controllers/users/sessions_controller_spec.rb
index efa388790..e644fa74a 100644
--- a/spec/controllers/users/sessions_controller_spec.rb
+++ b/spec/controllers/users/sessions_controller_spec.rb
@@ -28,31 +28,15 @@ describe Users::SessionsController, type: :controller do
context 'when the device is not trusted' do
let(:trusted_device) { false }
- it 'redirects to the confirmation link path' do
+ it 'redirects to the root path' do
subject
- expect(controller).to redirect_to link_sent_path(email: email)
+ expect(controller).to redirect_to(root_path)
- # do not know why, should be test related
expect(controller.current_user).to eq(user)
-
- expect(controller.current_gestionnaire).to be(nil)
- expect(controller.current_administrateur).to be(nil)
- expect(user.loged_in_with_france_connect).to be(nil)
- expect(GestionnaireMailer).to have_received(:send_login_token)
- end
-
- context 'and the user try to connect multiple times in a short period' do
- before do
- allow_any_instance_of(Gestionnaire).to receive(:young_login_token?).and_return(true)
- allow(GestionnaireMailer).to receive(:send_login_token)
- end
-
- it 'does not renew nor send a new login token' do
- subject
-
- expect(GestionnaireMailer).not_to have_received(:send_login_token)
- end
+ expect(controller.current_gestionnaire).to eq(gestionnaire)
+ expect(controller.current_administrateur).to eq(administrateur)
+ expect(user.loged_in_with_france_connect).to eq(nil)
end
end
@@ -69,7 +53,6 @@ describe Users::SessionsController, type: :controller do
expect(controller.current_gestionnaire).to eq(gestionnaire)
expect(controller.current_administrateur).to eq(administrateur)
expect(user.loged_in_with_france_connect).to be(nil)
- expect(GestionnaireMailer).not_to have_received(:send_login_token)
end
end
@@ -192,50 +175,57 @@ describe Users::SessionsController, type: :controller do
describe '#sign_in_by_link' do
context 'when the gestionnaire has non other account' do
let(:gestionnaire) { create(:gestionnaire) }
- let!(:good_jeton) { gestionnaire.login_token! }
+ let!(:good_jeton) { gestionnaire.create_trusted_device_token }
+ let(:logged) { false }
before do
+ if logged
+ sign_in gestionnaire
+ end
allow(controller).to receive(:trust_device)
+ allow(controller).to receive(:send_login_token_or_bufferize)
post :sign_in_by_link, params: { id: gestionnaire.id, jeton: jeton }
end
- context 'when the token is valid' do
- let(:jeton) { good_jeton }
+ context 'when the gestionnaire is not logged in' do
+ context 'when the token is valid' do
+ let(:jeton) { good_jeton }
- # TODO when the gestionnaire has no other account, and the token is valid, and the user signing in was not starting a demarche,
- # redirect to root_path, then redirect to gestionnaire_procedures_path (see root_controller)
- it { is_expected.to redirect_to root_path }
- it { expect(controller.current_gestionnaire).to eq(gestionnaire) }
- it { expect(controller).to have_received(:trust_device) }
+ it { is_expected.to redirect_to new_user_session_path }
+ it { expect(controller.current_gestionnaire).to be_nil }
+ it { expect(controller).to have_received(:trust_device) }
+ end
+
+ context 'when the token is invalid' do
+ let(:jeton) { 'invalid_token' }
+
+ it { is_expected.to redirect_to link_sent_path(email: gestionnaire.email) }
+ it { expect(controller.current_gestionnaire).to be_nil }
+ it { expect(controller).not_to have_received(:trust_device) }
+ it { expect(controller).to have_received(:send_login_token_or_bufferize) }
+ end
end
- context 'when the token is invalid' do
- let(:jeton) { 'invalid_token' }
+ context 'when the gestionnaire is logged in' do
+ let(:logged) { true }
- it { is_expected.to redirect_to new_user_session_path }
- it { expect(controller.current_gestionnaire).to be_nil }
- it { expect(controller).not_to have_received(:trust_device) }
- end
- end
+ context 'when the token is valid' do
+ let(:jeton) { good_jeton }
- context 'when the gestionnaire has an user and admin account' do
- let(:email) { 'unique@plop.com' }
- let(:password) { 'un super mot de passe' }
+ # redirect to root_path, then redirect to gestionnaire_procedures_path (see root_controller)
+ it { is_expected.to redirect_to root_path }
+ it { expect(controller.current_gestionnaire).to eq(gestionnaire) }
+ it { expect(controller).to have_received(:trust_device) }
+ end
- let!(:user) { create(:user, email: email, password: password) }
- let!(:administrateur) { create(:administrateur, email: email, password: password) }
- let(:gestionnaire) { administrateur.gestionnaire }
+ context 'when the token is invalid' do
+ let(:jeton) { 'invalid_token' }
- before do
- post :sign_in_by_link, params: { id: gestionnaire.id, jeton: jeton }
- end
-
- context 'when the token is valid' do
- let(:jeton) { gestionnaire.login_token! }
-
- it { expect(controller.current_gestionnaire).to eq(gestionnaire) }
- it { expect(controller.current_administrateur).to eq(administrateur) }
- it { expect(controller.current_user).to eq(user) }
+ it { is_expected.to redirect_to link_sent_path(email: gestionnaire.email) }
+ it { expect(controller.current_gestionnaire).to eq(gestionnaire) }
+ it { expect(controller).not_to have_received(:trust_device) }
+ it { expect(controller).to have_received(:send_login_token_or_bufferize) }
+ end
end
end
end
@@ -249,16 +239,15 @@ describe Users::SessionsController, type: :controller do
context 'when the cookie is outdated' do
before do
- Timecop.freeze(Time.zone.now - TrustedDeviceConcern::TRUSTED_DEVICE_PERIOD - 1.minute)
- controller.trust_device
- Timecop.return
+ emission_date = Time.zone.now - TrustedDeviceConcern::TRUSTED_DEVICE_PERIOD - 1.minute
+ controller.trust_device(emission_date)
end
it { is_expected.to be false }
end
context 'when the cookie is ok' do
- before { controller.trust_device }
+ before { controller.trust_device(Time.zone.now) }
it { is_expected.to be true }
end
diff --git a/spec/factories/service.rb b/spec/factories/service.rb
index 710721e7c..889a80240 100644
--- a/spec/factories/service.rb
+++ b/spec/factories/service.rb
@@ -2,7 +2,7 @@ FactoryBot.define do
factory :service do
nom { 'service' }
organisme { 'organisme' }
- type_organisme { Service.type_organismes.fetch(:commune) }
+ type_organisme { Service.type_organismes.fetch(:association) }
administrateur { create(:administrateur) }
email { 'email@toto.com' }
telephone { '1234' }
diff --git a/spec/models/dossier_spec.rb b/spec/models/dossier_spec.rb
index 60e8dab8c..78a39d7e0 100644
--- a/spec/models/dossier_spec.rb
+++ b/spec/models/dossier_spec.rb
@@ -892,4 +892,21 @@ describe Dossier do
end
end
end
+
+ describe '#hide!' do
+ let(:dossier) { create(:dossier) }
+ let(:administration) { create(:administration) }
+ let(:last_operation) { dossier.dossier_operation_logs.last }
+
+ before do
+ Timecop.freeze
+ dossier.hide!(administration)
+ end
+
+ after { Timecop.return }
+
+ it { expect(dossier.hidden_at).to eq(Time.zone.now) }
+ it { expect(last_operation.operation).to eq('supprimer') }
+ it { expect(last_operation.administration).to eq(administration) }
+ end
end
diff --git a/spec/models/gestionnaire_spec.rb b/spec/models/gestionnaire_spec.rb
index 344ee1683..2a28e5f6e 100644
--- a/spec/models/gestionnaire_spec.rb
+++ b/spec/models/gestionnaire_spec.rb
@@ -392,44 +392,24 @@ describe Gestionnaire, type: :model do
end
end
- describe '#login_token_valid?' do
- let!(:gestionnaire) { create(:gestionnaire) }
- let!(:good_token) { gestionnaire.login_token! }
-
- it { expect(gestionnaire.login_token_valid?(good_token)).to be true }
- it { expect(gestionnaire.login_token_valid?('bad_token')).to be false }
-
- context 'when the token as expired' do
- before { gestionnaire.update(login_token_created_at: (Gestionnaire::LOGIN_TOKEN_VALIDITY + 1.minute).ago) }
-
- it { expect(gestionnaire.login_token_valid?(good_token)).to be false }
- end
-
- context 'when the gestionnaire does not have a token' do
- before { gestionnaire.update(encrypted_login_token: nil) }
-
- it { expect(gestionnaire.login_token_valid?(nil)).to be false }
- end
- end
-
describe '#young_login_token?' do
let!(:gestionnaire) { create(:gestionnaire) }
context 'when there is a token' do
- let!(:good_token) { gestionnaire.login_token! }
+ let!(:good_token) { gestionnaire.create_trusted_device_token }
context 'when the token has just been created' do
it { expect(gestionnaire.young_login_token?).to be true }
end
context 'when the token is a bit old' do
- before { gestionnaire.update(login_token_created_at: (Gestionnaire::LOGIN_TOKEN_YOUTH + 1.minute).ago) }
+ before { gestionnaire.trusted_device_tokens.first.update(created_at: (TrustedDeviceToken::LOGIN_TOKEN_YOUTH + 1.minute).ago) }
it { expect(gestionnaire.young_login_token?).to be false }
end
end
context 'when there are no token' do
- it { expect(gestionnaire.young_login_token?).to be false }
+ it { expect(gestionnaire.young_login_token?).to be_falsey }
end
end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index 2a24cc89f..e34f56dac 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -5,7 +5,7 @@ describe Service, type: :model do
{
nom: 'service des jardins',
organisme: 'mairie des iles',
- type_organisme: Service.type_organismes.fetch(:commune),
+ type_organisme: Service.type_organismes.fetch(:association),
email: 'super@email.com',
telephone: '1212202',
horaires: 'du lundi au vendredi',
diff --git a/spec/models/trusted_device_token_spec.rb b/spec/models/trusted_device_token_spec.rb
new file mode 100644
index 000000000..ed5ae1e51
--- /dev/null
+++ b/spec/models/trusted_device_token_spec.rb
@@ -0,0 +1,29 @@
+RSpec.describe TrustedDeviceToken, type: :model do
+ describe '#token_valid?' do
+ let(:token) { TrustedDeviceToken.create }
+
+ context 'when the token is create after login_token_validity' do
+ it { expect(token.token_valid?).to be true }
+ end
+
+ context 'when the token is create before login_token_validity' do
+ before { token.update(created_at: (TrustedDeviceToken::LOGIN_TOKEN_VALIDITY + 1.minute).ago) }
+
+ it { expect(token.token_valid?).to be false }
+ end
+ end
+
+ describe '#token_young?' do
+ let(:token) { TrustedDeviceToken.create }
+
+ context 'when the token is create after login_token_youth' do
+ it { expect(token.token_young?).to be true }
+ end
+
+ context 'when the token is create before login_token_youth' do
+ before { token.update(created_at: (TrustedDeviceToken::LOGIN_TOKEN_YOUTH + 1.minute).ago) }
+
+ it { expect(token.token_young?).to be false }
+ end
+ end
+end