Save virus scan status to blob metadata (#3816)
Migration du statut de l'analyse anti-virus vers les objets `ActiveStorage` (plutôt que d'être liés à chaque modèle).
This commit is contained in:
commit
7b835f01fd
19 changed files with 181 additions and 131 deletions
|
@ -1,20 +0,0 @@
|
||||||
class AntiVirusJob < ApplicationJob
|
|
||||||
include ActiveStorage::Downloading
|
|
||||||
|
|
||||||
attr_reader :blob
|
|
||||||
|
|
||||||
def perform(virus_scan)
|
|
||||||
@blob = ActiveStorage::Blob.find_by(key: virus_scan.blob_key)
|
|
||||||
|
|
||||||
if @blob.present?
|
|
||||||
download_blob_to_tempfile do |file|
|
|
||||||
if ClamavService.safe_file?(file.path)
|
|
||||||
status = VirusScan.statuses.fetch(:safe)
|
|
||||||
else
|
|
||||||
status = VirusScan.statuses.fetch(:infected)
|
|
||||||
end
|
|
||||||
virus_scan.update(scanned_at: Time.zone.now, status: status)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
10
app/jobs/virus_scanner_job.rb
Normal file
10
app/jobs/virus_scanner_job.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
class VirusScannerJob < ApplicationJob
|
||||||
|
def perform(blob)
|
||||||
|
metadata = extract_metadata_via_virus_scanner(blob)
|
||||||
|
blob.update!(metadata: blob.metadata.merge(metadata))
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_metadata_via_virus_scanner(blob)
|
||||||
|
ActiveStorage::VirusScanner.new(blob).metadata
|
||||||
|
end
|
||||||
|
end
|
46
app/lib/active_storage/virus_scanner.rb
Normal file
46
app/lib/active_storage/virus_scanner.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
class ActiveStorage::VirusScanner
|
||||||
|
include ActiveStorage::Downloading
|
||||||
|
|
||||||
|
def initialize(blob)
|
||||||
|
@blob = blob
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :blob
|
||||||
|
|
||||||
|
PENDING = 'pending'
|
||||||
|
INFECTED = 'infected'
|
||||||
|
SAFE = 'safe'
|
||||||
|
|
||||||
|
def pending?
|
||||||
|
blob.metadata[:virus_scan_result] == PENDING
|
||||||
|
end
|
||||||
|
|
||||||
|
def infected?
|
||||||
|
blob.metadata[:virus_scan_result] == INFECTED
|
||||||
|
end
|
||||||
|
|
||||||
|
def safe?
|
||||||
|
blob.metadata[:virus_scan_result] == SAFE
|
||||||
|
end
|
||||||
|
|
||||||
|
def analyzed?
|
||||||
|
blob.metadata[:virus_scan_result].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def analyze_later
|
||||||
|
if !analyzed?
|
||||||
|
blob.update!(metadata: blob.metadata.merge(virus_scan_result: PENDING))
|
||||||
|
VirusScannerJob.perform_later(blob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def metadata
|
||||||
|
download_blob_to_tempfile do |file|
|
||||||
|
if ClamavService.safe_file?(file.path)
|
||||||
|
{ virus_scan_result: SAFE, scanned_at: Time.zone.now }
|
||||||
|
else
|
||||||
|
{ virus_scan_result: INFECTED, scanned_at: Time.zone.now }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,5 @@
|
||||||
class Avis < ApplicationRecord
|
class Avis < ApplicationRecord
|
||||||
include EmailSanitizableConcern
|
include EmailSanitizableConcern
|
||||||
include VirusScanConcern
|
|
||||||
|
|
||||||
belongs_to :dossier, touch: true
|
belongs_to :dossier, touch: true
|
||||||
belongs_to :gestionnaire
|
belongs_to :gestionnaire
|
||||||
|
@ -22,9 +21,6 @@ class Avis < ApplicationRecord
|
||||||
scope :by_latest, -> { order(updated_at: :desc) }
|
scope :by_latest, -> { order(updated_at: :desc) }
|
||||||
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
|
scope :updated_since?, -> (date) { where('avis.updated_at > ?', date) }
|
||||||
|
|
||||||
after_commit :create_avis_virus_scan
|
|
||||||
after_initialize { add_virus_scan_on(self.piece_justificative_file) }
|
|
||||||
|
|
||||||
# The form allows subtmitting avis requests to several emails at once,
|
# The form allows subtmitting avis requests to several emails at once,
|
||||||
# hence this virtual attribute.
|
# hence this virtual attribute.
|
||||||
attr_accessor :emails
|
attr_accessor :emails
|
||||||
|
@ -41,6 +37,27 @@ class Avis < ApplicationRecord
|
||||||
Avis.find_by(id: avis_id)&.email == email
|
Avis.find_by(id: avis_id)&.email == email
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# FIXME remove this after migrating virus_scan to blob metadata
|
||||||
|
def virus_scan
|
||||||
|
VirusScan.find_by(blob_key: piece_justificative_file.blob.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_safe?
|
||||||
|
virus_scan&.safe? || piece_justificative_file.virus_scanner.safe?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_infected?
|
||||||
|
virus_scan&.infected? || piece_justificative_file.virus_scanner.infected?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_pending?
|
||||||
|
virus_scan&.pending? || piece_justificative_file.virus_scanner.pending?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_no_scan?
|
||||||
|
virus_scan.blank? && !piece_justificative_file.virus_scanner.analyzed?
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def notify_gestionnaire
|
def notify_gestionnaire
|
||||||
|
@ -54,8 +71,4 @@ class Avis < ApplicationRecord
|
||||||
self.email = nil
|
self.email = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_avis_virus_scan
|
|
||||||
create_virus_scan(self.piece_justificative_file)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
class Champs::PieceJustificativeChamp < Champ
|
class Champs::PieceJustificativeChamp < Champ
|
||||||
after_commit :create_virus_scan
|
|
||||||
|
|
||||||
PIECE_JUSTIFICATIVE_FILE_MAX_SIZE = 200.megabytes
|
PIECE_JUSTIFICATIVE_FILE_MAX_SIZE = 200.megabytes
|
||||||
|
|
||||||
PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS = [
|
PIECE_JUSTIFICATIVE_FILE_ACCEPTED_FORMATS = [
|
||||||
|
@ -48,20 +46,26 @@ class Champs::PieceJustificativeChamp < Champ
|
||||||
errors
|
errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# FIXME remove this after migrating virus_scan to blob metadata
|
||||||
|
def virus_scan_safe?
|
||||||
|
virus_scan&.safe? || piece_justificative_file.virus_scanner.safe?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_infected?
|
||||||
|
virus_scan&.infected? || piece_justificative_file.virus_scanner.infected?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_pending?
|
||||||
|
virus_scan&.pending? || piece_justificative_file.virus_scanner.pending?
|
||||||
|
end
|
||||||
|
|
||||||
|
def virus_scan_no_scan?
|
||||||
|
virus_scan.blank? && !piece_justificative_file.virus_scanner.analyzed?
|
||||||
|
end
|
||||||
|
|
||||||
def for_api
|
def for_api
|
||||||
if piece_justificative_file.attached? && (virus_scan&.safe? || virus_scan&.pending?)
|
if piece_justificative_file.attached? && (virus_scan_safe? || virus_scan_pending?)
|
||||||
Rails.application.routes.url_helpers.url_for(piece_justificative_file)
|
Rails.application.routes.url_helpers.url_for(piece_justificative_file)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def create_virus_scan
|
|
||||||
if self.piece_justificative_file&.attachment&.blob.present?
|
|
||||||
VirusScan.where(champ: self).where.not(blob_key: self.piece_justificative_file.blob.key).delete_all
|
|
||||||
VirusScan.find_or_create_by!(champ: self, blob_key: self.piece_justificative_file.blob.key) do |virus_scan|
|
|
||||||
virus_scan.status = VirusScan.statuses.fetch(:pending)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
module VirusScanConcern
|
|
||||||
extend ActiveSupport::Concern
|
|
||||||
|
|
||||||
attr_reader :attachment_attribute
|
|
||||||
|
|
||||||
def add_virus_scan_on(piece_justificative)
|
|
||||||
@attachment_attribute = piece_justificative
|
|
||||||
end
|
|
||||||
|
|
||||||
def virus_scan
|
|
||||||
VirusScan.find_by(blob_key: self.attachment_attribute.blob.key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_virus_scan(piece_justificative)
|
|
||||||
if piece_justificative&.attachment&.blob.present?
|
|
||||||
VirusScan.find_or_create_by!(blob_key: piece_justificative.blob.key) do |virus_scan|
|
|
||||||
virus_scan.status = VirusScan.statuses.fetch(:pending)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -6,12 +6,4 @@ class VirusScan < ApplicationRecord
|
||||||
safe: 'safe',
|
safe: 'safe',
|
||||||
infected: 'infected'
|
infected: 'infected'
|
||||||
}
|
}
|
||||||
|
|
||||||
validates :champ_id, uniqueness: { scope: :blob_key }
|
|
||||||
|
|
||||||
after_create :perform_scan
|
|
||||||
|
|
||||||
def perform_scan
|
|
||||||
AntiVirusJob.perform_later(self)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
- pj = champ.piece_justificative_file
|
|
||||||
|
|
||||||
.pj-link
|
|
||||||
- if champ.virus_scan.blank? || champ.virus_scan.safe?
|
|
||||||
= link_to url_for(pj), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
|
|
||||||
%span.icon.attachment
|
|
||||||
= pj.filename.to_s
|
|
||||||
- if champ.virus_scan.blank?
|
|
||||||
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
|
||||||
|
|
||||||
- else
|
|
||||||
= pj.filename.to_s
|
|
||||||
- if champ.virus_scan.pending?
|
|
||||||
(analyse antivirus en cours
|
|
||||||
= link_to "rafraichir", request.path
|
|
||||||
)
|
|
||||||
- elsif champ.virus_scan.infected?
|
|
||||||
- if user_can_upload
|
|
||||||
(virus détecté, merci d’envoyer un autre fichier)
|
|
||||||
- else
|
|
||||||
(virus détecté, le téléchargement de ce fichier est bloqué)
|
|
|
@ -1,5 +1,5 @@
|
||||||
- pj = champ.piece_justificative_file
|
- pj = champ.piece_justificative_file
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: false }
|
= render partial: "shared/piece_jointe/pj_link", locals: { object: champ, pj: pj, user_can_upload: false }
|
||||||
- else
|
- else
|
||||||
Pièce justificative non fournie
|
Pièce justificative non fournie
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
.piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
|
.piece-justificative-actions{ id: "piece_justificative_#{champ.id}" }
|
||||||
.piece-justificative-action
|
.piece-justificative-action
|
||||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
= render partial: "shared/piece_jointe/pj_link", locals: { object: champ, pj: champ.piece_justificative_file, user_can_upload: true }
|
||||||
.piece-justificative-action
|
.piece-justificative-action
|
||||||
- if champ.private?
|
- if champ.private?
|
||||||
= link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
= link_to 'Supprimer', gestionnaire_champ_purge_champ_piece_justificative_path(procedure_id: champ.dossier.procedure_id, dossier_id: champ.dossier_id, champ_id: champ.id), remote: true, method: :delete, class: 'button small danger'
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
.pj-link
|
.pj-link
|
||||||
- if object.virus_scan.blank? || object.virus_scan.safe?
|
- if object.virus_scan_safe? || object.virus_scan_no_scan?
|
||||||
= link_to url_for(pj), target: '_blank', title: "Télécharger la pièce jointe" do
|
= link_to url_for(pj), target: '_blank', rel: 'noopener', title: "Télécharger la pièce jointe" do
|
||||||
%span.icon.attachment
|
%span.icon.attachment
|
||||||
= pj.filename.to_s
|
= pj.filename.to_s
|
||||||
- if object.virus_scan.blank?
|
- if object.virus_scan_no_scan?
|
||||||
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
(ce fichier n’a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
||||||
|
|
||||||
- else
|
- else
|
||||||
= pj.filename.to_s
|
= pj.filename.to_s
|
||||||
- if object.virus_scan.pending?
|
- if object.virus_scan_pending?
|
||||||
(analyse antivirus en cours
|
(analyse antivirus en cours
|
||||||
= link_to "rafraichir", request.path
|
= link_to "rafraichir", request.path
|
||||||
)
|
)
|
||||||
- elsif object.virus_scan.infected?
|
- elsif object.virus_scan_infected?
|
||||||
- if user_can_upload
|
- if user_can_upload
|
||||||
(virus détecté, merci d’envoyer un autre fichier)
|
(virus détecté, merci d’envoyer un autre fichier)
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -1 +1,29 @@
|
||||||
ActiveStorage::Service.url_expires_in = 1.hour
|
ActiveStorage::Service.url_expires_in = 1.hour
|
||||||
|
|
||||||
|
# We want to run the virus scan on every ActiveStorage attachment,
|
||||||
|
# regardless of its type (user-uploaded document, instructeur-uploaded attestation, form template, etc.)
|
||||||
|
#
|
||||||
|
# To do this, the best place to work on is the ActiveStorage::Attachment
|
||||||
|
# objects themselves.
|
||||||
|
#
|
||||||
|
# We have to monkey patch ActiveStorage in order to always run an analyzer.
|
||||||
|
# The way analyzable blob interface work is by running the first accepted analyzer.
|
||||||
|
# This is not what we want for the virus scan. Using analyzer interface is still beneficial
|
||||||
|
# as it takes care of downloading the blob.
|
||||||
|
ActiveStorage::Attachment.class_eval do
|
||||||
|
after_create_commit :virus_scan
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def virus_scan
|
||||||
|
ActiveStorage::VirusScanner.new(blob).analyze_later
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveStorage::Attached::One.class_eval do
|
||||||
|
def virus_scanner
|
||||||
|
if attached?
|
||||||
|
ActiveStorage::VirusScanner.new(attachment.blob)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
22
lib/tasks/deployment/20190425102459_migrate_virus_scans.rake
Normal file
22
lib/tasks/deployment/20190425102459_migrate_virus_scans.rake
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace :after_party do
|
||||||
|
desc 'Deployment task: migrate_virus_scans'
|
||||||
|
task migrate_virus_scans: :environment do
|
||||||
|
puts "Running deploy task 'migrate_virus_scans'"
|
||||||
|
|
||||||
|
virus_scans = VirusScan.all
|
||||||
|
progress = ProgressReport.new(virus_scans.count)
|
||||||
|
virus_scans.find_each do |virus_scan|
|
||||||
|
blob = ActiveStorage::Blob.find_by(key: virus_scan.blob_key)
|
||||||
|
if blob
|
||||||
|
metadata = { virus_scan_result: virus_scan.status, scanned_at: virus_scan.scanned_at }
|
||||||
|
blob.update_column(:metadata, blob.metadata.merge(metadata))
|
||||||
|
end
|
||||||
|
progress.inc
|
||||||
|
end
|
||||||
|
progress.finish
|
||||||
|
|
||||||
|
# Update task as completed. If you remove the line below, the task will
|
||||||
|
# run with every deploy (or every time you call after_party:run).
|
||||||
|
AfterParty::TaskRecord.create version: '20190425102459'
|
||||||
|
end
|
||||||
|
end
|
|
@ -167,8 +167,8 @@ feature 'The user' do
|
||||||
expect(page).to have_text('analyse antivirus en cours')
|
expect(page).to have_text('analyse antivirus en cours')
|
||||||
|
|
||||||
# Mark file as scanned and clean
|
# Mark file as scanned and clean
|
||||||
virus_scan = VirusScan.last
|
attachment = ActiveStorage::Attachment.last
|
||||||
virus_scan.update(scanned_at: Time.zone.now, status: VirusScan.statuses.fetch(:safe))
|
attachment.blob.update(metadata: attachment.blob.metadata.merge(scanned_at: Time.zone.now, virus_scan_result: ActiveStorage::VirusScanner::SAFE))
|
||||||
within '.piece-justificative' do
|
within '.piece-justificative' do
|
||||||
click_on 'rafraichir'
|
click_on 'rafraichir'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
RSpec.describe AntiVirusJob, type: :job do
|
RSpec.describe VirusScannerJob, type: :job do
|
||||||
let(:champ) do
|
let(:champ) do
|
||||||
champ = create(:champ, :piece_justificative)
|
champ = create(:champ, :piece_justificative)
|
||||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||||
champ
|
champ
|
||||||
end
|
end
|
||||||
let(:virus_scan) { create(:virus_scan, status: VirusScan.statuses.fetch(:pending), champ: champ, blob_key: champ.piece_justificative_file.blob.key) }
|
|
||||||
|
|
||||||
subject { AntiVirusJob.new.perform(virus_scan) }
|
subject { VirusScannerJob.new.perform(champ.piece_justificative_file.blob) }
|
||||||
|
|
||||||
context "when no virus is found" do
|
context "when no virus is found" do
|
||||||
let(:virus_found?) { true }
|
let(:virus_found?) { true }
|
||||||
|
@ -16,7 +15,7 @@ RSpec.describe AntiVirusJob, type: :job do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(virus_scan.reload.status).to eq(VirusScan.statuses.fetch(:safe)) }
|
it { expect(champ.piece_justificative_file.virus_scanner.safe?).to be_truthy }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when a virus is found" do
|
context "when a virus is found" do
|
||||||
|
@ -27,6 +26,6 @@ RSpec.describe AntiVirusJob, type: :job do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
|
|
||||||
it { expect(virus_scan.reload.status).to eq(VirusScan.statuses.fetch(:infected)) }
|
it { expect(champ.piece_justificative_file.virus_scanner.infected?).to be_truthy }
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -383,20 +383,19 @@ describe Champ do
|
||||||
let(:type_de_champ) { create(:type_de_champ_piece_justificative) }
|
let(:type_de_champ) { create(:type_de_champ_piece_justificative) }
|
||||||
|
|
||||||
context 'and there is a blob' do
|
context 'and there is a blob' do
|
||||||
before { champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") }
|
before do
|
||||||
|
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||||
|
champ.save
|
||||||
|
end
|
||||||
|
|
||||||
it { expect { champ.save }.to change(VirusScan, :count).by(1) }
|
it { expect(champ.piece_justificative_file.virus_scanner.analyzed?).to be_truthy }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'and there is no blob' do
|
context 'and there is no blob' do
|
||||||
it { expect { champ.save }.to_not change(VirusScan, :count) }
|
before { champ.save }
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when type_champ is not type_de_champ_piece_justificative' do
|
it { expect(champ.piece_justificative_file.virus_scanner).to be_nil }
|
||||||
let(:type_de_champ) { create(:type_de_champ_textarea) }
|
end
|
||||||
|
|
||||||
it { expect { champ.save }.to_not change(VirusScan, :count) }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,24 @@
|
||||||
describe Champs::PieceJustificativeChamp do
|
describe Champs::PieceJustificativeChamp do
|
||||||
describe '#for_api' do
|
describe '#for_api' do
|
||||||
let(:champ_pj) { create(:champ_piece_justificative) }
|
let(:champ_pj) { create(:champ_piece_justificative) }
|
||||||
|
let(:metadata) { champ_pj.piece_justificative_file.blob.metadata }
|
||||||
|
|
||||||
before { champ_pj.virus_scan.update(status: status) }
|
before { champ_pj.piece_justificative_file.blob.update(metadata: metadata.merge(virus_scan_result: status)) }
|
||||||
|
|
||||||
subject { champ_pj.for_api }
|
subject { champ_pj.for_api }
|
||||||
|
|
||||||
context 'when file is safe' do
|
context 'when file is safe' do
|
||||||
let(:status) { 'safe' }
|
let(:status) { ActiveStorage::VirusScanner::SAFE }
|
||||||
it { is_expected.to include("/rails/active_storage/blobs/") }
|
it { is_expected.to include("/rails/active_storage/blobs/") }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when file is not scanned' do
|
context 'when file is not scanned' do
|
||||||
let(:status) { 'pending' }
|
let(:status) { ActiveStorage::VirusScanner::PENDING }
|
||||||
it { is_expected.to include("/rails/active_storage/blobs/") }
|
it { is_expected.to include("/rails/active_storage/blobs/") }
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when file is infected' do
|
context 'when file is infected' do
|
||||||
let(:status) { 'infected' }
|
let(:status) { ActiveStorage::VirusScanner::INFECTED }
|
||||||
it { is_expected.to be_nil }
|
it { is_expected.to be_nil }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe ChampSerializer do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
champ.piece_justificative_file.attach({ filename: __FILE__, io: File.open(__FILE__) })
|
champ.piece_justificative_file.attach({ filename: __FILE__, io: File.open(__FILE__) })
|
||||||
champ.reload.virus_scan.safe!
|
champ.piece_justificative_file.virus_scanner.analyze_later
|
||||||
end
|
end
|
||||||
after { champ.piece_justificative_file.purge }
|
after { champ.piece_justificative_file.purge }
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
describe 'shared/piece_jointe/_pj_link.html.haml', type: :view do
|
||||||
let(:champ) { create(:champ, :piece_justificative, :with_piece_justificative_file) }
|
let(:champ) { create(:champ_piece_justificative) }
|
||||||
let(:virus_scan) { nil }
|
let(:virus_scan_result) { nil }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
if virus_scan
|
champ.piece_justificative_file.blob.update(metadata: champ.piece_justificative_file.blob.metadata.merge(virus_scan_result: virus_scan_result))
|
||||||
champ.update(virus_scan: virus_scan)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
subject { render 'shared/champs/piece_justificative/pj_link', champ: champ, user_can_upload: false }
|
subject { render 'shared/piece_jointe/pj_link', object: champ, pj: champ.piece_justificative_file, user_can_upload: false }
|
||||||
|
|
||||||
context 'when there is no anti-virus scan' do
|
context 'when there is no anti-virus scan' do
|
||||||
let(:virus_scan) { nil }
|
let(:virus_scan_result) { nil }
|
||||||
|
|
||||||
it 'allows to download the file' do
|
it 'allows to download the file' do
|
||||||
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
||||||
|
@ -22,7 +20,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the anti-virus scan is pending' do
|
context 'when the anti-virus scan is pending' do
|
||||||
let(:virus_scan) { create(:virus_scan, :pending) }
|
let(:virus_scan_result) { ActiveStorage::VirusScanner::PENDING }
|
||||||
|
|
||||||
it 'displays the filename, but doesn’t allow to download the file' do
|
it 'displays the filename, but doesn’t allow to download the file' do
|
||||||
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
||||||
|
@ -32,7 +30,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file is scanned and safe' do
|
context 'when the file is scanned and safe' do
|
||||||
let(:virus_scan) { create(:virus_scan, :safe) }
|
let(:virus_scan_result) { ActiveStorage::VirusScanner::SAFE }
|
||||||
|
|
||||||
it 'allows to download the file' do
|
it 'allows to download the file' do
|
||||||
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_link(champ.piece_justificative_file.filename.to_s)
|
||||||
|
@ -40,7 +38,7 @@ describe 'shared/champs/piece_justificative/_pj_link.html.haml', type: :view do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when the file is scanned and infected' do
|
context 'when the file is scanned and infected' do
|
||||||
let(:virus_scan) { create(:virus_scan, :infected) }
|
let(:virus_scan_result) { ActiveStorage::VirusScanner::INFECTED }
|
||||||
|
|
||||||
it 'displays the filename, but doesn’t allow to download the file' do
|
it 'displays the filename, but doesn’t allow to download the file' do
|
||||||
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
expect(subject).to have_text(champ.piece_justificative_file.filename.to_s)
|
Loading…
Reference in a new issue