diff --git a/Dockerfile b/Dockerfile index 8443debf..172fc949 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,6 +25,9 @@ RUN chmod +x /usr/src/app/entrypoint.sh # copy manage.py COPY manage.py . +# copy scripts +COPY scripts ./scripts + # copy project COPY backend ./backend diff --git a/Dockerfile.prod b/Dockerfile.prod index f2ec66bb..bb6452a5 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -57,6 +57,9 @@ COPY ./backend/entrypoint.prod.sh . RUN sed -i 's/\r$//g' $APP_HOME/entrypoint.prod.sh RUN chmod +x $APP_HOME/entrypoint.prod.sh +# copy scripts +COPY scripts ./scripts + # copy manage.py COPY manage.py . diff --git a/backend/entrypoint.prod.sh b/backend/entrypoint.prod.sh index 8e82918e..16a6e2c8 100644 --- a/backend/entrypoint.prod.sh +++ b/backend/entrypoint.prod.sh @@ -11,6 +11,11 @@ then echo "PostgreSQL started" fi +python manage.py migrate + +python manage.py runscript push_site + python manage.py collectstatic --noinput + exec "$@" \ No newline at end of file diff --git a/backend/entrypoint.sh b/backend/entrypoint.sh index ce1cafee..8784c67c 100755 --- a/backend/entrypoint.sh +++ b/backend/entrypoint.sh @@ -15,6 +15,8 @@ fi #python manage.py makemigrations python manage.py migrate +python manage.py runscript push_site + python manage.py createsuperuser --noinput --email $DJANGO_SUPERUSER_EMAIL exec "$@" \ No newline at end of file diff --git a/backend/pigeonhole/apps/users/admin.py b/backend/pigeonhole/apps/users/admin.py index ec1d1884..711156d3 100644 --- a/backend/pigeonhole/apps/users/admin.py +++ b/backend/pigeonhole/apps/users/admin.py @@ -16,6 +16,8 @@ class UserAdmin(BaseUserAdmin): 'password', 'first_name', 'last_name', + 'course', + 'role', )}), ) diff --git a/backend/pigeonhole/settings.py b/backend/pigeonhole/settings.py index f06af906..12b395d7 100644 --- a/backend/pigeonhole/settings.py +++ b/backend/pigeonhole/settings.py @@ -25,8 +25,9 @@ # SECURITY WARNING: don't run with debug turned on in production! DEBUG = int(os.environ.get("DEBUG", default=0)) +FRONTEND_URL = os.environ.get("FRONTEND_URL", default="http://localhost:3000") -ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", default="127.0.0.1").split(" ") +ALLOWED_HOSTS = os.environ.get("DJANGO_ALLOWED_HOSTS", default="127.0.0.1 example.com").split(" ") if not DEBUG: USE_X_FORWARDED_HOST = True @@ -35,22 +36,35 @@ # Application definition INSTALLED_APPS = [ + "corsheaders", + 'backend.pigeonhole.apps.courses', + 'backend.pigeonhole.apps.groups', + 'backend.pigeonhole.apps.projects', + 'backend.pigeonhole.apps.submissions', + 'backend.pigeonhole.apps.users', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', - 'django.contrib.sessions', 'django.contrib.messages', + 'django.contrib.sessions', + 'django.contrib.sites', 'django.contrib.staticfiles', - 'rest_framework', - "corsheaders", - 'backend.pigeonhole.apps.users', - 'backend.pigeonhole.apps.courses', - 'backend.pigeonhole.apps.projects', - 'backend.pigeonhole.apps.submissions', - 'backend.pigeonhole.apps.groups', + 'django_extensions', 'drf_yasg', + 'microsoft_auth', + 'rest_framework', +] + +AUTHENTICATION_BACKENDS = [ + 'django.contrib.auth.backends.ModelBackend', + 'microsoft_auth.backends.MicrosoftAuthenticationBackend', ] +MICROSOFT_AUTH_CLIENT_ID = os.environ.get("OAUTH_CLIENT_ID") +MICROSOFT_AUTH_CLIENT_SECRET = os.environ.get("OAUTH_CLIENT_SECRET") +MICROSOFT_AUTH_TENANT_ID = os.environ.get("OAUTH_TENANT_ID") +MICROSOFT_AUTH_LOGIN_TYPE = 'ma' + MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', @@ -84,14 +98,15 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'microsoft_auth.context_processors.microsoft', ], }, }, @@ -171,3 +186,5 @@ } } } + +SITE_URL = "http://localhost:8000" diff --git a/backend/pigeonhole/urls.py b/backend/pigeonhole/urls.py index 34374b3e..f93fa720 100644 --- a/backend/pigeonhole/urls.py +++ b/backend/pigeonhole/urls.py @@ -1,7 +1,8 @@ from django.conf import settings from django.conf.urls.static import static from django.contrib import admin -from django.urls import include, path +from django.urls import include, path as urlpath +from django.shortcuts import redirect from drf_yasg import openapi from drf_yasg.views import get_schema_view from rest_framework import routers, permissions @@ -33,16 +34,23 @@ router.register(r'groups', GroupViewSet) router.register(r'submissions', SubmissionsViewset) +def to_frontend(request, path): + return redirect(f"{settings.FRONTEND_URL}/{path}") + # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ - path('', include(router.urls)), - path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), - path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), - path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), - path("admin/", admin.site.urls), - path('auth/login/', TokenObtainPairView.as_view(), name='token_obtain_pair'), - path('auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'), + urlpath('', include(router.urls)), + urlpath('api-auth/', include('rest_framework.urls', namespace='rest_framework')), + urlpath('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), + urlpath('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), + urlpath("admin/", admin.site.urls), + urlpath('auth/login/', TokenObtainPairView.as_view(), name='token_obtain_pair'), + urlpath('auth/refresh/', TokenRefreshView.as_view(), name='token_refresh'), + urlpath('microsoft/', include('microsoft_auth.urls', namespace='microsoft')), + urlpath('redirect/', to_frontend, name='redirect'), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + + urlpatterns += router.urls diff --git a/backend/requirements.txt b/backend/requirements.txt index 22eb5e0d..6409aa3b 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,11 +1,13 @@ +Django~=4.2.11 coverage~=6.3 -Django~=5.0.2 django-cors-headers~=3.14.0 +django-extensions==3.2.3 +django_microsoft_auth==3.0.1 djangorestframework-simplejwt~=5.2.2 djangorestframework~=3.14.0 +drf-yasg==1.21.7 flake8==7.0.0 psycopg2-binary~=2.9.5 pytz~=2022.7.1 pyyaml==6.0.1 uritemplate==4.1.1 -drf-yasg==1.21.7 diff --git a/backend/templates/microsoft/auth_callback.html b/backend/templates/microsoft/auth_callback.html new file mode 100644 index 00000000..fd9dfd68 --- /dev/null +++ b/backend/templates/microsoft/auth_callback.html @@ -0,0 +1,17 @@ +{% load i18n %} + + + +

