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
|
belongs_to :type_de_champ, inverse_of: :champ
|
||||||
has_many :commentaires
|
has_many :commentaires
|
||||||
has_one_attached :piece_justificative_file
|
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
|
delegate :libelle, :type_champ, :order_place, :mandatory?, :description, :drop_down_list, to: :type_de_champ
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,12 @@
|
||||||
class Champs::PieceJustificativeChamp < Champ
|
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
|
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
|
- else
|
||||||
Pas de dossier associé
|
Pas de dossier associé
|
||||||
- elsif champ.type_champ == 'piece_justificative'
|
- elsif champ.type_champ == 'piece_justificative'
|
||||||
- pj = champ.piece_justificative_file
|
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
||||||
%a{ href: url_for(pj), target: '_blank' }
|
|
||||||
= pj.filename.to_s
|
|
||||||
- elsif champ.type_champ == 'textarea'
|
- elsif champ.type_champ == 'textarea'
|
||||||
= simple_format(champ.decorate.value)
|
= simple_format(champ.decorate.value)
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -36,8 +36,7 @@
|
||||||
%td.rich-text
|
%td.rich-text
|
||||||
- pj = c.piece_justificative_file
|
- pj = c.piece_justificative_file
|
||||||
- if pj.attached?
|
- if pj.attached?
|
||||||
%a{ href: url_for(pj), target: '_blank' }
|
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: c, user_can_upload: false }
|
||||||
= pj.filename.to_s
|
|
||||||
- else
|
- else
|
||||||
Pièce justificative non fournie
|
Pièce justificative non fournie
|
||||||
- when "textarea"
|
- 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}",
|
id: "champs_#{champ.id}",
|
||||||
direct_upload: true
|
direct_upload: true
|
||||||
- else
|
- else
|
||||||
%a{ href: url_for(pj), target: '_blank' }
|
= render partial: "shared/champs/piece_justificative/pj_link", locals: { champ: champ, user_can_upload: true }
|
||||||
= pj.filename.to_s
|
|
||||||
%br
|
%br
|
||||||
Modifier :
|
Modifier :
|
||||||
= form.file_field :piece_justificative_file,
|
= form.file_field :piece_justificative_file,
|
||||||
|
|
|
@ -47,6 +47,7 @@ Rails.application.configure do
|
||||||
}
|
}
|
||||||
|
|
||||||
config.active_job.queue_adapter = :test
|
config.active_job.queue_adapter = :test
|
||||||
|
config.active_storage.service = :test
|
||||||
|
|
||||||
# Raises error for missing translations
|
# Raises error for missing translations
|
||||||
# config.action_view.raise_on_missing_translations = true
|
# config.action_view.raise_on_missing_translations = true
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
local:
|
local:
|
||||||
service: Disk
|
service: Disk
|
||||||
root: <%= Rails.root.join("storage") %>
|
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
|
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||||
end
|
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|
|
create_table "without_continuation_mails", id: :serial, force: :cascade do |t|
|
||||||
t.text "body"
|
t.text "body"
|
||||||
t.string "subject"
|
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
|
trait :dossier_link do
|
||||||
type_de_champ { create(:type_de_champ_dossier_link) }
|
type_de_champ { create(:type_de_champ_dossier_link) }
|
||||||
end
|
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
|
||||||
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') }
|
it { expect(champ.for_export).to eq('Crétinier, Mousserie') }
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
Loading…
Reference in a new issue