def setUp(self):
        global store_upload
        # Set storage backend to sftp storage
        local_settings.STORAGES_BACKEND = \
            'storages.backends.sftpstorage.SFTPStorage'

        # Setting up a mock for the storage backend based on examples at
        # https://docs.python.org/3.7/library/unittest.mock-examples.html# \
        # applying-the-same-patch-to-every-test-method
        patcher = patch('storages.backends.sftpstorage.SFTPStorage')
        patcher.start()
        self.addCleanup(patcher.stop)

        # Mock the temporary storage object so that we're not unnecessarily
        # saving files to the local file system.
        patcher2 = patch('django.core.files.storage.FileSystemStorage')
        patcher2.start()
        self.addCleanup(patcher2.stop)
        # Reinitialse stored upload after we changed the STORAGES_BACKEND
        # class name above. This ensures that we're looking at the mocked
        # backend class.
        import django_drf_filepond.api
        self.api = django_drf_filepond.api
        django_drf_filepond.api.storage_backend_initialised = False
        django_drf_filepond.api._init_storage_backend()
        self.mock_storage_backend = django_drf_filepond.api.storage_backend
        store_upload = django_drf_filepond.api.store_upload

        # Check that we're using a mocked storage backend
        self.assertTrue(
            isinstance(django_drf_filepond.api.storage_backend, MagicMock),
            ('The created storage backend should be mocked but it is not of '
             'type unittest.mock.MagicMock...'))

        # Set up an initial file upload
        self.upload_id = _get_file_id()
        self.upload_id2 = _get_file_id()
        self.file_id = _get_file_id()
        self.file_content = ('This is some test file data for an '
                             'uploaded file.')
        self.fn = 'my_test_file.txt'
        self.test_target_filename = '/test_storage/testfile.txt'
        self.test_target_filename2 = '/test_storage/testfile2.txt'
        uploaded_file = MagicMock(spec=SimpleUploadedFile)
        uploaded_file.name = self.fn
        tu = TemporaryUpload(upload_id=self.upload_id,
                             file_id=self.file_id,
                             file=uploaded_file,
                             upload_name=self.fn,
                             upload_type=TemporaryUpload.FILE_DATA)
        tu.save()

        tu2 = TemporaryUpload(upload_id=self.upload_id2,
                              file_id=self.file_id,
                              file=uploaded_file,
                              upload_name=self.fn,
                              upload_type=TemporaryUpload.FILE_DATA)
        tu2.save()
Exemple #2
0
 def setUp(self):
     self.upload_id = _get_file_id()
     self.file_id = _get_file_id()
     self.upload_name = 'my_test_file.png'
     self.uploader = FilepondChunkedFileUploader()
     self.request = MagicMock(spec=Request)
     self.request.user = AnonymousUser()
     self.request.data = ensure_text(
         'This is the test upload chunk data...')
Exemple #3
0
 def setUp(self):
     self.file_id = _get_file_id()
     self.upload_id = _get_file_id()
     self.file_name = 'my_uploaded_file.txt'
     self.request = MagicMock(spec=Request)
     self.request.user = AnonymousUser()
     file_obj = MagicMock(spec=InMemoryUploadedFile)
     file_obj.name = self.file_name
     self.request.data = _setupRequestData({'filepond': ['{}', file_obj]})
     self.uploader = FilepondStandardFileUploader()