{% trans "This window should automatically close BUT ITs in microsoft. If it does not, it should be save to close after a few seconds." %}

+
{{ message }}
+ + + \ No newline at end of file diff --git a/frontend/src/app/components/CASButton.tsx b/frontend/src/app/components/CASButton.tsx index 6af7e5c7..079ae9e8 100644 --- a/frontend/src/app/components/CASButton.tsx +++ b/frontend/src/app/components/CASButton.tsx @@ -7,6 +7,7 @@ const CASButton = () => { const handleCASLogin = (): void => { // Implement CAS login logic here console.log('Login with CAS'); + window.location.href = "http://localhost:8000/microsoft/to-auth-redirect?next=/redirect/homepage" }; return ( diff --git a/frontend/src/auth/auth-agent.js b/frontend/src/auth/auth-agent.js index 279b33a2..a9c0a2e5 100644 --- a/frontend/src/auth/auth-agent.js +++ b/frontend/src/auth/auth-agent.js @@ -4,7 +4,7 @@ class AuthAgent { login(username, password) { return axios - .post("http://127.0.0.1:8000/auth/login/", { + .post("http://localhost:8000/auth/login/", { username, password }) diff --git a/scripts/push_site.py b/scripts/push_site.py new file mode 100644 index 00000000..53f78932 --- /dev/null +++ b/scripts/push_site.py @@ -0,0 +1,11 @@ +import os + +from django.contrib.sites.models import Site + + +def run(): + debug = int(os.environ.get("DEBUG", default=0)) + domain = 'localhost:8000' if debug else 'sel2-1.ugent.be' + print(domain) + Site.objects.all().delete() + Site.objects.create(pk=1, domain=domain, name='localhost')