diff --git a/app/api/urls.py b/app/api/urls.py index 4ce21dd5..831fa202 100644 --- a/app/api/urls.py +++ b/app/api/urls.py @@ -3,6 +3,7 @@ from .projects import ProjectViewSet from .tasks import TaskViewSet, TaskTiles, TaskTilesJson, TaskDownloads, TaskAssets from .processingnodes import ProcessingNodeViewSet from rest_framework_nested import routers +from rest_framework_jwt.views import obtain_jwt_token router = routers.DefaultRouter() router.register(r'projects', ProjectViewSet) @@ -21,4 +22,5 @@ urlpatterns = [ url(r'projects/(?P[^/.]+)/tasks/(?P[^/.]+)/assets/(?P.+)$', TaskAssets.as_view()), url(r'^auth/', include('rest_framework.urls')), + url(r'^token-auth/', obtain_jwt_token), ] \ No newline at end of file diff --git a/app/tests/test_api.py b/app/tests/test_api.py index 5dd83120..38d57b47 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -1,18 +1,15 @@ import datetime -import subprocess +from django.contrib.auth.models import User from guardian.shortcuts import assign_perm +from rest_framework import status +from rest_framework.test import APIClient +from rest_framework_jwt.settings import api_settings from app import pending_actions -from nodeodm import status_codes -from .classes import BootTestCase -from rest_framework.test import APIClient -from rest_framework import status -import time, os - -from app.models import Project, Task, ImageUpload +from app.models import Project, Task from nodeodm.models import ProcessingNode -from django.contrib.auth.models import User +from .classes import BootTestCase class TestApi(BootTestCase): @@ -310,3 +307,37 @@ class TestApi(BootTestCase): self.assertTrue(len(res.data) == 2) self.assertTrue(res.data[1]["port"] == 1000) + def test_token_auth(self): + client = APIClient() + + pnode = ProcessingNode.objects.create( + hostname="localhost", + port=999 + ) + + # Cannot access resources + res = client.get('/api/processingnodes/') + self.assertEqual(res.status_code, status.HTTP_403_FORBIDDEN) + + # Cannot generate token with invalid credentials + res = client.post('/api/token-auth/', { + 'username': 'testuser', + 'password': 'wrongpwd' + }) + self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST) + + # Can generate token with invalid credentials + res = client.post('/api/token-auth/', { + 'username': 'testuser', + 'password': 'test1234' + }) + self.assertEqual(res.status_code, status.HTTP_200_OK) + + token = res.data['token'] + self.assertTrue(len(token) > 0) + + # Can access resources by passing token + client = APIClient(HTTP_AUTHORIZATION="{0} {1}".format(api_settings.JWT_AUTH_HEADER_PREFIX, token)) + res = client.get('/api/processingnodes/') + self.assertEqual(res.status_code, status.HTTP_200_OK) + diff --git a/webodm/settings.py b/webodm/settings.py index 4983d013..4ac7fe0f 100644 --- a/webodm/settings.py +++ b/webodm/settings.py @@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/1.10/ref/settings/ """ import os, sys + +import datetime from django.contrib.messages import constants as messages # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -228,6 +230,10 @@ REST_FRAMEWORK = { 'PAGE_SIZE': 10, } +JWT_AUTH = { + 'JWT_EXPIRATION_DELTA': datetime.timedelta(hours=6), +} + TESTING = sys.argv[1:2] == ['test'] if TESTING: MEDIA_ROOT = os.path.join(BASE_DIR, 'app', 'media_test')