diff --git a/beat/web/algorithms/api.py b/beat/web/algorithms/api.py index f0f4912bfaf09aaae6d52e06419a53dcf3062aa8..c40350671051cf229da92380f6431a9bed572930 100755 --- a/beat/web/algorithms/api.py +++ b/beat/web/algorithms/api.py @@ -27,6 +27,12 @@ from django.http import Http404 from django.http import HttpResponse +from django.http import HttpResponseForbidden +from django.http import HttpResponseBadRequest +from django.shortcuts import get_object_or_404 +from django.conf import settings + +import os from .models import Algorithm from .serializers import AlgorithmSerializer @@ -130,6 +136,9 @@ def binary(request, author_name, object_name, version=None): """Returns the shared library of a binary algorithm """ + if request.method not in ['GET', 'POST']: + return HttpResponseNotAllowed(['GET', 'POST']) + # Retrieves the algorithm if version: algorithm = get_object_or_404( @@ -146,19 +155,34 @@ def binary(request, author_name, object_name, version=None): else: algorithm = algorithm[0] - (has_access, _, accessibility) = algorithm.accessibility_for(request.user, without_usable=True) - - if not has_access: - raise Http404() - if not algorithm.is_binary(): raise Http404() - binary_data = algorithm.source_code + if request.method == 'GET': + (has_access, _, accessibility) = algorithm.accessibility_for(request.user, without_usable=True) + + if not has_access: + raise Http404() + + binary_data = algorithm.source_code + + response = HttpResponse(binary_data, content_type='application/octet-stream') + + response['Content-Length'] = len(binary_data) + response['Content-Disposition'] = 'attachment; filename=%d.so' % algorithm.version + + return response + + else: + if request.user.is_anonymous() or (request.user.username != author_name): + return HttpResponseForbidden() - response = HttpResponse(binary_data, content_type='application/octet-stream') + if not request.FILES.has_key('binary'): + return HttpResponseBadRequest() - response['Content-Length'] = len(binary_data) - response['Content-Disposition'] = 'attachment; filename=%d.so' % algorithm.version + file = request.FILES['binary'] + with open(os.path.join(settings.ALGORITHMS_ROOT, algorithm.source_code_filename()), 'wb') as dest: + for chunk in file.chunks(): + dest.write(chunk) - return response + return HttpResponse(status=204) diff --git a/beat/web/algorithms/tests/core.py b/beat/web/algorithms/tests/core.py index 421f055087dfae0e6fc1f29b31fe8bc24c939ae4..ed684d6646c46f2069583e87bd24cb4f0dc1b986 100755 --- a/beat/web/algorithms/tests/core.py +++ b/beat/web/algorithms/tests/core.py @@ -562,6 +562,10 @@ class AlgorithmsAPIBase(AlgorithmsTestCase): BINARY = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" \ "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF" + BINARY_UPDATE = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" \ + "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" \ + "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF" + def setUp(self): super(AlgorithmsAPIBase, self).setUp() diff --git a/beat/web/algorithms/tests/tests_api.py b/beat/web/algorithms/tests/tests_api.py index 7d9db6b7d965beb0188a49ac2d74853c9b4e0408..5604c781c24e47a1e559d07b8a76990e065f0161 100755 --- a/beat/web/algorithms/tests/tests_api.py +++ b/beat/web/algorithms/tests/tests_api.py @@ -31,6 +31,7 @@ import simplejson as json from django.contrib.auth.models import User from django.conf import settings from django.core.urlresolvers import reverse +from django.core.files.uploadedfile import SimpleUploadedFile from ...dataformats.models import DataFormat from ...common.testutils import tearDownModule @@ -1587,6 +1588,90 @@ class AlgorithmUpdate(AlgorithmsAPIBase): +class AlgorithmBinaryUpdate(AlgorithmsAPIBase): + + def setUp(self): + super(AlgorithmBinaryUpdate, self).setUp() + + self.url = reverse('api_algorithms:binary', args=['jackdoe', 'binary_personal', 1]) + self.updated_binary_file = SimpleUploadedFile("algo.so", AlgorithmsAPIBase.BINARY_UPDATE, + content_type="application/octet-stream") + + def test_no_update_for_anonymous_user(self): + response = self.client.post(self.url, + { + 'binary': self.updated_binary_file, + }) + + self.checkResponse(response, 403) + + + def test_fail_to_update_with_invalid_username(self): + self.client.login(username='jackdoe', password='1234') + url = reverse('api_algorithms:binary', args=['unknown', 'personal', 1]) + + response = self.client.post(url, + { + 'binary': self.updated_binary_file, + }) + + self.checkResponse(response, 404) + + + def test_fail_to_update_with_invalid_algorithm_name(self): + self.client.login(username='jackdoe', password='1234') + url = reverse('api_algorithms:binary', args=['jackdoe', 'unknown', 1]) + + response = self.client.post(url, + { + 'binary': self.updated_binary_file, + }) + + self.checkResponse(response, 404) + + + def test_no_update_without_content(self): + self.client.login(username='jackdoe', password='1234') + response = self.client.post(self.url, + { + }) + self.checkResponse(response, 400) + + + def test_no_update_with_invalid_filename(self): + self.client.login(username='jackdoe', password='1234') + response = self.client.post(self.url, + { + 'unknown': self.updated_binary_file, + }) + self.checkResponse(response, 400) + + + def test_no_update_with_invalid_file_content(self): + self.client.login(username='jackdoe', password='1234') + response = self.client.post(self.url, + { + 'binary': None, + }) + self.checkResponse(response, 400) + + + def test_successfull_update(self): + self.client.login(username='jackdoe', password='1234') + + response = self.client.post(self.url, + { + 'binary': self.updated_binary_file, + }) + + self.checkResponse(response, 204) + + algorithm = Algorithm.objects.get(author__username='jackdoe', name='binary_personal', version=1) + + self.assertEqual(algorithm.source_code, AlgorithmsAPIBase.BINARY_UPDATE) + + + class AlgorithmRetrieval(AlgorithmsAPIBase): def test_no_retrieval_of_confidential_algorithm_for_anonymous_user(self):