diff --git a/app/api/externalauth.py b/app/api/externalauth.py new file mode 100644 index 00000000..dcc96a60 --- /dev/null +++ b/app/api/externalauth.py @@ -0,0 +1,38 @@ +from django.contrib.auth.models import User +from django.contrib.auth import login +from rest_framework.views import APIView +from rest_framework import exceptions, permissions, parsers +from rest_framework.response import Response +from app.auth.backends import get_user_from_external_auth_response +import requests +from webodm import settings + +class ExternalTokenAuth(APIView): + permission_classes = (permissions.AllowAny,) + parser_classes = (parsers.JSONParser, parsers.FormParser,) + + def post(self, request): + # This should never happen + if settings.EXTERNAL_AUTH_ENDPOINT == '': + return Response({'error': 'EXTERNAL_AUTH_ENDPOINT not set'}) + + token = request.COOKIES.get('external_access_token', '') + if token == '': + return Response({'error': 'external_access_token cookie not set'}) + + try: + r = requests.post(settings.EXTERNAL_AUTH_ENDPOINT, headers={ + 'Authorization': "Bearer %s" % token + }) + res = r.json() + if res.get('user_id') is not None: + user = get_user_from_external_auth_response(res) + if user is not None: + login(request, user, backend='django.contrib.auth.backends.ModelBackend') + return Response({'redirect': '/'}) + else: + return Response({'error': 'Invalid credentials'}) + else: + return Response({'error': res.get('message', 'Invalid external server response')}) + except Exception as e: + return Response({'error': str(e)}) diff --git a/app/api/urls.py b/app/api/urls.py index a29f0e0a..3de13590 100644 --- a/app/api/urls.py +++ b/app/api/urls.py @@ -13,6 +13,7 @@ from .tiler import TileJson, Bounds, Metadata, Tiles, Export from .potree import Scene, CameraView from .workers import CheckTask, GetTaskResult from .users import UsersList +from .externalauth import ExternalTokenAuth from webodm import settings router = routers.DefaultRouter() @@ -63,3 +64,6 @@ urlpatterns = [ if settings.ENABLE_USERS_API: urlpatterns.append(url(r'users', UsersList.as_view())) +if settings.EXTERNAL_AUTH_ENDPOINT != '': + urlpatterns.append(url(r'^external-token-auth/', ExternalTokenAuth.as_view())) + diff --git a/app/auth/backends.py b/app/auth/backends.py index c25f2be3..22c7a65f 100644 --- a/app/auth/backends.py +++ b/app/auth/backends.py @@ -8,6 +8,57 @@ import logging logger = logging.getLogger('app.logger') +def get_user_from_external_auth_response(res): + if 'message' in res or 'error' in res: + return None + + if 'user_id' in res and 'username' in res: + try: + user = User.objects.get(pk=res['user_id']) + + # Update user info + if user.username != res['username']: + user.username = res['username'] + user.save() + + # Update quotas + maxQuota = -1 + if 'maxQuota' in res: + maxQuota = res['maxQuota'] + if 'node' in res and 'limits' in res['node'] and 'maxQuota' in res['node']['limits']: + maxQuota = res['node']['limits']['maxQuota'] + + if user.profile.quota != maxQuota: + user.profile.quota = maxQuota + user.save() + except User.DoesNotExist: + user = User(pk=res['user_id'], username=username) + user.save() + + # Setup/update processing node + if ('api_key' in res or 'token' in res) and 'node' in res: + hostname = res['node']['hostname'] + port = res['node']['port'] + token = res['api_key'] if 'api_key' in res else res['token'] + + try: + node = ProcessingNode.objects.get(token=token) + if node.hostname != hostname or node.port != port: + node.hostname = hostname + node.port = port + node.save() + + except ProcessingNode.DoesNotExist: + node = ProcessingNode(hostname=hostname, port=port, token=token) + node.save() + + if not user.has_perm('view_processingnode', node): + assign_perm('view_processingnode', user, node) + + return user + else: + return None + class ExternalBackend(ModelBackend): def authenticate(self, request, username=None, password=None): if EXTERNAL_AUTH_ENDPOINT == "": @@ -20,57 +71,7 @@ class ExternalBackend(ModelBackend): }, headers={'Accept': 'application/json'}) res = r.json() - # logger.info(res) - - if 'message' in res or 'error' in res: - return None - - if 'user_id' in res: - try: - user = User.objects.get(pk=res['user_id']) - - # Update user info - if user.username != username: - user.username = username - user.save() - - # Update quotas - maxQuota = -1 - if 'maxQuota' in res: - maxQuota = res['maxQuota'] - if 'node' in res and 'limits' in res['node'] and 'maxQuota' in res['node']['limits']: - maxQuota = res['node']['limits']['maxQuota'] - - if user.profile.quota != maxQuota: - user.profile.quota = maxQuota - user.save() - except User.DoesNotExist: - user = User(pk=res['user_id'], username=username) - user.save() - - # Setup/update processing node - if ('api_key' in res or 'token' in res) and 'node' in res: - hostname = res['node']['hostname'] - port = res['node']['port'] - token = res['api_key'] if 'api_key' in res else res['token'] - - try: - node = ProcessingNode.objects.get(token=token) - if node.hostname != hostname or node.port != port: - node.hostname = hostname - node.port = port - node.save() - - except ProcessingNode.DoesNotExist: - node = ProcessingNode(hostname=hostname, port=port, token=token) - node.save() - - if not user.has_perm('view_processingnode', node): - assign_perm('view_processingnode', user, node) - - return user - else: - return None + return get_user_from_external_auth_response(res) except: return None diff --git a/app/templates/app/registration/login.html b/app/templates/app/registration/login.html index 9945dd42..a1b9b8d4 100644 --- a/app/templates/app/registration/login.html +++ b/app/templates/app/registration/login.html @@ -10,12 +10,12 @@ {% endif %} {% is_single_user_mode as autologin %} - {% external_auth_endpoint as ext_auth_ep %} + {% has_external_auth as ext_auth %} {% if autologin %} {% else %} - - {% if ext_auth_ep != '' %} + {% if ext_auth %}
diff --git a/app/templatetags/settings.py b/app/templatetags/settings.py index e439e161..d904dd1e 100644 --- a/app/templatetags/settings.py +++ b/app/templatetags/settings.py @@ -10,8 +10,8 @@ register = template.Library() logger = logging.getLogger('app.logger') @register.simple_tag -def external_auth_endpoint(): - return settings.EXTERNAL_AUTH_ENDPOINT +def has_external_auth(): + return settings.EXTERNAL_AUTH_ENDPOINT != "" @register.filter def disk_size(megabytes):