Scan files with clamdscan #77
@ -1,6 +1,20 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
import background_task.admin
|
||||||
|
import background_task.models
|
||||||
|
|
||||||
from .models import File
|
from .models import File, FileValidation
|
||||||
|
import files.signals
|
||||||
|
|
||||||
|
|
||||||
|
def scan_selected_files(self, request, queryset):
|
||||||
|
"""Scan selected files."""
|
||||||
|
for instance in queryset:
|
||||||
|
files.signals._initiate_scan(instance)
|
||||||
|
|
||||||
|
|
||||||
|
class FileValidationInlineAdmin(admin.TabularInline):
|
||||||
|
model = FileValidation
|
||||||
|
readonly_fields = ('date_created', 'date_modified', 'is_valid', 'results')
|
||||||
|
|
||||||
|
|
||||||
@admin.register(File)
|
@admin.register(File)
|
||||||
@ -77,3 +91,51 @@ class FileAdmin(admin.ModelAdmin):
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
inlines = [FileValidationInlineAdmin]
|
||||||
|
actions = [scan_selected_files]
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
admin.site.unregister(background_task.models.Task)
|
||||||
|
admin.site.unregister(background_task.models.CompletedTask)
|
||||||
|
except admin.site.NotRegistered:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TaskMixin:
|
||||||
|
"""Modify a few properties of background tasks displayed in admin."""
|
||||||
|
|
||||||
|
def no_errors(self, obj):
|
||||||
|
"""Replace background_task's "has_error".
|
||||||
|
|
||||||
|
Make Django's red/green boolean icons less confusing
|
||||||
|
in the context of "there's an error during task run".
|
||||||
|
"""
|
||||||
|
return not bool(obj.last_error)
|
||||||
|
|
||||||
|
no_errors.boolean = True
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(background_task.models.Task)
|
||||||
|
@admin.register(background_task.models.CompletedTask)
|
||||||
|
class TaskAdmin(background_task.admin.TaskAdmin, TaskMixin):
|
||||||
|
date_hierarchy = 'run_at'
|
||||||
|
list_display = [
|
||||||
|
'run_at',
|
||||||
|
'task_name',
|
||||||
|
'task_params',
|
||||||
|
'attempts',
|
||||||
|
'no_errors',
|
||||||
|
'locked_by',
|
||||||
|
'locked_by_pid_running',
|
||||||
|
]
|
||||||
|
list_filter = (
|
||||||
|
'task_name',
|
||||||
|
'run_at',
|
||||||
|
'failed_at',
|
||||||
|
'locked_at',
|
||||||
|
'attempts',
|
||||||
|
'creator_content_type',
|
||||||
|
)
|
||||||
|
search_fields = ['task_name', 'task_params', 'last_error', 'verbose_name']
|
||||||
|
@ -206,7 +206,7 @@ class File(CreatedModifiedMixin, TrackChangesMixin, SoftDeleteMixin, models.Mode
|
|||||||
|
|
||||||
|
|
||||||
class FileValidation(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
class FileValidation(CreatedModifiedMixin, TrackChangesMixin, models.Model):
|
||||||
track_changes_to_fields = {'is_valid', 'errors', 'warnings', 'notices', 'validation'}
|
track_changes_to_fields = {'is_valid', 'results'}
|
||||||
|
|
||||||
file = models.OneToOneField(File, related_name='validation', on_delete=models.CASCADE)
|
file = models.OneToOneField(File, related_name='validation', on_delete=models.CASCADE)
|
||||||
is_valid = models.BooleanField(default=False)
|
is_valid = models.BooleanField(default=False)
|
||||||
|
@ -16,6 +16,11 @@ def _record_changes(sender: object, instance: files.models.File, **kwargs: objec
|
|||||||
instance.record_status_change(was_changed, old_state, **kwargs)
|
instance.record_status_change(was_changed, old_state, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _initiate_scan(file: files.models.File) -> None:
|
||||||
|
logger.info('Initiating a scan for file pk=%s', file.pk)
|
||||||
|
files.tasks.scan(file_id=file.pk, creator=file, verbose_name=file.source.name)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=files.models.File)
|
@receiver(post_save, sender=files.models.File)
|
||||||
def _scan_new_file(
|
def _scan_new_file(
|
||||||
sender: object, instance: files.models.File, created: bool, **kwargs: object
|
sender: object, instance: files.models.File, created: bool, **kwargs: object
|
||||||
@ -23,5 +28,4 @@ def _scan_new_file(
|
|||||||
if not created:
|
if not created:
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info('Initiating a scan for file pk=%s', instance.pk)
|
_initiate_scan(instance)
|
||||||
files.tasks.scan(file_id=instance.pk, creator=instance)
|
|
||||||
|
@ -2,6 +2,7 @@ import logging
|
|||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from background_task import background
|
from background_task import background
|
||||||
|
from background_task.tasks import TaskSchedule
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
import files.models
|
import files.models
|
||||||
@ -10,7 +11,7 @@ import files.utils
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@background()
|
@background(schedule={'action': TaskSchedule.RESCHEDULE_EXISTING})
|
||||||
def scan(file_id: int):
|
def scan(file_id: int):
|
||||||
"""Run a scan on a given file and save its output as a FileValidation record."""
|
"""Run a scan on a given file and save its output as a FileValidation record."""
|
||||||
file = files.models.File.objects.get(pk=file_id)
|
file = files.models.File.objects.get(pk=file_id)
|
||||||
|
Loading…
Reference in New Issue
Block a user