Exemple #4
0
    def setUp(self):
        self.file_store_path = getattr(local_settings,
                                       'FILE_STORE_PATH', None)

        local_settings.STORAGES_BACKEND = \
            'storages.backends.sftpstorage.SFTPStorage'

        # Setting up a mock for the storage backend based on examples at
        # https://docs.python.org/3.7/library/unittest.mock-examples.html# \
        # applying-the-same-patch-to-every-test-method
        patcher = patch('storages.backends.sftpstorage.SFTPStorage')
        patcher.start()
        self.addCleanup(patcher.stop)

        # Mock the temporary storage object so that we're not unnecessarily
        # saving files to the local file system.
        patcher2 = patch('django.core.files.storage.FileSystemStorage')
        patcher2.start()
        self.addCleanup(patcher2.stop)

        # Reinitialse stored upload after we changed the STORAGES_BACKEND
        # class name above. This ensures that we're looking at the mocked
        # backend class.
        import django_drf_filepond.api
        django_drf_filepond.api.storage_backend_initialised = False
        django_drf_filepond.api._init_storage_backend()
        self.mock_storage_backend = django_drf_filepond.api.storage_backend

        # Set up an initial file upload
        self.upload_id = _get_file_id()
        self.file_id = _get_file_id()
        self.file_content = (b'This is some test file data for an '
                             b'uploaded file.')
        self.fn = 'my_test_file.txt'
        self.test_filename = 'sdf5dua32defh754dhsrr2'

        # Set up the mock_storage_backend.open to return the file content
        # In relation to the changes for #31, file is now a FileField
        # Since only a string is stored to the DB, the creation of the
        # StoredUpload object below is fine but we need to mock the FileField
        # object returned.
        self.mock_storage_backend.open.return_value = BytesIO(
            self.file_content)

        # Override storage object configured for the FileField in StoredUpload
        # at original init time since this happens before setUp is run.
        StoredUpload.file.field.storage = self.mock_storage_backend

        # Now set up a stored version of this upload
        su = StoredUpload(upload_id=self.upload_id,
                          file=('%s' % (self.fn)),
                          uploaded=timezone.now())
        su.file.storage = self.mock_storage_backend
        su.save()
Exemple #5
0
 def setUp(self):
     # Set up a database containing a mock file record
     data = BytesIO()
     data.write(os.urandom(16384))
     self.base_upload_dir_at_startup = os.path.exists(storage.location)
     self.file_id = _get_file_id()
     self.upload_id = _get_file_id()
     test_file = SimpleUploadedFile(self.file_id, data.read())
     self.tu = TemporaryUpload.objects.create(
         upload_id=self.upload_id,
         file_id=self.file_id,
         file=test_file,
         upload_name='testfile.txt',
         upload_type=TemporaryUpload.FILE_DATA)
     self.tu.save()
 def test_get_file_id(self):
     fid = _get_file_id()
     self.assertTrue(isinstance(fid, text_type),
                     'The file ID must be a string.')
     id_format = re.compile('^([%s]){22}$' % (shortuuid.get_alphabet()))
     self.assertRegex(fid, id_format, ('The generated ID does not match '
                                       'the defined ID format.'))
Exemple #7
0
 def test_upload_id_valid(self):
     # _get_file_id is currently used for getting both file+upload IDs
     # since the spec for both is the same at present.
     upload_id = _get_file_id()
     self.assertTrue(
         FilepondFileUploader._upload_id_valid(upload_id),
         'A valid upload ID has been provided and this test should pass!')
Exemple #8
0
 def test_chunk_restart_invalid_id(self):
     self._setup_tuc()
     new_upload_id = _get_file_id()
     res = self.uploader._handle_chunk_restart(self.request, new_upload_id)
     res = self._prep_response(res)
     self.assertContains(res, 'Invalid upload ID specified.',
                         status_code=404)
Exemple #9
0
 def test_file_id_wrong_data_type(self):
     # Test using bytes instead of str
     file_id = six.ensure_binary(_get_file_id())
     self.assertFalse(
         FilepondFileUploader._file_id_valid(file_id),
         ('The provided file ID is of the wrong data type, this test '
          'should fail.'))
 def test_patch_invalid_request_content_type(self):
     chunk_id = _get_file_id()
     req_url = reverse('patch', args=[chunk_id])
     LOG.debug('About to run patch test with req_url: %s' % req_url)
     response = self.client.patch(req_url, data='Some byte data'.encode(),
                                  content_type='image/png')
     self.assertContains(response, 'Unsupported media type',
                         status_code=415)
