diff --git a/caprover_deploy.sh b/caprover_deploy.sh index 70a9ac69..dd6c138e 100755 --- a/caprover_deploy.sh +++ b/caprover_deploy.sh @@ -13,10 +13,9 @@ set -ev npm run build # create and push new docker container -docker buildx build --platform=linux/amd64 -f compose/django/Dockerfile -t "$TAG" -t "$LATEST" --build-arg VERSION="$VERSION" --build-arg BUILD_TIMESTAMP="$BUILD_TIMESTAMP" . +# docker buildx build --platform=linux/amd64 -f compose/django/Dockerfile -t "$TAG" -t "$LATEST" --build-arg VERSION="$VERSION" --build-arg BUILD_TIMESTAMP="$BUILD_TIMESTAMP" . +docker buildx build --platform=linux/amd64 -f compose/django/Dockerfile . docker push iterativ/vbv-lernwelt-django -docker push "$TAG" -docker push "$LATEST" # deploy to caprover caprover deploy -h https://captain.control.iterativ.ch -a vbv-lernwelt -i docker.io/iterativ/vbv-lernwelt-django diff --git a/client/src/components/MainNavigationBar.vue b/client/src/components/MainNavigationBar.vue index f3e7454a..ec3a2f6b 100644 --- a/client/src/components/MainNavigationBar.vue +++ b/client/src/components/MainNavigationBar.vue @@ -24,12 +24,12 @@ function toggleNav() { state.showMenu = !state.showMenu; } -function menuActive(checkPath) { - return route.path.startsWith(checkPath); +function isInRoutePath(checkPaths: string[]) { + return checkPaths.some((path) => route.path.startsWith(path)) } function inLearningPath() { - return route.path.startsWith('/learningpath/') || route.path.startsWith('/circle/'); + return isInRoutePath(['/learningpath/', '/circle/']); } function getLearningPathStringProp (prop: 'title' | 'slug'): string { @@ -58,13 +58,17 @@ function handleDropdownSelect(data) { router.push('/profile') break case 'logout': - router.push('/logout') + userStore.handleLogout(); break default: console.log('no action') } } +function logout () { + userStore.handleLogout(); +} + onMounted(() => { log.debug('MainNavigationBar mounted'); }) @@ -96,7 +100,7 @@ const profileDropdownData = [
@@ -164,7 +169,7 @@ const profileDropdownData = [
Lernpfad @@ -185,7 +190,7 @@ const profileDropdownData = [ v-if="inLearningPath()" to="/competences/" class="nav-item" - :class="{'nav-item--active': menuActive('/competences/')}" + :class="{'nav-item--active': isInRoutePath(['/competences/'])}" > Kompetenzprofil @@ -194,14 +199,14 @@ const profileDropdownData = [ Shop Mediathek diff --git a/client/src/components/MobileMenu.vue b/client/src/components/MobileMenu.vue index 93312cf1..2d33dbf5 100644 --- a/client/src/components/MobileMenu.vue +++ b/client/src/components/MobileMenu.vue @@ -8,7 +8,7 @@ const router = useRouter() const props = defineProps<{ show: boolean, - user: object, + userStore: object, learningPathName: string, learningPathSlug: string }>() @@ -29,19 +29,25 @@ const clickLink = (to: string) => { >
-
-
- -
-
-

{{user.first_name}} {{user.last_name}}

- +
+
+
+ +
+
+

{{userStore.first_name}} {{userStore.last_name}}

+ +
@@ -50,7 +56,7 @@ const clickLink = (to: string) => { v-if="learningPathName">

Kurs: {{learningPathName}}

    -
  • +
  • Kompetenzprofil
@@ -60,9 +66,13 @@ const clickLink = (to: string) => {
  • Mediathek
  • -
    +
    +
    diff --git a/client/src/components/ui/ItFullScreenModal.vue b/client/src/components/ui/ItFullScreenModal.vue index 53ff4e5a..8bed583b 100644 --- a/client/src/components/ui/ItFullScreenModal.vue +++ b/client/src/components/ui/ItFullScreenModal.vue @@ -1,23 +1,43 @@ diff --git a/client/tailwind.css b/client/tailwind.css index a62e7281..d6ff3921 100644 --- a/client/tailwind.css +++ b/client/tailwind.css @@ -90,3 +90,10 @@ svg { } } +@layer utilities { + .no-scroll { + height: 100vh; + overflow: hidden; + } +} + diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 8f935ced..b6315517 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -526,15 +526,17 @@ OAUTH = { "client_name": env("IT_OAUTH_CLIENT_NAME", default="lernetz"), "client_id": env("IT_OAUTH_CLIENT_ID", default="iterativ"), "client_secret": env("IT_OAUTH_CLIENT_SECRET", default=""), - "access_token_url": env("IT_OAUTH_ACCESS_TOKEN_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token"), - "authorize_url": env("IT_OAUTH_AUTHORIZE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth"), + # "access_token_url": env("IT_OAUTH_ACCESS_TOKEN_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/token"), + # "authorize_url": env("IT_OAUTH_AUTHORIZE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth"), "authorize_params": IT_OAUTH_AUTHORIZE_PARAMS, + "access_token_params": IT_OAUTH_AUTHORIZE_PARAMS, "api_base_url": env("IT_OAUTH_API_BASE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/"), "local_redirect_uri": env("IT_OAUTH_LOCAL_DIRECT_URI", default="http://localhost:8000/sso/callback/"), + "server_metadata_url": env("IT_OAUTH_SERVER_METADATA_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/.well-known/openid-configuration"), "client_kwargs": { 'scope': env("IT_OAUTH_SCOPE", default=''), 'token_endpoint_auth_method': 'client_secret_post', - 'token_placement': 'header', + 'token_placement': 'body', } } diff --git a/server/config/urls.py b/server/config/urls.py index f99b3bdb..d5aeada9 100644 --- a/server/config/urls.py +++ b/server/config/urls.py @@ -16,7 +16,7 @@ from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt from vbv_lernwelt.core.views import ( rate_limit_exceeded_view, permission_denied_view, - check_rate_limit, cypress_reset_view, vue_home, vue_login, me_user_view, ) + check_rate_limit, cypress_reset_view, vue_home, vue_login, me_user_view, vue_logout, ) from .wagtail_api import wagtail_api_router @@ -42,6 +42,7 @@ urlpatterns = [ path('learnpath/', include("vbv_lernwelt.learnpath.urls")), path('api/completion/', include("vbv_lernwelt.completion.urls")), re_path(r'api/core/me/$', me_user_view, name='me_user_view'), + re_path(r'core/logout/$', vue_logout, name='vue_logout'), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: # Static file serving when using Gunicorn + Uvicorn for local web socket development diff --git a/server/vbv_lernwelt/core/views.py b/server/vbv_lernwelt/core/views.py index 36c3a3b9..4e035fd5 100644 --- a/server/vbv_lernwelt/core/views.py +++ b/server/vbv_lernwelt/core/views.py @@ -3,7 +3,7 @@ import requests import structlog from django.conf import settings -from django.contrib.auth import authenticate, login +from django.contrib.auth import authenticate, login, logout from django.core.management import call_command from django.http import JsonResponse, HttpResponse, HttpResponseRedirect from django.shortcuts import render @@ -69,6 +69,12 @@ def me_user_view(request): return Response(status=403) +@api_view(['POST']) +def vue_logout(request): + logout(request) + return Response({'success': True}, 200) + + def permission_denied_view(request, exception): return render(request, "403.html", status=403) diff --git a/server/vbv_lernwelt/sso/client.py b/server/vbv_lernwelt/sso/client.py index 0170f2e3..875bfec1 100644 --- a/server/vbv_lernwelt/sso/client.py +++ b/server/vbv_lernwelt/sso/client.py @@ -2,17 +2,6 @@ from authlib.integrations.django_client import OAuth from django.conf import settings # # https://docs.authlib.org/en/latest/client/frameworks.html#frameworks-clients -# def fetch_token(_name, request): -# try: -# token = OAuth2Token.objects.get( -# user=request.user -# ) -# return token.to_token() -# except (OAuth2Token.DoesNotExist, TypeError): -# return None - - -# oauth = OAuth(fetch_token=fetch_token) oauth = OAuth() oauth.register( name=settings.OAUTH["client_name"], @@ -20,10 +9,7 @@ oauth.register( client_secret=settings.OAUTH["client_secret"], request_token_url=None, request_token_params=None, - access_token_url=settings.OAUTH["access_token_url"], - access_token_params=None, - authorize_url=settings.OAUTH["authorize_url"], authorize_params=settings.OAUTH["authorize_params"], - api_base_url=settings.OAUTH["api_base_url"], - client_kwargs=settings.OAUTH["client_kwargs"] + client_kwargs=settings.OAUTH["client_kwargs"], + server_metadata_url=settings.OAUTH["server_metadata_url"], ) diff --git a/server/vbv_lernwelt/sso/views.py b/server/vbv_lernwelt/sso/views.py index 96298586..b37b41c2 100644 --- a/server/vbv_lernwelt/sso/views.py +++ b/server/vbv_lernwelt/sso/views.py @@ -2,6 +2,7 @@ import structlog as structlog from authlib.integrations.base_client import OAuthError from django.conf import settings from django.shortcuts import redirect +from django.http import HttpResponse from sentry_sdk import capture_exception from django.contrib.auth import login as dj_login, get_user_model @@ -24,7 +25,7 @@ def authorize(request): try: logger.debug(request) token = getattr(oauth, settings.OAUTH["client_name"]).authorize_access_token(request) - deocded_token = decode_jwt(token["access_token"]) + deocded_token = decode_jwt(token["id_token"]) except OAuthError as e: logger.error(f'OAuth error: {e}') if not settings.DEBUG: @@ -45,5 +46,5 @@ def _user_data_from_token_data(token: dict) -> dict: "first_name": token.get("given_name", ""), "last_name": token.get("family_name", ""), "username": token.get("preferred_username", ""), - "email": token.get("email", ""), + "email": token.get("emails", [''])[0], }