diff --git a/src/backend/core/addons/__init__.py b/src/backend/core/addons/__init__.py new file mode 100644 index 00000000..f784ffb1 --- /dev/null +++ b/src/backend/core/addons/__init__.py @@ -0,0 +1 @@ +"""Meet core add-ons module.""" diff --git a/src/backend/core/addons/service.py b/src/backend/core/addons/service.py new file mode 100644 index 00000000..a3008888 --- /dev/null +++ b/src/backend/core/addons/service.py @@ -0,0 +1,124 @@ +"""Authentication session management for add-ons using temporary cache-based sessions.""" + +import secrets +from datetime import datetime, timedelta, timezone +from enum import Enum +from logging import getLogger + +from django.conf import settings +from django.core.cache import cache +from django.core.exceptions import SuspiciousOperation + +from core.models import User +from core.services.jwt_token import JwtTokenService + +logger = getLogger(__name__) + + +class SessionState(str, Enum): + """Add-on authentication session states.""" + + PENDING = "pending" + AUTHENTICATED = "authenticated" + + +class TokenExchangeService: + """Manage temporary authentication sessions for add-on JWT token exchange.""" + + def __init__(self): + """Initialize the service with the configured token service.""" + + self._token_service = JwtTokenService( + secret_key=settings.ADDONS_JWT_SECRET_KEY, + algorithm=settings.ADDONS_JWT_ALG, + issuer=settings.ADDONS_JWT_ISSUER, + audience=settings.ADDONS_JWT_AUDIENCE, + expiration_seconds=settings.ADDONS_JWT_EXPIRATION_SECONDS, + token_type=settings.ADDONS_JWT_TOKEN_TYPE, + ) + + def _get_cache_key(self, session_id: str) -> str: + """Generate cache key for a session ID.""" + return f"{settings.ADDONS_SESSION_KEY_PREFIX}_{session_id}" + + def init_session(self) -> str: + """Create a new pending authentication session and return its ID.""" + + session_id = secrets.token_urlsafe(settings.ADDONS_SESSION_ID_LENGTH) + expires_at = datetime.now(timezone.utc) + timedelta( + seconds=settings.ADDONS_SESSION_TIMEOUT + ) + + session_data = { + "state": SessionState.PENDING, + "expires_at": expires_at.isoformat(), + } + + cache_key = self._get_cache_key(session_id) + cache.set( + cache_key, + session_data, + timeout=settings.ADDONS_SESSION_TIMEOUT, + ) + + return session_id + + def get_session(self, session_id: str) -> dict: + """Retrieve session data and clear it if authenticated.""" + + cache_key = self._get_cache_key(session_id) + data = cache.get(cache_key) + + if not data: + return {} + + if data.get("state") == SessionState.AUTHENTICATED: + self.clear_session(session_id) + + # Return copy without internal fields + internal_fields = {"expires_at"} + return {k: v for k, v in data.items() if k not in internal_fields} + + def clear_session(self, session_id: str) -> None: + """Remove session data from cache.""" + + cache_key = self._get_cache_key(session_id) + cache.delete(cache_key) + + def set_access_token(self, user: User, session_id: str): + """Generate and store access token for an authenticated user session.""" + + cache_key = self._get_cache_key(session_id) + existing_data = cache.get(cache_key) + + if not existing_data: + raise SuspiciousOperation("Session not found.") + + expires_at = existing_data.get("expires_at", None) + + if not expires_at: + self.clear_session(session_id) + raise SuspiciousOperation("Invalid session data.") + + remaining_seconds = int( + ( + datetime.fromisoformat(expires_at) - datetime.now(timezone.utc) + ).total_seconds() + ) + + if remaining_seconds <= 0: + self.clear_session(session_id) + raise SuspiciousOperation("Session expired.") + + if existing_data.get("state") != SessionState.PENDING: + self.clear_session(session_id) + raise SuspiciousOperation("Access token already set.") + + response = self._token_service.generate_jwt(user, settings.ADDONS_SCOPES) + new_data = { + **existing_data, + **response, + "state": SessionState.AUTHENTICATED, + } + + cache.set(cache_key, new_data, timeout=remaining_seconds) diff --git a/src/backend/core/addons/views.py b/src/backend/core/addons/views.py new file mode 100644 index 00000000..7e594301 --- /dev/null +++ b/src/backend/core/addons/views.py @@ -0,0 +1,57 @@ +"""Add-ons views.""" + +from django.conf import settings +from django.core.exceptions import SuspiciousOperation +from django.shortcuts import redirect, render +from django.utils.translation import gettext_lazy as _ +from django.views.decorators.http import require_http_methods + +from core.addons.service import SessionState, TokenExchangeService + + +def render_error(request, message, status=400): + """Render simple error page.""" + return render(request, "addons/error.html", {"message": message}, status=status) + + +@require_http_methods(["GET"]) +def transit_page(request): + """Initialize authentication flow for add-on session.""" + + session_id = request.GET.get("session_id") + + if not session_id: + return render_error(request, _("Session ID is required."), status=400) + + data = TokenExchangeService().get_session(session_id) + + if not data: + return render_error(request, _("Session not found or expired."), status=404) + + if data.get("state") != SessionState.PENDING: + return render_error(request, _("Invalid session state."), status=400) + + request.session[settings.ADDONS_SESSION_KEY_AUTH] = session_id + + return_to = request.build_absolute_uri("/addons/redirect") + return redirect(f"/api/{settings.API_VERSION}/authenticate/?returnTo={return_to}") + + +@require_http_methods(["GET"]) +def redirect_page(request): + """Complete authentication and close the popup window.""" + + if not request.user.is_authenticated: + return render_error(request, _("Authentication required."), status=401) + + session_id = request.session.pop(settings.ADDONS_SESSION_KEY_AUTH, None) + + if not session_id: + return render_error(request, _("No active session found."), status=404) + + try: + TokenExchangeService().set_access_token(request.user, session_id) + except SuspiciousOperation: + return render_error(request, _("Invalid or expired session."), status=400) + + return render(request, "addons/redirect_success.html") diff --git a/src/backend/core/addons/viewsets.py b/src/backend/core/addons/viewsets.py new file mode 100644 index 00000000..9a04463e --- /dev/null +++ b/src/backend/core/addons/viewsets.py @@ -0,0 +1,47 @@ +"""Add-ons API endpoints""" + +from logging import getLogger + +from rest_framework import ( + response as drf_response, +) +from rest_framework import status as drf_status +from rest_framework import viewsets + +from core.addons.service import TokenExchangeService + +logger = getLogger(__name__) + + +class AuthSessionViewSet(viewsets.ViewSet): + """ViewSet for managing add-on authentication sessions via token exchange.""" + + authentication_classes = [] + permission_classes = [] + throttle_classes = [] + + def create(self, request): + """Create a new pending authentication session.""" + session_id = TokenExchangeService().init_session() + return drf_response.Response( + {"session_id": session_id}, status=drf_status.HTTP_201_CREATED + ) + + def retrieve(self, request, pk=None): + """Retrieve authentication session data by session ID.""" + data = TokenExchangeService().get_session(pk) + + if not data: + return drf_response.Response( + {"detail": "Session not found or expired."}, + status=drf_status.HTTP_404_NOT_FOUND, + ) + + return drf_response.Response(data, status=drf_status.HTTP_200_OK) + + def destroy(self, request, pk=None): + """Delete an authentication session by session ID.""" + TokenExchangeService().clear_session(pk) + return drf_response.Response( + {"status": "ok"}, status=drf_status.HTTP_204_NO_CONTENT + ) diff --git a/src/backend/core/external_api/authentication.py b/src/backend/core/external_api/authentication.py index c16f6573..0309e860 100644 --- a/src/backend/core/external_api/authentication.py +++ b/src/backend/core/external_api/authentication.py @@ -214,6 +214,19 @@ class ApplicationJWTAuthentication(BaseJWTAuthentication): raise exceptions.AuthenticationFailed("Invalid token type.") +class AddonsJWTAuthentication(BaseJWTAuthentication): + """JWT authentication for addons API access. + + Validates JWT tokens issued by addons for authenticating users. + Tokens must include user_id to identify the authenticated user. + """ + + secret_key = settings.ADDONS_JWT_SECRET_KEY + algorithm = settings.ADDONS_JWT_ALG + issuer = settings.ADDONS_JWT_ISSUER + audience = settings.ADDONS_JWT_AUDIENCE + + class ResourceServerBackend(LaSuiteBackend): """OIDC Resource Server backend for user creation and retrieval.""" diff --git a/src/backend/core/external_api/viewsets.py b/src/backend/core/external_api/viewsets.py index 5a0e550c..bba2564f 100644 --- a/src/backend/core/external_api/viewsets.py +++ b/src/backend/core/external_api/viewsets.py @@ -173,6 +173,7 @@ class RoomViewSet( authentication_classes = [ authentication.ApplicationJWTAuthentication, + authentication.AddonsJWTAuthentication, ResourceServerAuthentication, ] permission_classes = [ diff --git a/src/backend/core/templates/addons/error.html b/src/backend/core/templates/addons/error.html new file mode 100644 index 00000000..f749ae43 --- /dev/null +++ b/src/backend/core/templates/addons/error.html @@ -0,0 +1,17 @@ +{% load i18n %} +{% get_current_language as LANGUAGE %} + + + + + {% trans "Error" %} + + +
+