Exemple #11
0
    def setUp(self):
        self.storage_backend = local_settings.STORAGES_BACKEND
        # Set storage backend to sftp storage
        local_settings.STORAGES_BACKEND = \
            'storages.backends.sftpstorage.SFTPStorage'

        # Setting up a mock for the storage backend based on examples at
        # https://docs.python.org/3.7/library/unittest.mock-examples.html# \
        # applying-the-same-patch-to-every-test-method
        patcher = patch('storages.backends.sftpstorage.SFTPStorage')
        patcher.start()
        self.addCleanup(patcher.stop)

        # Mock the temporary storage object so that we're not unnecessarily
        # saving files to the local file system.
        patcher2 = patch('django.core.files.storage.FileSystemStorage')
        patcher2.start()
        self.addCleanup(patcher2.stop)
        # Reinitialse stored upload after we changed the STORAGES_BACKEND
        # class name above. This ensures that we're looking at the mocked
        # backend class.
        import django_drf_filepond.api
        self.api = django_drf_filepond.api
        django_drf_filepond.api.storage_backend_initialised = False
        django_drf_filepond.api._init_storage_backend()
        self.mock_storage_backend = django_drf_filepond.api.storage_backend
        self.delete_upload = django_drf_filepond.api.delete_stored_upload

        # Check that we're using a mocked storage backend
        self.assertTrue(
            isinstance(django_drf_filepond.api.storage_backend, MagicMock),
            ('The created storage backend should be mocked but it is not of '
             'type unittest.mock.MagicMock...'))

        # Set up a "StoredUpload"
        self.upload_id = _get_file_id()
        self.file_id = _get_file_id()
        # self.file_content = ('This is some test file data for an '
        #                      'uploaded file.')
        self.fn = 'my_test_file.txt'
        self.test_target_filepath = os.path.join('test_storage', self.fn)
        self.su = StoredUpload(upload_id=self.upload_id,
                               file=self.test_target_filepath,
                               uploaded=timezone.now(),
                               stored=timezone.now())
        self.su.save()
    def post(self, request):
        LOG.debug('Filepond API: Process view POST called...')

        # Check that the temporary upload directory has been set
        if not hasattr(local_settings, 'UPLOAD_TMP'):
            return Response(
                'The file upload path settings are not '
                'configured correctly.',
                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # By default, enforce that the temporary upload location must be a
        # sub-directory of the project base directory.
        # TODO: Check whether this is necessary - maybe add a security
        # parameter that can be disabled to turn off this check if the
        # developer wishes?
        if ((not (storage.location).startswith(local_settings.BASE_DIR))
                and (local_settings.BASE_DIR != os.path.dirname(
                    django_drf_filepond.__file__))):
            if not local_settings.ALLOW_EXTERNAL_UPLOAD_DIR:
                return Response(
                    'The file upload path settings are not '
                    'configured correctly.',
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # Check that a relative path is not being used to store the
        # upload outside the specified UPLOAD_TMP directory.
        if not getattr(local_settings, 'UPLOAD_TMP').startswith(
                os.path.abspath(storage.location)):
            return Response(
                'An invalid storage location has been '
                'specified.',
                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        # Check that we've received a file and then generate a unique ID
        # for it. Also generate a unique UD for the temp upload dir
        file_id = _get_file_id()
        upload_id = _get_file_id()

        try:
            uploader = FilepondFileUploader.get_uploader(request)
            response = uploader.handle_upload(request, file_id, upload_id)
        except ParseError as e:
            # Re-raise the ParseError to trigger a 400 response via DRF.
            raise e

        return response
Exemple #13
0
 def test_file_id_clashes(self):
     GENERATED_IDS = 5000
     LOG.debug('Generating list of many file ids...')
     file_ids = [_get_file_id() for _ in range(GENERATED_IDS)]
     file_id_set = set(file_ids)
     LOG.debug('File id list generated...')
     self.assertEqual(len(file_id_set), GENERATED_IDS, 'There were '
                      'clashes in the generated file IDs!')
Exemple #14
0
 def test_upload_id_wrong_data_type(self):
     # _get_file_id is currently used for getting both file+upload IDs
     # since the spec for both is the same at present. Test using bytes
     # instead of str
     upload_id = _get_file_id().encode()
     self.assertFalse(
         FilepondFileUploader._upload_id_valid(upload_id),
         ('The provided upload ID is of the wrong data type, this test '
          'should fail.'))
Exemple #15
0
 def test_store_upload_local_direct_missing_store_path(self):
     fsp = local_settings.FILE_STORE_PATH
     test_dir = '/tmp/%s' % _get_file_id()
     local_settings.FILE_STORE_PATH = test_dir
     with self.assertRaisesMessage(
             FileNotFoundError,
             'The local output directory [%s] defined by '
             'FILE_STORE_PATH is missing.' % test_dir):
         _store_upload_local('/test_storage', 'test_file.txt', None)
     local_settings.FILE_STORE_PATH = fsp
 def test_patch_valid_request(self, mock_hcu):
     chunk_id = _get_file_id()
     req_url = reverse('patch', args=[chunk_id])
     LOG.debug('About to run patch test with req_url: %s' % req_url)
     mock_hcu.return_value = Response(chunk_id, status=status.HTTP_200_OK,
                                      content_type='text/plain')
     response = self.client.patch(
         req_url, data='Some byte data'.encode(),
         content_type='application/offset+octet-stream')
     self.assertContains(response, chunk_id, status_code=200)
 def setUp(self):
     # Set up an initial file upload
     self.upload_id = _get_file_id()
     self.file_id = _get_file_id()
     self.file_content = ('This is some test file data for an '
                          'uploaded file.')
     self.fn = 'my_test_file.txt'
     self.test_target_filename = '/test_storage/testfile.txt'
     self.su = StoredUpload(upload_id=self.upload_id,
                            file=self.test_target_filename[1:],
                            uploaded=timezone.now())
     self.su.save()
     # Create file
     self.file_store_path = getattr(local_settings, 'FILE_STORE_PATH', None)
     file_full_path = os.path.join(self.file_store_path,
                                   self.test_target_filename[1:])
     if not os.path.exists(os.path.dirname(file_full_path)):
         os.mkdir(os.path.dirname(file_full_path))
     with open(file_full_path, 'w') as f:
         f.write(self.file_content)
    def setUp(self):
        # Set up an initial file upload
        self.upload_id = _get_file_id()
        self.file_id = _get_file_id()
        self.file_content = ('This is some test file data for an '
                             'uploaded file.')
        self.fn = 'my_test_file.txt'
        self.test_filename = 'sdf5dua32defh754dhsrr2'
        uploaded_file = SimpleUploadedFile(self.fn,
                                           str.encode(self.file_content))
        tu = TemporaryUpload(upload_id=self.upload_id,
                             file_id=self.file_id,
                             file=uploaded_file,
                             upload_name=self.fn,
                             upload_type=TemporaryUpload.FILE_DATA)
        tu.save()

        # Now set up a stored version of this upload
        su = StoredUpload(upload_id=self.upload_id,
                          file=('%s' % (self.fn)),
                          uploaded=tu.uploaded)
        su.save()
 def test_head_request_valid(self, mock_hcr):
     upload_id = _get_file_id()
     req_url = reverse('patch', args=[upload_id])
     mock_hcr.return_value = Response(
         upload_id, status=status.HTTP_200_OK,
         headers={'Upload-Offset': '100000'},
         content_type='text/plain')
     response = self.client.head(req_url)
     self.assertEqual(response.status_code, 200)
     self.assertTrue(
         response.has_header('Upload-Offset'),
         'Offset header is missing from chunk restart response.')
     self.assertEqual(response['Upload-Offset'], '100000',
                      'Upload-Offset header has wrong value in response.')
Exemple #20
0
    def setUp(self):
        # Set up an initial file upload
        self.upload_id = _get_file_id()
        self.upload_id2 = _get_file_id()
        self.file_id = _get_file_id()
        self.file_content = ('This is some test file data for an '
                             'uploaded file.')
        self.fn = 'my_test_file.txt'
        self.test_target_dirname = 'test_storage/'
        self.test_target_filename = '/test_storage/testfile.txt'
        self.test_target_filename2 = '/test_storage/testfile2.txt'
        uploaded_file = SimpleUploadedFile(self.fn,
                                           str.encode(self.file_content))
        tu = TemporaryUpload(upload_id=self.upload_id,
                             file_id=self.file_id,
                             file=uploaded_file, upload_name=self.fn,
                             upload_type=TemporaryUpload.FILE_DATA)
        tu.save()

        tu2 = TemporaryUpload(upload_id=self.upload_id2,
                              file_id=self.file_id,
                              file=uploaded_file, upload_name=self.fn,
                              upload_type=TemporaryUpload.FILE_DATA)
        tu2.save()
    def head(self, request):
        LOG.debug('Filepond API: Fetch view HEAD called...')
        result = self._process_request(request)
        if isinstance(result, tuple):
            buf, file_id, upload_file_name, content_type = result
        elif isinstance(result, Response):
            return result
        else:
            raise ValueError('process_request result is of an unexpected type')

        file_size = buf.seek(0, os.SEEK_END)
        buf.seek(0)

        # The addressing of filepond issue #154
        # (https://github.com/pqina/filepond/issues/154) means that fetch
        # can now store a file downloaded from a remote URL and return file
        # metadata in the header if a HEAD request is received. If we get a
        # GET request then the standard approach of proxying the file back
        # to the client is used.
        upload_id = _get_file_id()
        memfile = InMemoryUploadedFile(buf, None, file_id, content_type,
                                       file_size, None)
        tu = TemporaryUpload(upload_id=upload_id,
                             file_id=file_id,
                             file=memfile,
                             upload_name=upload_file_name,
                             upload_type=TemporaryUpload.URL,
                             uploaded_by=_get_user(request))
        tu.save()

        response = Response(status=status.HTTP_200_OK)
        response['Content-Type'] = content_type
        response['Content-Length'] = file_size
        response['X-Content-Transfer-Id'] = upload_id
        response['Content-Disposition'] = ('inline; filename=%s' %
                                           upload_file_name)
        return response
    def _process_request(self, request):
        LOG.debug('Filepond API: Fetch view GET called...')
        '''
        Supports retrieving a file on the server side that the user has
        specified by calling addFile on the filepond API and passing a
        URL to a file.
        '''
        # Retrieve the target URL from the request query string target
        # target parameter, pull the file into temp upload storage and
        # return a file object.

        # First check we have a URL and parse to check it's valid
        target_url = request.query_params.get('target', None)
        if not target_url:
            raise ParseError('Required query parameter(s) missing.')

        # Use Django's URL validator to see if we've been given a valid URL
        validator = URLValidator(message=('An invalid URL <%s> has been '
                                          'provided' % (target_url)))
        try:
            validator(target_url)
        except ValidationError as e:
            raise ParseError(str(e))

        # TODO: SHould we check the headers returned when we request the
        # download to see that we're getting a file rather than an HTML page?
        # For now this check is enabled on the basis that we assume target
        # data file will not be HTML. However, there should be a way to turn
        # this off if the client knows that they want to get an HTML file.
        # TODO: *** Looks like this use of head can be removed since with
        # the new approach of streaming content to a BytesIO object, when
        # stream=True, the connection begins by being opened and only
        # fetching the headers. We could do this check then.
        try:
            header = requests.head(target_url, allow_redirects=True)
        except ConnectionError as e:
            msg = ('Unable to access the requested remote file headers: %s' %
                   str(e))
            LOG.error(msg)
            return Response(msg, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        if header.status_code == 404:
            raise NotFound('The remote file was not found.')

        content_type = header.headers.get('Content-Type', '')

        # If the URL has returned URL content but an HTML file was not
        # requested then assume that the URL has linked to a download page or
        # some sort of error page or similar and raise an error.
        if 'html' in content_type.lower() and '.html' not in target_url:
            LOG.error('The requested data seems to be in HTML format. '
                      'Assuming this is not valid data file.')
            raise ParseError('Provided URL links to HTML content.')

        buf = BytesIO()
        upload_file_name = None
        try:
            with requests.get(target_url, allow_redirects=True,
                              stream=True) as r:
                if 'Content-Disposition' in r.headers:
                    cd = r.headers['Content-Disposition']
                    matches = re.findall('filename=(.+)', cd)
                    if len(matches):
                        upload_file_name = matches[0]
                for chunk in r.iter_content(chunk_size=1048576):
                    buf.write(chunk)
        except ConnectionError as e:
            raise NotFound('Unable to access the requested remote file: %s' %
                           str(e))

        file_id = _get_file_id()
        # If filename wasn't extracted from Content-Disposition header, get
        # from the URL or otherwise set it to the auto-generated file_id
        if not upload_file_name:
            if not target_url.endswith('/'):
                split = target_url.rsplit('/', 1)
                upload_file_name = split[1] if len(split) > 1 else split[0]
            else:
                upload_file_name = file_id

        return (buf, file_id, upload_file_name, content_type)
Exemple #23
0
 def test_file_id_valid(self):
     file_id = _get_file_id()
     self.assertTrue(
         FilepondFileUploader._file_id_valid(file_id),
         'A valid file ID has been provided and this test should pass!')
Exemple #24
0
 def test_file_id(self):
     file_id = _get_file_id()
     self.assertEqual(len(file_id), 22,
                      'Incorrect length for generated file ID.')
Exemple #25
0
 def setUp(self):
     self.project = prepare_project(self.task)
     self.user = self.project.admin
     self.data_path = pathlib.Path(__file__).parent / "data"
     self.upload_id = _get_file_id()