diff --git a/edivorce/apps/core/serializer.py b/edivorce/apps/core/serializer.py index c756d1f3..644ab410 100644 --- a/edivorce/apps/core/serializer.py +++ b/edivorce/apps/core/serializer.py @@ -3,6 +3,7 @@ from rest_framework import serializers from rest_framework.exceptions import ValidationError from .models import Document, UserResponse +from .validators import * class UserResponseSerializer(serializers.ModelSerializer): @@ -23,22 +24,10 @@ class UserResponseSerializer(serializers.ModelSerializer): instance.save() -def valid_file_extension(file): - extension = file.name.split('.')[-1] - if extension.lower() not in ['pdf', 'png', 'gif', 'jpg', 'jpe', 'jpeg']: - raise ValidationError(f'File type not supported: {extension}') - - -def valid_doc_type(value): - valid_codes = ['AAI', 'AFDO', 'AFTL', 'CSA', 'EFSS', 'MC', 'NCV', 'OFI', 'RDP'] - if value.upper() not in valid_codes: - raise ValidationError(f'Doc type not supported: {value}. Valid codes: {", ".join(valid_codes)}') - - class CreateDocumentSerializer(serializers.ModelSerializer): doc_type = serializers.CharField(required=True, validators=[valid_doc_type]) party_code = serializers.IntegerField(min_value=0, max_value=2, required=True) - file = serializers.FileField(required=True, validators=[valid_file_extension]) + file = serializers.FileField(required=True, validators=[valid_file_extension,file_scan_validation]) filename = serializers.CharField(read_only=True) size = serializers.IntegerField(read_only=True) rotation = serializers.IntegerField(read_only=True) @@ -62,11 +51,6 @@ class CreateDocumentSerializer(serializers.ModelSerializer): return response -def valid_rotation(value): - if value % 90 != 0: - raise serializers.ValidationError('Rotation must be 0, 90, 180, or 270') - - class DocumentMetadataSerializer(serializers.ModelSerializer): doc_type = serializers.CharField(read_only=True) party_code = serializers.IntegerField(read_only=True) diff --git a/edivorce/apps/core/tests/test_api.py b/edivorce/apps/core/tests/test_api.py index 4da835c8..ad775003 100644 --- a/edivorce/apps/core/tests/test_api.py +++ b/edivorce/apps/core/tests/test_api.py @@ -72,7 +72,10 @@ class APITest(APITestCase): 'party_code': 1 } self.client.force_authenticate(self.user) - response = self.client.post(url, data) + + with self.settings(CLAMAV_ENABLED=False): + response = self.client.post(url, data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Document.objects.count(), 1) @@ -97,13 +100,18 @@ class APITest(APITestCase): 'party_code': 1 } self.client.force_authenticate(self.user) - response = self.client.post(url, data) + + with self.settings(CLAMAV_ENABLED=False): + response = self.client.post(url, data) self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(Document.objects.count(), 1) file.seek(0) # - response = self.client.post(url, data) + + with self.settings(CLAMAV_ENABLED=False): + response = self.client.post(url, data) + self.assertContains(response, 'This file appears to have already been uploaded for this document.', status_code=status.HTTP_400_BAD_REQUEST) diff --git a/edivorce/apps/core/tests/test_upload.py b/edivorce/apps/core/tests/test_upload.py index 0334dc57..1ce14564 100644 --- a/edivorce/apps/core/tests/test_upload.py +++ b/edivorce/apps/core/tests/test_upload.py @@ -2,15 +2,21 @@ import clamd from clamd import BufferTooLongError from unittest import mock -from django import forms from django.test import TestCase from django.core.files.uploadedfile import SimpleUploadedFile +from rest_framework import serializers +from rest_framework.test import APIRequestFactory from ..validators import file_scan_validation +from ..models import Document -class TestUploadForm(forms.Form): - upload = forms.FileField(validators=[file_scan_validation]) +class TestUploadSerializer(serializers.ModelSerializer): + upload = serializers.FileField(validators=[file_scan_validation]) + + class Meta: + model = Document + fields = ('upload', 'filename') class UploadScanTests(TestCase): @@ -18,17 +24,17 @@ class UploadScanTests(TestCase): def test_validation_disabled(self): with self.settings(CLAMAV_ENABLED=False): infected = SimpleUploadedFile('infected.txt', clamd.EICAR) - form = TestUploadForm(files={'upload': infected}) + serializer = TestUploadSerializer(data={'upload': infected}) - self.assertTrue(form.is_valid(), form.errors) + self.assertTrue(serializer.is_valid(), serializer.errors) def test_validation_invalid_network_connection(self): with self.settings(CLAMAV_TCP_PORT=9999): infected = SimpleUploadedFile('infected.txt', clamd.EICAR) - form = TestUploadForm(files={'upload': infected}) + serializer = TestUploadSerializer(data={'upload': infected}) - self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['upload'][0], 'Unable to scan file.') + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors['upload'][0], 'Unable to scan file.') @mock.patch('clamd.ClamdNetworkSocket.instream') def test_validation_buffer_overflow(self, mock_clam): @@ -36,26 +42,26 @@ class UploadScanTests(TestCase): # by default clamav has a 10mb limit for instream clean = SimpleUploadedFile('clean.txt', b'clean file') - form = TestUploadForm(files={'upload': clean}) + serializer = TestUploadSerializer(data={'upload': clean}) - self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['upload'][0], 'Unable to scan file.') + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors['upload'][0], 'Unable to scan file.') @mock.patch('clamd.ClamdNetworkSocket.instream') def test_validation_virus_found(self, mock_clam): mock_clam.return_value = {'stream': ('FOUND', 'Eicar-Test-Signature')} infected = SimpleUploadedFile('infected.txt', clamd.EICAR) - form = TestUploadForm(files={'upload': infected}) + serializer = TestUploadSerializer(data={'upload': infected}) - self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['upload'][0], 'Infected file found.') + self.assertFalse(serializer.is_valid()) + self.assertEqual(serializer.errors['upload'][0], 'Infected file found.') @mock.patch('clamd.ClamdNetworkSocket.instream') def test_validation_no_virus_found(self, mock_clam): mock_clam.return_value = {'stream': ('OK', None)} clean = SimpleUploadedFile('clean.txt', b'clean file') - form = TestUploadForm(files={'upload': clean}) + serializer = TestUploadSerializer(data={'upload': clean}) - self.assertTrue(form.is_valid()) + self.assertTrue(serializer.is_valid()) diff --git a/edivorce/apps/core/validators.py b/edivorce/apps/core/validators.py index adf83148..ded51f8b 100644 --- a/edivorce/apps/core/validators.py +++ b/edivorce/apps/core/validators.py @@ -2,7 +2,8 @@ import logging import clamd import sys -from django.core.exceptions import ValidationError +from rest_framework import serializers +from rest_framework.exceptions import ValidationError from django.conf import settings logger = logging.getLogger(__name__) @@ -42,3 +43,20 @@ def file_scan_validation(file): if result and result['stream'][0] == 'FOUND': logger.warning('Virus found: {}'.format(file.name)) raise ValidationError('Infected file found.', code='infected') + + +def valid_file_extension(file): + extension = file.name.split('.')[-1] + if extension.lower() not in ['pdf', 'png', 'gif', 'jpg', 'jpe', 'jpeg']: + raise ValidationError(f'File type not supported: {extension}') + + +def valid_doc_type(value): + valid_codes = ['AAI', 'AFDO', 'AFTL', 'CSA', 'EFSS', 'MC', 'NCV', 'OFI', 'RDP'] + if value.upper() not in valid_codes: + raise ValidationError(f'Doc type not supported: {value}. Valid codes: {", ".join(valid_codes)}') + + +def valid_rotation(value): + if value % 90 != 0: + raise serializers.ValidationError('Rotation must be 0, 90, 180, or 270')