commit
8bb4343b90
17 changed files with 173 additions and 7 deletions
20
app/jobs/anti_virus_job.rb
Normal file
20
app/jobs/anti_virus_job.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
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 = "safe"
|
||||
else
|
||||
status = "infected"
|
||||
end
|
||||
virus_scan.update(scanned_at: Time.now, status: status)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@ class Champ < ApplicationRecord
|
|||
belongs_to :type_de_champ, inverse_of: :champ
|
||||
has_many :commentaires
|
||||
has_one_attached :piece_justificative_file
|
||||
has_one :virus_scan
|
||||
|
||||
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, to: :type_de_champ
|
||||
|
||||
|
|
|
@ -1,2 +1,12 @@
|
|||
class Champs::PieceJustificativeChamp < Champ
|
||||
after_commit :create_virus_scan
|
||||
|
||||
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 = "pending"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
17
app/models/virus_scan.rb
Normal file
17
app/models/virus_scan.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
class VirusScan < ApplicationRecord
|
||||
belongs_to :champ
|
||||
|
||||
enum status: {
|
||||
pending: 'pending',
|
||||
safe: 'safe',
|
||||
infected: 'infected',
|
||||
}
|
||||
|
||||
validates :champ_id, uniqueness: { scope: :blob_key }
|
||||
|
||||
after_create :perform_scan
|
||||
|
||||
def perform_scan
|
||||
AntiVirusJob.perform_later(self)
|
||||
end
|
||||
end
|
|
@ -47,9 +47,7 @@
|
|||
- else
|
||||
Pas de dossier associé
|
||||
- elsif champ.type_champ == 'piece_justificative'
|
||||
- pj = champ.piece_justificative_file
|
||||
%a{ href: url_for(pj), target: '_blank' }
|
||||
= pj.filename.to_s
|
||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
||||
- elsif champ.type_champ == 'textarea'
|
||||
= simple_format(champ.decorate.value)
|
||||
- else
|
||||
|
|
|
@ -36,8 +36,7 @@
|
|||
%td.rich-text
|
||||
- pj = c.piece_justificative_file
|
||||
- if pj.attached?
|
||||
%a{ href: url_for(pj), target: '_blank' }
|
||||
= pj.filename.to_s
|
||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: c, user_can_upload: false }
|
||||
- else
|
||||
Pièce justificative non fournie
|
||||
- when "textarea"
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
- pj = champ.piece_justificative_file
|
||||
- if champ.virus_scan.present?
|
||||
- if champ.virus_scan.safe?
|
||||
= link_to pj.filename.to_s, url_for(pj), target: '_blank'
|
||||
- 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é)
|
||||
- else
|
||||
= link_to pj.filename.to_s, url_for(pj), target: '_blank'
|
||||
(ce fichier n'a pas été analysé par notre antivirus, téléchargez-le avec précaution)
|
|
@ -10,8 +10,7 @@
|
|||
id: "champs_#{champ.id}",
|
||||
direct_upload: true
|
||||
- else
|
||||
%a{ href: url_for(pj), target: '_blank' }
|
||||
= pj.filename.to_s
|
||||
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
||||
%br
|
||||
Modifier :
|
||||
= form.file_field :piece_justificative_file,
|
||||
|
|
|
@ -47,6 +47,7 @@ Rails.application.configure do
|
|||
}
|
||||
|
||||
config.active_job.queue_adapter = :test
|
||||
config.active_storage.service = :test
|
||||
|
||||
# Raises error for missing translations
|
||||
# config.action_view.raise_on_missing_translations = true
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
local:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("storage") %>
|
||||
|
||||
test:
|
||||
service: Disk
|
||||
root: <%= Rails.root.join("tmp/storage") %>
|
||||
|
|
12
db/migrate/20180511124302_create_virus_scans.rb
Normal file
12
db/migrate/20180511124302_create_virus_scans.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
class CreateVirusScans < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
create_table :virus_scans do |t|
|
||||
t.datetime :scanned_at
|
||||
t.string :status
|
||||
t.references :champ, index: true
|
||||
t.string :blob_key
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
10
db/schema.rb
10
db/schema.rb
|
@ -594,6 +594,16 @@ ActiveRecord::Schema.define(version: 2018_06_01_084546) do
|
|||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||
end
|
||||
|
||||
create_table "virus_scans", force: :cascade do |t|
|
||||
t.datetime "scanned_at"
|
||||
t.string "status"
|
||||
t.bigint "champ_id"
|
||||
t.string "blob_key"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["champ_id"], name: "index_virus_scans_on_champ_id"
|
||||
end
|
||||
|
||||
create_table "without_continuation_mails", id: :serial, force: :cascade do |t|
|
||||
t.text "body"
|
||||
t.string "subject"
|
||||
|
|
7
lib/tasks/2018_06_04_scan_pjs.rake
Normal file
7
lib/tasks/2018_06_04_scan_pjs.rake
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace :'2018_06_04_scan_pjs' do
|
||||
task scan_all: :environment do
|
||||
Champs::PieceJustificativeChamp.all.each do |pj_champ|
|
||||
pj_champ.create_virus_scan
|
||||
end
|
||||
end
|
||||
end
|
|
@ -17,5 +17,15 @@ FactoryBot.define do
|
|||
trait :dossier_link do
|
||||
type_de_champ { create(:type_de_champ_dossier_link) }
|
||||
end
|
||||
|
||||
trait :piece_justificative do
|
||||
type_de_champ { create(:type_de_champ_piece_justificative) }
|
||||
end
|
||||
|
||||
trait :with_piece_justificative_file do
|
||||
after(:create) do |champ, evaluator|
|
||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
4
spec/factories/virus_scan.rb
Normal file
4
spec/factories/virus_scan.rb
Normal file
|
@ -0,0 +1,4 @@
|
|||
FactoryBot.define do
|
||||
factory :virus_scan do
|
||||
end
|
||||
end
|
32
spec/jobs/anti_virus_job_spec.rb
Normal file
32
spec/jobs/anti_virus_job_spec.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
RSpec.describe AntiVirusJob, type: :job do
|
||||
let(:champ) do
|
||||
champ = create(:champ, :piece_justificative)
|
||||
champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain")
|
||||
champ
|
||||
end
|
||||
let(:virus_scan) { create(:virus_scan, status: "pending", champ: champ, blob_key: champ.piece_justificative_file.blob.key) }
|
||||
|
||||
subject { AntiVirusJob.new.perform(virus_scan) }
|
||||
|
||||
context "when no virus is found" do
|
||||
let(:virus_found?) { true }
|
||||
|
||||
before do
|
||||
allow(ClamavService).to receive(:safe_file?).and_return(virus_found?)
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(virus_scan.reload.status).to eq("safe") }
|
||||
end
|
||||
|
||||
context "when a virus is found" do
|
||||
let(:virus_found?) { false }
|
||||
|
||||
before do
|
||||
allow(ClamavService).to receive(:safe_file?).and_return(virus_found?)
|
||||
subject
|
||||
end
|
||||
|
||||
it { expect(virus_scan.reload.status).to eq("infected") }
|
||||
end
|
||||
end
|
|
@ -127,4 +127,28 @@ describe Champ do
|
|||
it { expect(champ.for_export).to eq('Crétinier, Mousserie') }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#enqueue_virus_check' do
|
||||
let(:champ) { type_de_champ.champ.build(value: nil) }
|
||||
|
||||
context 'when type_champ is type_de_champ_piece_justificative' do
|
||||
let(:type_de_champ) { create(:type_de_champ_piece_justificative) }
|
||||
|
||||
context 'and there is a blob' do
|
||||
before { champ.piece_justificative_file.attach(io: StringIO.new("toto"), filename: "toto.txt", content_type: "text/plain") }
|
||||
|
||||
it { expect{ champ.save }.to change(VirusScan, :count).by(1) }
|
||||
end
|
||||
|
||||
context 'and there is no blob' do
|
||||
it { expect{ champ.save }.to_not change(VirusScan, :count) }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when type_champ is not type_de_champ_piece_justificative' do
|
||||
let(:type_de_champ) { create(:type_de_champ_textarea) }
|
||||
|
||||
it { expect{ champ.save }.to_not change(VirusScan, :count) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue