aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Doan <andy.doan@linaro.org>2014-09-04 17:18:23 -0500
committerAndy Doan <andy.doan@linaro.org>2014-09-17 14:23:51 -0500
commit0bdb00d0551e7f0c2850097f54f9c633befb0ab8 (patch)
treee02e966044216264704282bf247176153c1ee3a4
parentfb2e01c1cf500c8f52add181ed807b8fa15cf456 (diff)
apiv2: create api to publish files
Change-Id: Ieaf1ffff87734fcc4b8c585e631a5304ec686e9a
-rw-r--r--license_protected_downloads/api/v1.py29
-rw-r--r--license_protected_downloads/api/v2.py37
-rw-r--r--license_protected_downloads/tests/test_api_v2.py68
-rw-r--r--urls.py3
4 files changed, 123 insertions, 14 deletions
diff --git a/license_protected_downloads/api/v1.py b/license_protected_downloads/api/v1.py
index 139cc9e..f606249 100644
--- a/license_protected_downloads/api/v1.py
+++ b/license_protected_downloads/api/v1.py
@@ -36,6 +36,21 @@ def upload_target_path(path, key, public):
return safe_path_join(base_path, path)
+def do_upload(infd, path, api_key):
+ path = upload_target_path(
+ path, api_key.key, public=api_key.public)
+
+ # Create directory if required
+ dirname = os.path.dirname(path)
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
+
+ with open(path, "wb") as destination:
+ for chunk in infd.chunks():
+ destination.write(chunk)
+
+
+
@csrf_exempt
def file_server_post(request, path):
""" Handle post requests.
@@ -58,19 +73,7 @@ def file_server_post(request, path):
return HttpResponseServerError("Invalid call")
APILog.mark(request, 'FILE_UPLOAD', api_key)
-
- path = upload_target_path(
- path, request.POST["key"], public=api_key.public)
-
- # Create directory if required
- dirname = os.path.dirname(path)
- if not os.path.isdir(dirname):
- os.makedirs(dirname)
-
- with open(path, "wb") as destination:
- for chunk in request.FILES["file"].chunks():
- destination.write(chunk)
-
+ do_upload(request.FILES['file'], path, api_key)
return HttpResponse("OK")
diff --git a/license_protected_downloads/api/v2.py b/license_protected_downloads/api/v2.py
index 397b9b0..35fe19d 100644
--- a/license_protected_downloads/api/v2.py
+++ b/license_protected_downloads/api/v2.py
@@ -5,6 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
+from license_protected_downloads.api.v1 import do_upload
from license_protected_downloads.models import (
APIKeyStore,
APILog,
@@ -97,3 +98,39 @@ class TokenResource(RestResource):
@csrf_exempt
def token(request, token):
return TokenResource(request, token).handle()
+
+
+class PublishResource(RestResource):
+ def __init__(self, request, path):
+ super(PublishResource, self).__init__(request)
+ self.path = path
+
+ def authenticate(self):
+ if 'HTTP_AUTHTOKEN' not in self.request.META:
+ APILog.mark(self.request, 'INVALID_KEY_MISSING')
+ raise RestException('Missing api token', 401)
+ try:
+ token = APIToken.objects.get(
+ token=self.request.META['HTTP_AUTHTOKEN'])
+ if not token.valid_request(self.request):
+ raise RestException('Token no longer valid', 401)
+ self.api_key = token.key
+ except APIToken.DoesNotExist:
+ APILog.mark(self.request, 'INVALID_KEY')
+ raise RestException('Invalid api token', 401)
+
+ def POST(self):
+ if 'file' not in self.request.FILES or not self.path:
+ APILog.mark(self.request, 'INVALID_ARGUMENTS', self.api_key)
+ raise RestException('Invalid Arguments', 401)
+
+ APILog.mark(self.request, 'FILE_UPLOAD', self.api_key)
+ do_upload(self.request.FILES['file'], self.path, self.api_key)
+ resp = HttpResponse(status=201)
+ resp['Location'] = self.path
+ return resp
+
+
+@csrf_exempt
+def publish(request, path):
+ return PublishResource(request, path).handle()
diff --git a/license_protected_downloads/tests/test_api_v2.py b/license_protected_downloads/tests/test_api_v2.py
index 942b9a7..12acf2f 100644
--- a/license_protected_downloads/tests/test_api_v2.py
+++ b/license_protected_downloads/tests/test_api_v2.py
@@ -1,5 +1,10 @@
import datetime
import json
+import shutil
+import tempfile
+import StringIO
+
+import mock
from django.test import Client, TestCase
@@ -12,7 +17,14 @@ from license_protected_downloads.models import (
class APIv2Tests(TestCase):
def setUp(self):
self.client = Client(enforce_csrf_checks=True)
- self.api_key = APIKeyStore.objects.create(key='foo')
+ self.api_key = APIKeyStore.objects.create(key='foo', public=True)
+
+ path = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, path)
+ m = mock.patch('django.conf.settings.SERVED_PATHS',
+ new_callable=lambda: [path])
+ self.addCleanup(m.stop)
+ m.start()
def test_token_no_auth(self):
resp = self.client.get('/api/v2/token/')
@@ -64,3 +76,57 @@ class APIv2Tests(TestCase):
resp = self.client.get(
resp['Location'], HTTP_AUTHTOKEN=self.api_key.key)
self.assertEqual(200, resp.status_code)
+
+ def test_publish_no_token(self):
+ resp = self.client.post('/api/v2/publish/a')
+ self.assertEqual(401, resp.status_code)
+
+ def test_publish_bad_token(self):
+ resp = self.client.post('/api/v2/publish/a', HTTP_AUTHTOKEN='bad')
+ self.assertEqual(401, resp.status_code)
+
+ def test_publish_expired_token(self):
+ expires = datetime.datetime.now() - datetime.timedelta(minutes=1)
+ token = APIToken.objects.create(key=self.api_key, expires=expires)
+ resp = self.client.post(
+ '/api/v2/publish/a', HTTP_AUTHTOKEN=token.token)
+ self.assertEqual(401, resp.status_code)
+
+ def _send_file(self, url, token, content, resp_code=201):
+ f = StringIO.StringIO(content)
+ f.name = 'name' # to fool django's client.post
+ response = self.client.post(
+ url, HTTP_AUTHTOKEN=token, data={"file": f})
+ self.assertEqual(response.status_code, resp_code)
+
+ def test_publish_simple(self):
+ content = 'foo bar'
+ token = APIToken.objects.create(key=self.api_key).token
+ self._send_file('/api/v2/publish/a', token, content)
+
+ # we'll be missing a build-info so:
+ self.assertEqual(403, self.client.get('/a').status_code)
+
+ # add a build-info and see if the file works
+ info = 'Format-Version: 0.5\n\nFiles-Pattern: *\nLicense-Type: open\n'
+ self._send_file('/api/v2/publish/BUILD-INFO.txt', token, info)
+
+ resp = self.client.get('/a')
+ self.assertEqual(200, resp.status_code)
+ self.assertEqual(content, open(resp['X-Sendfile']).read())
+
+ def test_publish_with_dirs(self):
+ content = 'foo bar'
+ token = APIToken.objects.create(key=self.api_key).token
+ self._send_file('/api/v2/publish/a/b', token, content)
+
+ # we'll be missing a build-info so:
+ self.assertEqual(403, self.client.get('/a/b').status_code)
+
+ # add a build-info and see if the file works
+ info = 'Format-Version: 0.5\n\nFiles-Pattern: *\nLicense-Type: open\n'
+ self._send_file('/api/v2/publish/a/BUILD-INFO.txt', token, info)
+
+ resp = self.client.get('/a/b')
+ self.assertEqual(200, resp.status_code)
+ self.assertEqual(content, open(resp['X-Sendfile']).read())
diff --git a/urls.py b/urls.py
index 9d4a133..6b267be 100644
--- a/urls.py
+++ b/urls.py
@@ -56,6 +56,9 @@ urlpatterns = patterns(
url(r'^api/v2/token/(?P<token>.*)$',
'license_protected_downloads.api.v2.token'),
+ url(r'^api/v2/publish/(?P<path>.*)$',
+ 'license_protected_downloads.api.v2.publish'),
+
# Catch-all. We always return a file (or try to) if it exists.
# This handler does that.
url(r'(?P<path>.*)', 'license_protected_downloads.views.file_server'),