diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 90d28ec1..adc9e6ae 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -8,6 +8,10 @@ services: # Log printing via Rich is enhanced by a TTY tty: true env_file: ./dev/.env.docker-compose + environment: + # ensure these match the web container + - VUE_APP_BASE_URL=http://localhost:8080/ + - VUE_APP_OAUTH_CLIENT_ID=cBmD6D6F2YAmMWHNQZFPUr4OpaXVpW5w4Thod6Kj volumes: - .:/opt/uvdat-server ports: diff --git a/uvdat/core/management/commands/makeclient.py b/uvdat/core/management/commands/makeclient.py new file mode 100644 index 00000000..b65454a5 --- /dev/null +++ b/uvdat/core/management/commands/makeclient.py @@ -0,0 +1,46 @@ +import os +from django.contrib.auth.models import User +from django.contrib.sites.models import Site +from django.core.management.base import BaseCommand, CommandError +from oauth2_provider.models import Application + + +class Command(BaseCommand): + help = 'Creates a client Application object for authentication purposes.' + + def handle(self, **kwargs): + uri = os.environ.get('VUE_APP_BASE_URL') + client_id = os.environ.get('VUE_APP_OAUTH_CLIENT_ID') + if uri is None: + raise CommandError('Environment variable VUE_APP_BASE_URL is not set.') + if client_id is None: + raise CommandError('Environment variable VUE_APP_OAUTH_CLIENT_ID is not set.') + + site = Site.objects.get_current() # type: ignore + site.domain = 'uvdat.demo' + site.name = 'UVDAT' + site.save() + + try: + user = User.objects.first() + if Application.objects.filter(user=user).exists(): + raise CommandError( + 'The client already exists. You can administer it from the admin console.' + ) + application = Application( + user=user, + redirect_uris=uri, + client_id=client_id, + name='client-app', + client_type='public', + authorization_grant_type='authorization-code', + skip_authorization=True, + ) + application.save() + self.stdout.write( + self.style.SUCCESS('Client Application created.') + ) + except User.DoesNotExist: + raise CommandError( + 'A user must exist before creating a client. Use createsuperuser command.' + ) diff --git a/uvdat/core/rest/__init__.py b/uvdat/core/rest/__init__.py index 6bc2c50e..ef381955 100644 --- a/uvdat/core/rest/__init__.py +++ b/uvdat/core/rest/__init__.py @@ -6,6 +6,7 @@ from .network import NetworkEdgeViewSet, NetworkNodeViewSet, NetworkViewSet from .regions import DerivedRegionViewSet, SourceRegionViewSet from .simulations import SimulationViewSet +from .user import UserViewSet __all__ = [ ContextViewSet, @@ -20,4 +21,5 @@ SourceRegionViewSet, DerivedRegionViewSet, SimulationViewSet, + UserViewSet, ] diff --git a/uvdat/core/rest/serializers.py b/uvdat/core/rest/serializers.py index 5f4db738..262fb35e 100644 --- a/uvdat/core/rest/serializers.py +++ b/uvdat/core/rest/serializers.py @@ -1,5 +1,6 @@ import json +from django.contrib.auth.models import User from django.contrib.gis.serializers import geojson from rest_framework import serializers @@ -18,6 +19,11 @@ VectorMapLayer, ) +class UserSerializer(serializers.ModelSerializer): + class Meta: + model = User + fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_superuser'] + class ContextSerializer(serializers.ModelSerializer): default_map_center = serializers.SerializerMethodField('get_center') diff --git a/uvdat/core/rest/user.py b/uvdat/core/rest/user.py new file mode 100644 index 00000000..717e0310 --- /dev/null +++ b/uvdat/core/rest/user.py @@ -0,0 +1,23 @@ +import json +from django.http import HttpResponse +from django.contrib.auth.models import User +from django.contrib.auth import logout +from rest_framework.decorators import action +from rest_framework.viewsets import ReadOnlyModelViewSet + +from .serializers import UserSerializer + + +class UserViewSet(ReadOnlyModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + + @action(detail=False, pagination_class=None) + def me(self, request): + """Return the currently logged in user's information.""" + if request.user.is_anonymous: + return HttpResponse(status=204) + return HttpResponse( + json.dumps(UserSerializer(request.user).data), + status=200 + ) diff --git a/uvdat/settings.py b/uvdat/settings.py index 358f348c..f9ec325a 100644 --- a/uvdat/settings.py +++ b/uvdat/settings.py @@ -20,6 +20,8 @@ class UvdatMixin(ConfigMixin): BASE_DIR = Path(__file__).resolve(strict=True).parent.parent + SESSION_COOKIE_AGE = 3 + @staticmethod def mutate_configuration(configuration: ComposedConfiguration) -> None: # Install local apps first, to ensure any overridden resources are found first @@ -34,10 +36,12 @@ def mutate_configuration(configuration: ComposedConfiguration) -> None: 's3_file_field', ] - # Disable authentication requirements for REST - # TODO: configure authentication and remove this workaround - configuration.REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = [] - configuration.REST_FRAMEWORK['DEFAULT_PERMISSION_CLASSES'] = [] + configuration.REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = [ + 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', + ] + configuration.REST_FRAMEWORK['DEFAULT_PERMISSION_CLASSES'] = [ + 'rest_framework.permissions.IsAuthenticated' + ] # Re-configure the database for PostGIS db_parts = urlparse(os.environ['DJANGO_DATABASE_URL']) diff --git a/uvdat/urls.py b/uvdat/urls.py index 06f204ea..0f97345f 100644 --- a/uvdat/urls.py +++ b/uvdat/urls.py @@ -6,6 +6,7 @@ from rest_framework import permissions, routers from uvdat.core.rest import ( + UserViewSet, ChartViewSet, ContextViewSet, DatasetViewSet, @@ -28,6 +29,7 @@ permission_classes=(permissions.AllowAny,), ) +router.register(r'users', UserViewSet, basename='users') router.register(r'contexts', ContextViewSet, basename='contexts') router.register(r'datasets', DatasetViewSet, basename='datasets') router.register(r'files', FileItemViewSet, basename='files')