{{ title|default:_("Error") }}

+

{{ message|default:_("Something went wrong.") }}

+ +
+ + + diff --git a/src/backend/core/templates/addons/redirect_success.html b/src/backend/core/templates/addons/redirect_success.html new file mode 100644 index 00000000..769e73cc --- /dev/null +++ b/src/backend/core/templates/addons/redirect_success.html @@ -0,0 +1,17 @@ +{% load i18n %} +{% get_current_language as LANGUAGE %} + + + + + {% trans "Authentication Success" %} + + + +

{% trans "Session stored successfully. This window will close automatically." %}

+

{% trans "If it doesn't close" %}, {% trans "click here" %}.

+ + + diff --git a/src/backend/core/urls.py b/src/backend/core/urls.py index d8493e5a..c81c4229 100644 --- a/src/backend/core/urls.py +++ b/src/backend/core/urls.py @@ -6,6 +6,8 @@ from django.urls import include, path from lasuite.oidc_login.urls import urlpatterns as oidc_urls from rest_framework.routers import DefaultRouter +from core.addons import views as addons_views +from core.addons import viewsets as addons_viewsets from core.api import get_frontend_configuration, viewsets from core.external_api import viewsets as external_viewsets @@ -26,12 +28,24 @@ external_router.register( basename="external_application", ) +# - Addons API +addons_router = DefaultRouter() +addons_router.register( + "addons/sessions", + addons_viewsets.AuthSessionViewSet, + basename="addons_auth_sessions", +) + external_router.register( "rooms", external_viewsets.RoomViewSet, basename="external_room", ) + +addons_urls = addons_router.urls if settings.ADDONS_ENABLED else [] + + urlpatterns = [ path( f"api/{settings.API_VERSION}/", @@ -39,12 +53,26 @@ urlpatterns = [ [ *router.urls, *oidc_urls, + *addons_urls, path("config/", get_frontend_configuration, name="config"), ] ), ), ] +if settings.ADDONS_ENABLED: + urlpatterns.append( + path( + "addons/", + include( + [ + path("transit/", addons_views.transit_page, name="transit_page"), + path("redirect/", addons_views.redirect_page, name="redirect_page"), + ] + ), + ), + ) + if settings.EXTERNAL_API_ENABLED: urlpatterns.append( path( diff --git a/src/backend/locale/de_DE/LC_MESSAGES/django.mo b/src/backend/locale/de_DE/LC_MESSAGES/django.mo index fd9e4b59..22522cd6 100644 Binary files a/src/backend/locale/de_DE/LC_MESSAGES/django.mo and b/src/backend/locale/de_DE/LC_MESSAGES/django.mo differ diff --git a/src/backend/locale/de_DE/LC_MESSAGES/django.po b/src/backend/locale/de_DE/LC_MESSAGES/django.po index 051db135..0b668f15 100644 --- a/src/backend/locale/de_DE/LC_MESSAGES/django.po +++ b/src/backend/locale/de_DE/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-29 15:15+0000\n" +"POT-Creation-Date: 2026-01-26 15:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,30 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: core/addons/views.py:24 +msgid "Session ID is required." +msgstr "Sitzungs-ID ist erforderlich." + +#: core/addons/views.py:29 +msgid "Session not found or expired." +msgstr "Sitzung nicht gefunden oder abgelaufen." + +#: core/addons/views.py:32 +msgid "Invalid session state." +msgstr "Ungültiger Sitzungsstatus." + +#: core/addons/views.py:45 +msgid "Authentication required." +msgstr "Authentifizierung erforderlich." + +#: core/addons/views.py:50 +msgid "No active session found." +msgstr "Keine aktive Sitzung gefunden." + +#: core/addons/views.py:55 +msgid "Invalid or expired session." +msgstr "Ungültige oder abgelaufene Sitzung." + #: core/admin.py:29 msgid "Personal info" msgstr "Persönliche Informationen" @@ -408,7 +432,7 @@ msgstr "Anwendungsdomain" msgid "Application domains" msgstr "Anwendungsdomains" -#: core/recording/event/notification.py:94 +#: core/recording/event/notification.py:116 msgid "Your recording is ready" msgstr "Ihre Aufzeichnung ist bereit" @@ -417,6 +441,30 @@ msgstr "Ihre Aufzeichnung ist bereit" msgid "Video call in progress: {sender.email} is waiting for you to connect" msgstr "Videoanruf läuft: {sender.email} wartet auf Ihre Teilnahme" +#: core/templates/addons/error.html:7 core/templates/addons/error.html:11 +msgid "Error" +msgstr "Fehler" + +#: core/templates/addons/error.html:12 +msgid "Something went wrong." +msgstr "Etwas ist schiefgelaufen." + +#: core/templates/addons/error.html:13 +msgid "Close" +msgstr "Schließen" + +#: core/templates/addons/redirect_success.html:7 +msgid "Authentication Success" +msgstr "Authentifizierung erfolgreich" + +#: core/templates/addons/redirect_success.html:13 +msgid "Session stored successfully. This window will close automatically." +msgstr "Sitzung erfolgreich gespeichert. Dieses Fenster wird automatisch geschlossen." + +#: core/templates/addons/redirect_success.html:14 +msgid "If it doesn't close" +msgstr "Falls es sich nicht schließt" + #: core/templates/mail/html/invitation.html:159 #: core/templates/mail/html/screen_recording.html:159 #: core/templates/mail/text/invitation.txt:3 diff --git a/src/backend/locale/en_US/LC_MESSAGES/django.mo b/src/backend/locale/en_US/LC_MESSAGES/django.mo index cdd88d17..fb982e7b 100644 Binary files a/src/backend/locale/en_US/LC_MESSAGES/django.mo and b/src/backend/locale/en_US/LC_MESSAGES/django.mo differ diff --git a/src/backend/locale/en_US/LC_MESSAGES/django.po b/src/backend/locale/en_US/LC_MESSAGES/django.po index 313f2352..9d3fcd50 100644 --- a/src/backend/locale/en_US/LC_MESSAGES/django.po +++ b/src/backend/locale/en_US/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-29 15:15+0000\n" +"POT-Creation-Date: 2026-01-26 15:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,30 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: core/addons/views.py:24 +msgid "Session ID is required." +msgstr "Session ID is required." + +#: core/addons/views.py:29 +msgid "Session not found or expired." +msgstr "Session not found or expired." + +#: core/addons/views.py:32 +msgid "Invalid session state." +msgstr "Invalid session state." + +#: core/addons/views.py:45 +msgid "Authentication required." +msgstr "Authentication required." + +#: core/addons/views.py:50 +msgid "No active session found." +msgstr "No active session found." + +#: core/addons/views.py:55 +msgid "Invalid or expired session." +msgstr "Invalid or expired session." + #: core/admin.py:29 msgid "Personal info" msgstr "Personal info" @@ -405,7 +429,7 @@ msgstr "Application domain" msgid "Application domains" msgstr "Application domains" -#: core/recording/event/notification.py:94 +#: core/recording/event/notification.py:116 msgid "Your recording is ready" msgstr "Your recording is ready" @@ -414,6 +438,30 @@ msgstr "Your recording is ready" msgid "Video call in progress: {sender.email} is waiting for you to connect" msgstr "Video call in progress: {sender.email} is waiting for you to connect" +#: core/templates/addons/error.html:7 core/templates/addons/error.html:11 +msgid "Error" +msgstr "Error" + +#: core/templates/addons/error.html:12 +msgid "Something went wrong." +msgstr "Something went wrong." + +#: core/templates/addons/error.html:13 +msgid "Close" +msgstr "Close" + +#: core/templates/addons/redirect_success.html:7 +msgid "Authentication Success" +msgstr "Authentication Success" + +#: core/templates/addons/redirect_success.html:13 +msgid "Session stored successfully. This window will close automatically." +msgstr "Session stored successfully. This window will close automatically." + +#: core/templates/addons/redirect_success.html:14 +msgid "If it doesn't close" +msgstr "If it doesn't close" + #: core/templates/mail/html/invitation.html:159 #: core/templates/mail/html/screen_recording.html:159 #: core/templates/mail/text/invitation.txt:3 diff --git a/src/backend/locale/fr_FR/LC_MESSAGES/django.mo b/src/backend/locale/fr_FR/LC_MESSAGES/django.mo index c76470df..11899a94 100644 Binary files a/src/backend/locale/fr_FR/LC_MESSAGES/django.mo and b/src/backend/locale/fr_FR/LC_MESSAGES/django.mo differ diff --git a/src/backend/locale/fr_FR/LC_MESSAGES/django.po b/src/backend/locale/fr_FR/LC_MESSAGES/django.po index 5f8c16b2..f61a3fab 100644 --- a/src/backend/locale/fr_FR/LC_MESSAGES/django.po +++ b/src/backend/locale/fr_FR/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-29 15:15+0000\n" +"POT-Creation-Date: 2026-01-26 15:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: antoine.lebaud@mail.numerique.gouv.fr\n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,30 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: core/addons/views.py:24 +msgid "Session ID is required." +msgstr "L'identifiant de session est requis." + +#: core/addons/views.py:29 +msgid "Session not found or expired." +msgstr "Session introuvable ou expirée." + +#: core/addons/views.py:32 +msgid "Invalid session state." +msgstr "État de session invalide." + +#: core/addons/views.py:45 +msgid "Authentication required." +msgstr "Authentification requise." + +#: core/addons/views.py:50 +msgid "No active session found." +msgstr "Aucune session active trouvée." + +#: core/addons/views.py:55 +msgid "Invalid or expired session." +msgstr "Session invalide ou expirée." + #: core/admin.py:29 msgid "Personal info" msgstr "Informations personnelles" @@ -409,7 +433,7 @@ msgstr "Domaine d’application" msgid "Application domains" msgstr "Domaines d’application" -#: core/recording/event/notification.py:94 +#: core/recording/event/notification.py:116 msgid "Your recording is ready" msgstr "Votre enregistrement est prêt" @@ -418,6 +442,30 @@ msgstr "Votre enregistrement est prêt" msgid "Video call in progress: {sender.email} is waiting for you to connect" msgstr "Appel vidéo en cours : {sender.email} attend que vous vous connectiez" +#: core/templates/addons/error.html:7 core/templates/addons/error.html:11 +msgid "Error" +msgstr "Erreur" + +#: core/templates/addons/error.html:12 +msgid "Something went wrong." +msgstr "Une erreur s'est produite." + +#: core/templates/addons/error.html:13 +msgid "Close" +msgstr "Fermer" + +#: core/templates/addons/redirect_success.html:7 +msgid "Authentication Success" +msgstr "Authentification réussie" + +#: core/templates/addons/redirect_success.html:13 +msgid "Session stored successfully. This window will close automatically." +msgstr "Session enregistrée avec succès. Cette fenêtre se fermera automatiquement." + +#: core/templates/addons/redirect_success.html:14 +msgid "If it doesn't close" +msgstr "Si elle ne se ferme pas" + #: core/templates/mail/html/invitation.html:159 #: core/templates/mail/html/screen_recording.html:159 #: core/templates/mail/text/invitation.txt:3 diff --git a/src/backend/locale/nl_NL/LC_MESSAGES/django.mo b/src/backend/locale/nl_NL/LC_MESSAGES/django.mo index afea428a..e26acf92 100644 Binary files a/src/backend/locale/nl_NL/LC_MESSAGES/django.mo and b/src/backend/locale/nl_NL/LC_MESSAGES/django.mo differ diff --git a/src/backend/locale/nl_NL/LC_MESSAGES/django.po b/src/backend/locale/nl_NL/LC_MESSAGES/django.po index 160e2240..4951037c 100644 --- a/src/backend/locale/nl_NL/LC_MESSAGES/django.po +++ b/src/backend/locale/nl_NL/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-29 15:15+0000\n" +"POT-Creation-Date: 2026-01-26 15:40+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,6 +17,30 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +#: core/addons/views.py:24 +msgid "Session ID is required." +msgstr "Sessie-ID is vereist." + +#: core/addons/views.py:29 +msgid "Session not found or expired." +msgstr "Sessie niet gevonden of verlopen." + +#: core/addons/views.py:32 +msgid "Invalid session state." +msgstr "Ongeldige sessiestatus." + +#: core/addons/views.py:45 +msgid "Authentication required." +msgstr "Authenticatie vereist." + +#: core/addons/views.py:50 +msgid "No active session found." +msgstr "Geen actieve sessie gevonden." + +#: core/addons/views.py:55 +msgid "Invalid or expired session." +msgstr "Ongeldige of verlopen sessie." + #: core/admin.py:29 msgid "Personal info" msgstr "Persoonlijke informatie" @@ -404,7 +428,7 @@ msgstr "Applicatiedomein" msgid "Application domains" msgstr "Applicatiedomeinen" -#: core/recording/event/notification.py:94 +#: core/recording/event/notification.py:116 msgid "Your recording is ready" msgstr "Je opname is klaar" @@ -413,6 +437,30 @@ msgstr "Je opname is klaar" msgid "Video call in progress: {sender.email} is waiting for you to connect" msgstr "Video-oproep bezig: {sender.email} wacht op je verbinding" +#: core/templates/addons/error.html:7 core/templates/addons/error.html:11 +msgid "Error" +msgstr "Fout" + +#: core/templates/addons/error.html:12 +msgid "Something went wrong." +msgstr "Er is iets misgegaan." + +#: core/templates/addons/error.html:13 +msgid "Close" +msgstr "Sluiten" + +#: core/templates/addons/redirect_success.html:7 +msgid "Authentication Success" +msgstr "Authenticatie geslaagd" + +#: core/templates/addons/redirect_success.html:13 +msgid "Session stored successfully. This window will close automatically." +msgstr "Sessie succesvol opgeslagen. Dit venster wordt automatisch gesloten." + +#: core/templates/addons/redirect_success.html:14 +msgid "If it doesn't close" +msgstr "Als het niet sluit" + #: core/templates/mail/html/invitation.html:159 #: core/templates/mail/html/screen_recording.html:159 #: core/templates/mail/text/invitation.txt:3 diff --git a/src/backend/meet/settings.py b/src/backend/meet/settings.py index eda28a76..f2b0279d 100755 --- a/src/backend/meet/settings.py +++ b/src/backend/meet/settings.py @@ -797,6 +797,66 @@ class Base(Configuration): environ_prefix=None, ) + # Addons + ADDONS_ENABLED = values.BooleanValue( + False, + environ_name="ADDONS_ENABLED", + environ_prefix=None, + ) + ADDONS_SESSION_ID_LENGTH = values.PositiveIntegerValue( + 32, + environ_name="ADDONS_SESSION_ID_LENGTH", + environ_prefix=None, + ) + # Used in cache key generation + ADDONS_SESSION_KEY_PREFIX = values.Value( + "addons_session_id", + environ_name="ADDONS_SESSION_KEY_PREFIX", + environ_prefix=None, + ) + # Used as the Django session key in transit page + ADDONS_SESSION_KEY_AUTH = values.Value( + "addons_session_id", + environ_name="ADDONS_SESSION_KEY_AUTH", + environ_prefix=None, + ) + ADDONS_SESSION_TIMEOUT = values.PositiveIntegerValue( + 600, environ_name="ADDONS_SESSION_TIMEOUT", environ_prefix=None + ) + ADDONS_JWT_SECRET_KEY = SecretFileValue( + None, environ_name="ADDONS_JWT_SECRET_KEY", environ_prefix=None + ) + ADDONS_JWT_ALG = values.Value( + "HS256", + environ_name="ADDONS_JWT_ALG", + environ_prefix=None, + ) + ADDONS_SCOPES = values.Value( + "rooms:create rooms:list", + environ_name="ADDONS_SCOPES", + environ_prefix=None, + ) + ADDONS_JWT_ISSUER = values.Value( + "lasuite-meet", + environ_name="ADDONS_JWT_ISSUER", + environ_prefix=None, + ) + ADDONS_JWT_AUDIENCE = values.Value( + None, + environ_name="ADDONS_JWT_AUDIENCE", + environ_prefix=None, + ) + ADDONS_JWT_EXPIRATION_SECONDS = values.PositiveIntegerValue( + 3600, + environ_name="ADDONS_JWT_EXPIRATION_SECONDS", + environ_prefix=None, + ) + ADDONS_JWT_TOKEN_TYPE = values.Value( + "Bearer", + environ_name="ADDONS_JWT_TOKEN_TYPE", + environ_prefix=None, + ) + # pylint: disable=invalid-name @property def ENVIRONMENT(self): diff --git a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl index 04c2f170..e34a3075 100644 --- a/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl +++ b/src/helm/env.d/dev-keycloak/values.meet.yaml.gotmpl @@ -79,6 +79,7 @@ backend: APPLICATION_JWT_AUDIENCE: https://meet.127.0.0.1.nip.io/external-api/v1.0/ APPLICATION_JWT_SECRET_KEY: devKeyApplication APPLICATION_BASE_URL: https://meet.127.0.0.1.nip.io + ADDONS_JWT_SECRET_KEY: devKeyApplicationAddons migrate: diff --git a/src/helm/meet/templates/ingress.yaml b/src/helm/meet/templates/ingress.yaml index 8f15f761..e83459cf 100644 --- a/src/helm/meet/templates/ingress.yaml +++ b/src/helm/meet/templates/ingress.yaml @@ -88,6 +88,20 @@ spec: serviceName: {{ include "meet.backend.fullname" . }} servicePort: {{ .Values.backend.service.port }} {{- end }} + - path: /addons/ + {{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }} + pathType: Prefix + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ include "meet.backend.fullname" . }} + port: + number: {{ .Values.backend.service.port }} + {{- else }} + serviceName: {{ include "meet.backend.fullname" . }} + servicePort: {{ .Values.backend.service.port }} + {{- end }} {{- with .Values.ingress.customBackends }} {{- toYaml . | nindent 10 }} {{- end }} @@ -138,6 +152,20 @@ spec: serviceName: {{ include "meet.backend.fullname" $ }} servicePort: {{ $.Values.backend.service.port }} {{- end }} + - path: /addons/ + {{- if semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion }} + pathType: Prefix + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ include "meet.backend.fullname" $ }} + port: + number: {{ $.Values.backend.service.port }} + {{- else }} + serviceName: {{ include "meet.backend.fullname" $ }} + servicePort: {{ $.Values.backend.service.port }} + {{- end }} {{- with $.Values.ingress.customBackends }} {{- toYaml . | nindent 10 }} {{- end }}