diff --git a/backend/iam/sso/saml/urls.py b/backend/iam/sso/saml/urls.py index 60cc533967..5728928ee2 100644 --- a/backend/iam/sso/saml/urls.py +++ b/backend/iam/sso/saml/urls.py @@ -18,6 +18,16 @@ views.FinishACSView.as_view(), name="saml_finish_acs", ), + path( + "generate-keys/", + views.GenerateSAMLKeyView.as_view(), + name="generate_saml_keys", + ), + path( + "download-cert/", + views.DownloadSAMLPublicCertView.as_view(), + name="download_saml_cert", + ), ] ), ) diff --git a/backend/iam/sso/saml/views.py b/backend/iam/sso/saml/views.py index 28cf189730..d737e2df18 100644 --- a/backend/iam/sso/saml/views.py +++ b/backend/iam/sso/saml/views.py @@ -1,5 +1,25 @@ -from allauth.account.models import EmailAddress +# === Python standard library === +import json +from datetime import datetime, timedelta +from django.http import HttpRequest, HttpResponseRedirect, HttpResponse +from django.http.response import Http404 +from django.urls import reverse +from django.utils.decorators import method_decorator +from django.views import View + +# === Third-party packages === import structlog +from rest_framework.views import APIView +from django.views.decorators.csrf import csrf_exempt +from rest_framework.response import Response +from rest_framework import status + +from cryptography import x509 +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.x509.oid import NameOID + +from allauth.account.models import EmailAddress from allauth.core.exceptions import SignupClosedException from allauth.socialaccount.adapter import get_account_adapter from allauth.socialaccount.internal.flows.login import ( @@ -7,6 +27,7 @@ record_authentication, ) from allauth.socialaccount.models import PermissionDenied, SocialLogin +from allauth.socialaccount.providers.saml.provider import SAMLProvider from allauth.socialaccount.providers.saml.views import ( AuthProcess, LoginSession, @@ -18,19 +39,15 @@ httpkit, render_authentication_error, ) -from allauth.socialaccount.providers.saml.provider import SAMLProvider from allauth.utils import ValidationError -from django.http import HttpRequest, HttpResponseRedirect -from django.http.response import Http404 -from django.urls import reverse -from django.utils.decorators import method_decorator -from django.views import View -from rest_framework.views import csrf_exempt +# === Application-specific imports === +from core.permissions import IsAdministrator # ou une permission plus adaptée from iam.models import User from iam.sso.errors import AuthError from iam.sso.models import SSOSettings from iam.utils import generate_token +from global_settings.models import GlobalSettings DEFAULT_SAML_ATTRIBUTE_MAPPING_EMAIL = SAMLProvider.default_attribute_mapping["email"] @@ -192,3 +209,86 @@ def dispatch(self, request, organization_slug): email_object.save() logger.info("Email verified", user=user) return HttpResponseRedirect(next_url) + + +class GenerateSAMLKeyView(SAMLViewMixin, APIView): + """ + Endpoint to generate a key pair (private key + self-signed X.509 certificate). + Accessible only to admins (to be adapted as needed). + """ + + permission_classes = [IsAdministrator] + + def post(self, request, organization_slug): + try: + data = json.loads(request.body) + except json.JSONDecodeError: + data = {} + cn = data.get("common_name", "saml-sp.example.com") + days = int(data.get("days", 365)) + + # RSA key generation + key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048, + ) + + # Self-signed certificate generation + subject = issuer = x509.Name( + [ + x509.NameAttribute(NameOID.COMMON_NAME, cn), + ] + ) + cert = ( + x509.CertificateBuilder() + .subject_name(subject) + .issuer_name(issuer) + .public_key(key.public_key()) + .serial_number(x509.random_serial_number()) + .not_valid_before(datetime.utcnow()) + .not_valid_after(datetime.utcnow() + timedelta(days=days)) + .sign(private_key=key, algorithm=hashes.SHA256()) + ) + + private_key_pem = key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + + cert_pem = cert.public_bytes(serialization.Encoding.PEM) + + provider = self.get_provider(organization_slug) + # Retrieves the 'advanced' dictionary, or creates it if it doesn't exist + advanced_settings = provider.app.settings.get("advanced", {}) + advanced_settings["private_key"] = private_key_pem.decode("utf-8") + advanced_settings["x509cert"] = cert_pem.decode("utf-8") + + # Re-injects the dict into the application configuration + settings = GlobalSettings.objects.get(name=GlobalSettings.Names.SSO) + settings.value["settings"]["advanced"] = advanced_settings + settings.save() + + return Response( + { + "message": f"Key and certificate saved in advanced settings of SP {organization_slug}", + "cert": cert_pem.decode("utf-8"), + }, + status=status.HTTP_201_CREATED, + ) + + +class DownloadSAMLPublicCertView(SAMLViewMixin, APIView): + permission_classes = [IsAdministrator] + + def get(self, request, organization_slug): + provider = self.get_provider(organization_slug) + cert_pem = provider.app.settings.get("advanced", {}).get("x509cert") + if not cert_pem: + return HttpResponse(status=404) + + response = HttpResponse(cert_pem, content_type="application/x-pem-file") + response["Content-Disposition"] = ( + 'attachment; filename="ciso-saml-public-cert.pem"' + ) + return response diff --git a/backend/iam/sso/serializers.py b/backend/iam/sso/serializers.py index 3c4eec43bf..2d39b93271 100644 --- a/backend/iam/sso/serializers.py +++ b/backend/iam/sso/serializers.py @@ -212,6 +212,20 @@ class SSOSettingsWriteSerializer(BaseModelSerializer): required=False, source="settings.advanced.want_name_id_encrypted", ) + sp_x509cert = serializers.CharField( + required=False, + allow_blank=True, + allow_null=True, + source="settings.advanced.x509cert", + ) + sp_private_key = serializers.CharField( + required=False, + allow_blank=True, + allow_null=True, + trim_whitespace=False, + source="settings.advanced.private_key", + write_only=True, + ) oidc_has_secret = serializers.SerializerMethodField() def get_oidc_has_secret(self, obj) -> bool: @@ -224,13 +238,34 @@ class Meta: model = SSOSettings exclude = ["value"] + def to_representation(self, instance): + data = super().to_representation(instance) + + # remove nested private_key if present + advanced = data.get("settings", {}).get("advanced", {}) + if "private_key" in advanced: + advanced.pop("private_key") + + return data + def update(self, instance, validated_data): settings_object = GlobalSettings.objects.get(name=GlobalSettings.Names.SSO) - # Use stored secret if no secret is transmitted + # Use stored secret and sp_private_key if no transmitted validated_data["secret"] = validated_data.get( "secret", settings_object.value.get("secret", "") ) + validated_data["settings"]["advanced"]["private_key"] = ( + validated_data.get("settings", {}) + .get("advanced", {}) + .get( + "private_key", + settings_object.value.get("settings", {}) + .get("advanced", {}) + .get("private_key", ""), + ) + ) + validated_data["provider_id"] = validated_data.get("provider", "n/a") if "settings" not in validated_data: validated_data["settings"] = {} diff --git a/backend/iam/sso/views.py b/backend/iam/sso/views.py index ede2e2be67..a7e3c3fcd5 100644 --- a/backend/iam/sso/views.py +++ b/backend/iam/sso/views.py @@ -36,8 +36,8 @@ def post(self, request, *args, **kwargs): next_url=next_url, headless=True, ) - except: - logger.error("Cannot perform redirection, Check your IdP URLs") + except Exception as e: + logger.error("SSO redirection failed", provider=provider.id, error=str(e)) return render_authentication_error(request, provider, error="failedSSO") diff --git a/frontend/messages/ar.json b/frontend/messages/ar.json index 9c0b2ca473..0992f39157 100644 --- a/frontend/messages/ar.json +++ b/frontend/messages/ar.json @@ -938,5 +938,12 @@ "securityObjectiveScale": "مقياس خصائص الأمان", "securityObjectiveScaleHelpText": "اختر مقياسك لخصائص الأمان (1 إلى 4 بشكل افتراضي)", "actor": "فاعل", - "action": "إجراء" + "action": "إجراء", + "privateKey": "المفتاح الخاص", + "generateDots": "إنشاء...", + "samlKeysGenerated": "تم إنشاء مفاتيح SAML بنجاح", + "samlPrivateKeyHelpText": "كتابة فقط: استخدم هذا الحقل فقط إذا كنت بحاجة إلى تقديم مفتاح/شهادة خاص بك. خلاف ذلك، سيقوم التطبيق بإنشائه.", + "samlCertificateHelpText": "يمكنك تحديد شهادتك الخاصة أو السماح للتطبيق بإنشائها.", + "samlAuthnRequestSignedHelpText": "عند التمكين، يتم توقيع طلب المصادقة SAML بالمفتاح الخاص أدناه. يتم إرسال الشهادة العامة المقابلة إلى موفر الهوية حتى يتمكن من التحقق من التوقيع.", + "downloadCertificate": "تنزيل الشهادة" } diff --git a/frontend/messages/cs.json b/frontend/messages/cs.json index aee8a893e3..6f0fb2b08c 100644 --- a/frontend/messages/cs.json +++ b/frontend/messages/cs.json @@ -958,5 +958,12 @@ "securityObjectiveScale": "Měřítko bezpečnostních vlastností", "securityObjectiveScaleHelpText": "Vyberte měřítko pro bezpečnostní vlastnosti (1 až 4 výchozí)", "actor": "Aktér", - "action": "Akce" + "action": "Akce", + "privateKey": "Soukromý klíč", + "generateDots": "Generování...", + "samlKeysGenerated": "SAML klíče úspěšně vygenerovány", + "samlPrivateKeyHelpText": "Pouze pro zápis: použijte toto pole pouze v případě, že potřebujete zadat vlastní soukromý klíč/certifikát. Jinak aplikace vygeneruje klíč.", + "samlCertificateHelpText": "Můžete zadat vlastní certifikát nebo nechat aplikaci vygenerovat klíč.", + "samlAuthnRequestSignedHelpText": "Pokud je povoleno, požadavek na ověření SAML je podepsán soukromým klíčem níže. Odpovídající veřejný certifikát je odeslán poskytovateli identity, aby mohl ověřit podpis.", + "downloadCertificate": "Stáhnout certifikát" } diff --git a/frontend/messages/da.json b/frontend/messages/da.json index 7b9cb5b38b..65bbf4bf16 100644 --- a/frontend/messages/da.json +++ b/frontend/messages/da.json @@ -1250,5 +1250,12 @@ "securityObjectiveScale": "Skala for sikkerhedsegenskaber", "securityObjectiveScaleHelpText": "Vælg skala for sikkerhedsegenskaber (1 til 4 som standard)", "actor": "Aktør", - "action": "Handling" + "action": "Handling", + "privateKey": "Privat nøgle", + "generateDots": "Genererer...", + "samlKeysGenerated": "SAML-nøgler genereret med succes", + "samlPrivateKeyHelpText": "Skrivebeskyttet: brug kun dette felt, hvis du skal angive din egen private nøgle/certifikat. Ellers vil applikationen generere det.", + "samlCertificateHelpText": "Du kan angive dit eget certifikat eller lade applikationen generere det.", + "samlAuthnRequestSignedHelpText": "Når aktiveret, bliver SAML-autentificeringsanmodningen underskrevet med den private nøgle nedenfor. Det tilsvarende offentlige certifikat sendes til Identity Provider, så det kan verificere underskriften.", + "downloadCertificate": "Download certifikat" } diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 88946fd78d..5e24fffe0b 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -975,5 +975,12 @@ "securityObjectiveScale": "Skala für Sicherheitsmerkmale", "securityObjectiveScaleHelpText": "Wählen Sie Ihre Skala für Sicherheitsmerkmale (Standardmäßig 1 bis 4)", "actor": "Akteur", - "action": "Aktion" + "action": "Aktion", + "privateKey": "Privater Schlüssel", + "generateDots": "Generiere...", + "samlKeysGenerated": "SAML-Schlüssel erfolgreich generiert", + "samlPrivateKeyHelpText": "Schreibgeschützt: Verwenden Sie dieses Feld nur, wenn Sie Ihren eigenen privaten Schlüssel/Zertifikat angeben müssen. Andernfalls generiert die Anwendung ihn.", + "samlCertificateHelpText": "Sie können Ihr eigenes Zertifikat angeben oder die Anwendung generieren lassen.", + "samlAuthnRequestSignedHelpText": "Wenn aktiviert, wird die SAML-Authentifizierungsanfrage mit dem privaten Schlüssel unten signiert. Das zugehörige öffentliche Zertifikat wird an den Identitätsanbieter gesendet, damit er die Signatur überprüfen kann.", + "downloadCertificate": "Zertifikat herunterladen" } diff --git a/frontend/messages/el.json b/frontend/messages/el.json index 4b5450b0c6..883d29fe64 100644 --- a/frontend/messages/el.json +++ b/frontend/messages/el.json @@ -1667,5 +1667,12 @@ "image": "Εικόνα", "securityObjectives": "Ασφαλή ιδιότητες", "securityObjectiveScale": "Κλίμακα ασφαλών ιδιοτήτων", - "securityObjectiveScaleHelpText": "Επιλέξτε την κλίμακα σας για τις ασφαλείς ιδιότητες (1 έως 4 από προεπιλογή)" + "securityObjectiveScaleHelpText": "Επιλέξτε την κλίμακα σας για τις ασφαλείς ιδιότητες (1 έως 4 από προεπιλογή)", + "privateKey": "Κλειδί ιδιωτικό", + "generateDots": "Δημιουργία...", + "samlKeysGenerated": "Τα κλειδιά SAML δημιουργήθηκαν επιτυχώς", + "samlPrivateKeyHelpText": "Write-only: χρησιμοποιήστε αυτό το πεδίο μόνο αν χρειάζεται να παρέχετε δικό σας κλειδί ιδιωτικό/πιστοποιητικό. Διαφορετικά, η εφαρμογή θα το δημιουργήσει αυτόματα.", + "samlCertificateHelpText": "Μπορείτε να καθορίσετε δικό σας πιστοποιητικό ή να αφήσετε την εφαρμογή να το δημιουργήσει αυτόματα.", + "samlAuthnRequestSignedHelpText": "Όταν είναι ενεργοποιημένο, το αίτημα αυθεντικοποίησης SAML υπογράφεται με το ιδιωτικό κλειδί παρακάτω. Το αντίστοιχο δημόσιο πιστοποιητικό αποστέλλεται στον πάροχο ταυτότητας ώστε να μπορέσει να επαληθεύσει την υπογραφή.", + "downloadCertificate": "Λήψη πιστοποιητικού" } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 7fba16484a..6b64f73b89 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -2206,5 +2206,12 @@ "libraryOverview": "{objectType}: {count}", "evidenceRevisions": "Revisions", "addEvidenceRevision": "Add Revision", - "associatedEvidenceRevisions": "Associated versions" + "associatedEvidenceRevisions": "Associated versions", + "privateKey": "Private Key", + "generateDots": "Generate...", + "samlKeysGenerated": "Saml keys successfully generated", + "samlPrivateKeyHelpText": "Write-only: use this field only if you need to supply your own private key/certificate. Otherwise, the application will generate it.", + "samlCertificateHelpText": "You can specify your own certificate or let the application generate it.", + "samlAuthnRequestSignedHelpText": "When enabled, the SAML authentication request is signed with the private key below. The corresponding public certificate is sent to the Identity Provider so it can verify the signature.", + "downloadCertificate": "Download certificate" } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index e4e517a42d..ed78046e4a 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -975,5 +975,12 @@ "securityObjectiveScale": "Escala de propiedades de seguridad", "securityObjectiveScaleHelpText": "Elija su escala para las propiedades de seguridad (1 a 4 por defecto)", "actor": "Actor", - "action": "Acción" + "action": "Acción", + "privateKey": "Clave privada", + "generateDots": "Generando...", + "samlKeysGenerated": "Claves SAML generadas con éxito", + "samlPrivateKeyHelpText": "Sólo escritura: utilice este campo solo si necesita proporcionar su propia clave/certificado privado. De lo contrario, la aplicación lo generará.", + "samlCertificateHelpText": "Puede especificar su propio certificado o dejar que la aplicación lo genere.", + "samlAuthnRequestSignedHelpText": "Cuando está habilitado, la solicitud de autenticación SAML se firma con la clave privada a continuación. El certificado público correspondiente se envía al Proveedor de Identidad para que pueda verificar la firma.", + "downloadCertificate": "Descargar certificado" } diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 3b6b5c78f1..3592e0d8bb 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -2139,5 +2139,12 @@ "hasMfaEnabled": "MFA activée", "evidenceRevisions": "Révisions", "addEvidenceRevision": "Nouvelle révision", - "associatedEvidenceRevisions": "Versions associées" + "associatedEvidenceRevisions": "Révisions associées", + "privateKey": "Clé privée", + "generateDots": "Générer...", + "samlKeysGenerated": "Clés SAML générées avec succès", + "samlPrivateKeyHelpText": "Écriture seule : utilisez ce champ uniquement si vous devez fournir votre propre clé/certificat privée. Sinon, l'application générera celle-ci.", + "samlCertificateHelpText": "Vous pouvez spécifier votre propre certificat ou laisser l'application le générer.", + "samlAuthnRequestSignedHelpText": "Lorsque cette option est activée, la requête d'authentification SAML est signée avec la clé privée ci-dessous. Le certificat public correspondant est envoyé au fournisseur d'identité afin qu'il puisse vérifier la signature.", + "downloadCertificate": "Télécharger le certificat" } diff --git a/frontend/messages/hi.json b/frontend/messages/hi.json index d641435e88..4d22f56435 100644 --- a/frontend/messages/hi.json +++ b/frontend/messages/hi.json @@ -939,5 +939,12 @@ "securityObjectiveScale": "सुरक्षा गुण स्केल", "securityObjectiveScaleHelpText": "सुरक्षा गुणों के लिए अपना स्केल चुनें (डिफ़ॉल्ट रूप से 1 से 4)", "actor": "अभिनेता", - "action": "क्रिया" + "action": "कार्रवाई", + "privateKey": "निजी कुंजी", + "generateDots": "उत्पन्न...", + "samlKeysGenerated": "सामल कुंजियाँ सफलतापूर्वक उत्पन्न की गईं", + "samlPrivateKeyHelpText": "लेखन-केवल: इस क्षेत्र का उपयोग केवल तभी करें यदि आपको अपना निजी कुंजी/सर्टिफिकेट प्रदान करने की आवश्यकता है। अन्यथा, एप्लिकेशन इसे उत्पन्न करेगा।", + "samlCertificateHelpText": "आप अपना स्वयं का सर्टिफिकेट निर्दिष्ट कर सकते हैं या एप्लिकेशन को इसे उत्पन्न करने दें।", + "samlAuthnRequestSignedHelpText": "जब सक्षम किया जाता है, तो SAML प्रमाणीकरण अनुरोध नीचे दिए गए निजी कुंजी के साथ हस्ताक्षरित किया जाता है। संबंधित सार्वजनिक सर्टिफिकेट को पहचान प्रदाता को भेजा जाता है ताकि यह हस्ताक्षर की पुष्टि कर सके।", + "downloadCertificate": "सर्टिफिकेट डाउनलोड करें" } diff --git a/frontend/messages/hr.json b/frontend/messages/hr.json index 27adbfb50c..271e97ae66 100644 --- a/frontend/messages/hr.json +++ b/frontend/messages/hr.json @@ -2200,5 +2200,12 @@ "isSelectedHelpText": "Odlučite treba li biti pušteno u procjenu", "quantRiskPriorityHelpText": "Korisno analitičaru za forsiranje poretka u izvještaju za Upravu", "lossThreshold": "Prag gubitka", - "lossThresholdHelpText": "Maksimalni prihvatljivi gubitak koji će služiti kao osnova za toleranciju" + "lossThresholdHelpText": "Maksimalni prihvatljivi gubitak koji će služiti kao osnova za toleranciju", + "privateKey": "Privatni ključ", + "generateDots": "Generiraj...", + "samlKeysGenerated": "SAML ključevi uspješno generirani", + "samlPrivateKeyHelpText": "Samo za pisanje: koristite ovo polje samo ako trebate unijeti vlastiti privatni ključ/certifikat. U suprotnom, aplikacija će ga generirati.", + "samlCertificateHelpText": "Možete navesti vlastiti certifikat ili pustiti aplikaciju da ga generira.", + "samlAuthnRequestSignedHelpText": "Kada je omogućeno, zahtjev za SAML autentifikaciju potpisuje se privatnim ključem ispod. Odgovarajući javni certifikat šalje se Identity Provideru kako bi mogao provjeriti potpis.", + "downloadCertificate": "Preuzmi certifikat" } diff --git a/frontend/messages/hu.json b/frontend/messages/hu.json index 8ff5ce379d..d1285fefa2 100644 --- a/frontend/messages/hu.json +++ b/frontend/messages/hu.json @@ -970,5 +970,12 @@ "securityObjectiveScale": "Biztonsági tulajdonságok skálája", "securityObjectiveScaleHelpText": "Válassza ki a biztonsági tulajdonságok skáláját (alapértelmezés szerint 1-től 4-ig)", "actor": "Szereplő", - "action": "Művelet" + "action": "Akció", + "privateKey": "Privát kulcs", + "generateDots": "Generálás...", + "samlKeysGenerated": "SAML kulcsok sikeresen generálva", + "samlPrivateKeyHelpText": "Csak írásra használható: csak akkor használja ezt a mezőt, ha saját privát kulcs/tanúsítványt kell megadnia. Ellenkező esetben az alkalmazás generálja.", + "samlCertificateHelpText": "Saját tanúsítványt adhat meg, vagy az alkalmazás generálhatja.", + "samlAuthnRequestSignedHelpText": "Ha engedélyezve van, a SAML hitelesítési kérelem aláírva lesz az alábbi privát kulccsal. A megfelelő nyilvános tanúsítvány elküldve lesz az Azonosítószolgáltatónak, hogy ellenőrizhesse az aláírást.", + "downloadCertificate": "Tanúsítvány letöltése" } diff --git a/frontend/messages/id.json b/frontend/messages/id.json index e46e9a2443..ac33f3ed17 100644 --- a/frontend/messages/id.json +++ b/frontend/messages/id.json @@ -1155,5 +1155,12 @@ "securityObjectiveScale": "Skala properti keamanan", "securityObjectiveScaleHelpText": "Pilih skala untuk properti keamanan Anda (1 hingga 4 secara default)", "actor": "Pelaku", - "action": "Aksi" + "action": "Aksi", + "privateKey": "Kunci privat", + "generateDots": "Membuat...", + "samlKeysGenerated": "Kunci Saml berhasil dibuat", + "samlPrivateKeyHelpText": "Hanya tulis: gunakan bidang ini hanya jika Anda perlu menyediakan kunci/certifikat privat Anda sendiri. Jika tidak, aplikasi akan membuatnya.", + "samlCertificateHelpText": "Anda dapat menentukan sertifikat Anda sendiri atau biarkan aplikasi membuatnya.", + "samlAuthnRequestSignedHelpText": "Ketika diaktifkan, permintaan autentikasi SAML ditandatangani dengan kunci privat di bawah ini. Sertifikat publik yang sesuai dikirimkan ke Penyedia Identitas sehingga dapat memverifikasi tanda tangan.", + "downloadCertificate": "Unduh sertifikat" } diff --git a/frontend/messages/it.json b/frontend/messages/it.json index 3b907f747c..845246ac70 100644 --- a/frontend/messages/it.json +++ b/frontend/messages/it.json @@ -1345,5 +1345,12 @@ "securityObjectiveScale": "Scala proprietà di sicurezza", "securityObjectiveScaleHelpText": "Scegli la scala per le proprietà di sicurezza (1 a 4 di default)", "actor": "Attore", - "action": "Azione" + "action": "Azione", + "privateKey": "Chiave privata", + "generateDots": "Generazione...", + "samlKeysGenerated": "Chiavi SAML generate con successo", + "samlPrivateKeyHelpText": "Scrittura sola: usa questo campo solo se devi fornire la tua chiave/certificato privata. Altrimenti, l'applicazione lo genererà.", + "samlCertificateHelpText": "Puoi specificare il tuo certificato o lasciare che l'applicazione lo generi.", + "samlAuthnRequestSignedHelpText": "Quando abilitato, la richiesta di autenticazione SAML viene firmata con la chiave privata qui sotto. Il certificato pubblico corrispondente viene inviato al Provider di Identità in modo che possa verificare la firma.", + "downloadCertificate": "Scarica certificato" } diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json index f846ba381d..d08346ae86 100644 --- a/frontend/messages/nl.json +++ b/frontend/messages/nl.json @@ -971,5 +971,12 @@ "securityObjectiveScale": "Schaal voor beveiligingskenmerken", "securityObjectiveScaleHelpText": "Kies uw schaal voor beveiligingskenmerken (standaard 1 tot 4)", "actor": "Actor", - "action": "Actie" + "action": "Actie", + "privateKey": "Privésleutel", + "generateDots": "Genereren...", + "samlKeysGenerated": "Saml-sleutels succesvol gegenereerd", + "samlPrivateKeyHelpText": "Alleen-schrijven: gebruik dit veld alleen als u uw eigen privésleutel/certificaat moet opgeven. Anders genereert de applicatie het.", + "samlCertificateHelpText": "U kunt uw eigen certificaat opgeven of de applicatie laten genereren.", + "samlAuthnRequestSignedHelpText": "Wanneer ingeschakeld, wordt het SAML-authenticatieverzoek ondertekend met de privésleutel hieronder. Het bijbehorende openbare certificaat wordt naar de identiteitsprovider verzonden, zodat deze de handtekening kan verifiëren.", + "downloadCertificate": "Certificaat downloaden" } diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index b6c407e8bd..101f8643fc 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -1710,5 +1710,12 @@ "image": "Obraz", "securityObjectives": "Właściwości bezpieczeństwa", "securityObjectiveScale": "Skala właściwości bezpieczeństwa", - "securityObjectiveScaleHelpText": "Wybierz skalę dla właściwości bezpieczeństwa (domyślnie 1 do 4)" + "securityObjectiveScaleHelpText": "Wybierz skalę dla właściwości bezpieczeństwa (domyślnie 1 do 4)", + "privateKey": "Klucz prywatny", + "generateDots": "Generowanie...", + "samlKeysGenerated": "Klucze SAML pomyślnie wygenerowane", + "samlPrivateKeyHelpText": "Tylko do zapisu: użyj tego pola tylko wtedy, gdy musisz dostarczyć własny klucz prywatny/certyfikat. W przeciwnym razie aplikacja wygeneruje go.", + "samlCertificateHelpText": "Możesz określić własny certyfikat lub pozwolić aplikacji wygenerować go.", + "samlAuthnRequestSignedHelpText": "Gdy włączone, żądanie uwierzytelniania SAML jest podpisywane kluczem prywatnym poniżej. Odpowiedni certyfikat publiczny jest wysyłany do dostawcy tożsamości, aby mógł zweryfikować podpis.", + "downloadCertificate": "Pobierz certyfikat" } diff --git a/frontend/messages/pt.json b/frontend/messages/pt.json index 0e1f4f2064..d12f7c3e2f 100644 --- a/frontend/messages/pt.json +++ b/frontend/messages/pt.json @@ -968,5 +968,12 @@ "securityObjectiveScale": "Escala de propriedades de segurança", "securityObjectiveScaleHelpText": "Escolha sua escala para propriedades de segurança (1 a 4 por padrão)", "actor": "Ator", - "action": "Ação" + "action": "Ação", + "privateKey": "Chave Privada", + "generateDots": "Gerar...", + "samlKeysGenerated": "Chaves SAML geradas com sucesso", + "samlPrivateKeyHelpText": "Somente escrita: use este campo apenas se precisar fornecer sua própria chave/certificado privada. Caso contrário, o aplicativo irá gerá-lo.", + "samlCertificateHelpText": "Você pode especificar seu próprio certificado ou deixar que o aplicativo o gere.", + "samlAuthnRequestSignedHelpText": "Quando ativado, a solicitação de autenticação SAML é assinada com a chave privada abaixo. O certificado público correspondente é enviado ao Provedor de Identidade para que ele possa verificar a assinatura.", + "downloadCertificate": "Baixar certificado" } diff --git a/frontend/messages/ro.json b/frontend/messages/ro.json index e102eddbe6..69668be0f1 100644 --- a/frontend/messages/ro.json +++ b/frontend/messages/ro.json @@ -967,5 +967,12 @@ "securityObjectiveScale": "Scară proprietăți de securitate", "securityObjectiveScaleHelpText": "Alegeți scala dvs. pentru proprietățile de securitate (1 la 4 implicit)", "actor": "Actor", - "action": "Acțiune" + "action": "Acțiune", + "privateKey": "Cheie privată", + "generateDots": "Generare...", + "samlKeysGenerated": "Cheile SAML au fost generate cu succes", + "samlPrivateKeyHelpText": "Doar scriere: utilizați acest câmp numai dacă aveți nevoie să furnizați propria cheie/certificat privat. În caz contrar, aplicația va genera cheia.", + "samlCertificateHelpText": "Puteți specifica propriul certificat sau lăsați aplicația să-l genereze.", + "samlAuthnRequestSignedHelpText": "Când este activat, cererea de autentificare SAML este semnată cu cheia privată de mai jos. Certificatul public corespunzător este trimis furnizorului de identitate pentru a putea verifica semnătura.", + "downloadCertificate": "Descărcați certificatul" } diff --git a/frontend/messages/sv.json b/frontend/messages/sv.json index a4a2345460..ed1a3f1c9b 100644 --- a/frontend/messages/sv.json +++ b/frontend/messages/sv.json @@ -961,5 +961,12 @@ "securityObjectiveScale": "Skala för säkerhetsmål", "securityObjectiveScaleHelpText": "Välj din skala för säkerhetsmål (1 till 4 som standard)", "actor": "Aktör", - "action": "Åtgärd" + "action": "Åtgärd", + "privateKey": "Privat nyckel", + "generateDots": "Genererar...", + "samlKeysGenerated": "SAML-nycklar genererades framgångsrikt", + "samlPrivateKeyHelpText": "Skrivskyddad: använd endast detta fält om du behöver ange din egen privata nyckel/certifikat. Annars kommer applikationen att generera det.", + "samlCertificateHelpText": "Du kan ange ditt eget certifikat eller låta applikationen generera det.", + "samlAuthnRequestSignedHelpText": "När det är aktiverat, signeras SAML-autentiseringsbegäran med den privata nyckeln nedan. Det motsvarande offentliga certifikatet skickas till identitetsleverantören så att den kan verifiera signaturen.", + "downloadCertificate": "Ladda ner certifikat" } diff --git a/frontend/messages/tr.json b/frontend/messages/tr.json index e6c648a127..f3d7534bb5 100644 --- a/frontend/messages/tr.json +++ b/frontend/messages/tr.json @@ -1703,5 +1703,12 @@ "image": "Resim", "securityObjectives": "Güvenlik özellikleri", "securityObjectiveScale": "Güvenlik özellikleri ölçeği", - "securityObjectiveScaleHelpText": "Güvenlik özellikleri için ölçeğinizi seçin (varsayılan olarak 1 ile 4)" + "securityObjectiveScaleHelpText": "Güvenlik özellikleri için ölçeğinizi seçin (varsayılan olarak 1 ile 4)", + "privateKey": "Özel Anahtar", + "generateDots": "Oluşturuluyor...", + "samlKeysGenerated": "Saml anahtarları başarıyla oluşturuldu", + "samlPrivateKeyHelpText": "Yalnızca yazma: özel anahtar/belgenizi sağlamanız gerekiyorsa bu alanı kullanın. Aksi takdirde uygulama onu oluşturacaktır.", + "samlCertificateHelpText": "Kendi sertifikanızı belirleyebilir veya uygulamanın oluşturmasını sağlayabilirsiniz.", + "samlAuthnRequestSignedHelpText": "Etkinleştirildiğinde, SAML kimlik doğrulama isteği aşağıdaki özel anahtar ile imzalanır. İmzayı doğrulayabilmesi için ilgili genel sertifika Kimlik Sağlayıcıya gönderilir.", + "downloadCertificate": "Sertifikayı indir" } diff --git a/frontend/messages/uk.json b/frontend/messages/uk.json index a3b7237dc9..d5c28bca9c 100644 --- a/frontend/messages/uk.json +++ b/frontend/messages/uk.json @@ -1484,5 +1484,12 @@ "securityObjectiveScale": "Шкала властивостей безпеки", "securityObjectiveScaleHelpText": "Виберіть шкалу для властивостей безпеки (за замовчуванням 1 до 4)", "actor": "Актор", - "action": "Дія" + "action": "Дія", + "privateKey": "Приватний ключ", + "generateDots": "Генерувати...", + "samlKeysGenerated": "Saml ключі успішно згенеровані", + "samlPrivateKeyHelpText": "Тільки для запису: використовуйте це поле, лише якщо вам потрібно надати власний приватний ключ/сертифікат. В іншому випадку додаток згенерує його.", + "samlCertificateHelpText": "Ви можете вказати власний сертифікат або дозволити додатку згенерувати його.", + "samlAuthnRequestSignedHelpText": "Коли увімкнено, запит на аутентифікацію SAML підписується приватним ключем нижче. Відповідний публічний сертифікат надсилається постачальнику ідентифікації, щоб він міг перевірити підпис.", + "downloadCertificate": "Завантажити сертифікат" } diff --git a/frontend/messages/ur.json b/frontend/messages/ur.json index 0a3a810cc3..e1e719dc7a 100644 --- a/frontend/messages/ur.json +++ b/frontend/messages/ur.json @@ -938,5 +938,12 @@ "securityObjectiveScale": "سیکیورٹی خصوصیات کی پیمانہ", "securityObjectiveScaleHelpText": "سیکیورٹی خصوصیات کے لیے اپنی پیمانہ منتخب کریں (ڈیفالٹ طور پر 1 سے 4)", "actor": "اداکار", - "action": "عمل" + "action": "عمل", + "privateKey": "کلی پریویٹ", + "generateDots": "تخلیق کریں...", + "samlKeysGenerated": "Saml کلیاں کامیابی کے ساتھ تیار کی گئیں", + "samlPrivateKeyHelpText": "صرف لکھنے کے لیے: صرف اس فیلڈ کو استعمال کریں اگر آپ اپنی ذاتی کلی/سرٹیفکیٹ فراہم کرنے کی ضرورت ہے۔ ورنہ، ایپلی کیشن اسے تیار کرے گا۔", + "samlCertificateHelpText": "آپ اپنی اپنی سرٹیفکیٹ فراہم کر سکتے ہیں یا ایپلی کیشن کو اسے تیار کرنے دیں۔", + "samlAuthnRequestSignedHelpText": "جب فعال ہو، تو SAML توثیق کی درخواست نیچے دی گئی پریویٹ کلی کے ساتھ سائن کی جاتی ہے۔ متعلقہ پبلک سرٹیفکیٹ کو شناخت فراہم کنندہ کو بھیجا جاتا ہے تاکہ وہ دستخط کی تصدیق کر سکے۔", + "downloadCertificate": "سرٹیفکیٹ ڈاؤن لوڈ کریں" } diff --git a/frontend/src/lib/components/Forms/ModelForm/SsoSettingForm.svelte b/frontend/src/lib/components/Forms/ModelForm/SsoSettingForm.svelte index 0105bdf775..49d4458249 100644 --- a/frontend/src/lib/components/Forms/ModelForm/SsoSettingForm.svelte +++ b/frontend/src/lib/components/Forms/ModelForm/SsoSettingForm.svelte @@ -10,6 +10,9 @@ import { m } from '$paraglide/messages'; import { Accordion } from '@skeletonlabs/skeleton-svelte'; import type { SuperValidated } from 'sveltekit-superforms'; + import { enhance } from '$app/forms'; + import Anchor from '$lib/components/Anchor/Anchor.svelte'; + interface Props { form: SuperValidated; model: ModelInfo; @@ -37,6 +40,26 @@ let openAccordionItems = $state(['saml', 'idp', 'sp']); let showSecretField = $state(!page.data?.ssoSettings.oidc_has_secret); + + let isGenerating = $state(false); + + const handleGenerateKeys = ({ cancel }) => { + if (!data.is_enabled || !data.authn_request_signed) { + cancel(); + return; + } + isGenerating = true; + + return async ({ result, update }) => { + isGenerating = false; + + if (result.type === 'success' && result.data?.generatedKeys?.cert) { + $formStore.sp_x509cert = result.data.generatedKeys.cert; + } + + await update(); + }; + }; -