Skip to content

Commit

Permalink
feat: server-side changes for authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
annehaley committed Sep 25, 2024
1 parent ad426e9 commit b60591c
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 4 deletions.
4 changes: 4 additions & 0 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
46 changes: 46 additions & 0 deletions uvdat/core/management/commands/makeclient.py
Original file line number Diff line number Diff line change
@@ -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.'
)
2 changes: 2 additions & 0 deletions uvdat/core/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -20,4 +21,5 @@
SourceRegionViewSet,
DerivedRegionViewSet,
SimulationViewSet,
UserViewSet,
]
6 changes: 6 additions & 0 deletions uvdat/core/rest/serializers.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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')
Expand Down
23 changes: 23 additions & 0 deletions uvdat/core/rest/user.py
Original file line number Diff line number Diff line change
@@ -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
)
12 changes: 8 additions & 4 deletions uvdat/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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'])
Expand Down
2 changes: 2 additions & 0 deletions uvdat/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rest_framework import permissions, routers

from uvdat.core.rest import (
UserViewSet,
ChartViewSet,
ContextViewSet,
DatasetViewSet,
Expand All @@ -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')
Expand Down

0 comments on commit b60591c

Please sign in to comment.