From c8b193e6588c986dbbbc79a44d03e3d9f8258730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Jes=C3=BAs=20Pe=C3=B1a=20Rodr=C3=ADguez?= Date: Tue, 1 Jul 2025 18:41:16 +0200 Subject: [PATCH] fix(saml): add user to SAML tenant (#8152) --- .env | 2 -- api/src/backend/api/tests/test_views.py | 24 +++++++----------- api/src/backend/api/v1/views.py | 25 +++++++++++++------ .../backend/config/settings/social_login.py | 21 ++++++++++------ 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/.env b/.env index d985a88e50..4a0e1ef2d4 100644 --- a/.env +++ b/.env @@ -143,8 +143,6 @@ SOCIAL_GITHUB_OAUTH_CLIENT_ID="" SOCIAL_GITHUB_OAUTH_CLIENT_SECRET="" # Single Sign-On (SSO) -SAML_PUBLIC_CERT="" -SAML_PRIVATE_KEY="" SAML_SSO_CALLBACK_URL="${AUTH_URL}/api/auth/callback/saml" # Lighthouse tracing diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 22f5d72c1d..1c54dcd930 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -5710,21 +5710,6 @@ class TestSAMLInitiateAPIView: assert response.status_code == status.HTTP_403_FORBIDDEN assert response.json()["errors"]["detail"] == "Unauthorized domain." - def test_missing_certificates(self, authenticated_client, saml_setup, monkeypatch): - monkeypatch.setenv("SAML_PUBLIC_CERT", "") - monkeypatch.setenv("SAML_PRIVATE_KEY", "") - - url = reverse("api_saml_initiate") - payload = {"email_domain": saml_setup["email"]} - - response = authenticated_client.post(url, data=payload, format="json") - - assert response.status_code == status.HTTP_403_FORBIDDEN - assert ( - response.json()["errors"]["detail"] - == "SAML configuration is invalid: missing certificates." - ) - @pytest.mark.django_db class TestSAMLConfigurationViewSet: @@ -5941,6 +5926,15 @@ class TestTenantFinishACSView: .exists() ) + # Membership should have been created with default role + membership = Membership.objects.using(MainRouter.admin_db).get( + user=user, tenant=tenants_fixture[0] + ) + assert membership.role == Membership.RoleChoices.MEMBER + assert membership.user == user + assert membership.tenant == tenants_fixture[0] + + # Restore original user state user.email = original_email user.name = original_name user.company_name = original_company diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index 4ea380e9d0..06b55ad716 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -476,15 +476,15 @@ class SAMLInitiateAPIView(GenericAPIView): {"detail": "Unauthorized domain."}, status=status.HTTP_403_FORBIDDEN ) - # Check certificates are not empty - saml_public_cert = os.getenv("SAML_PUBLIC_CERT", "").strip() - saml_private_key = os.getenv("SAML_PRIVATE_KEY", "").strip() + # Check certificates are not empty (TODO: Validate certificates) + # saml_public_cert = os.getenv("SAML_PUBLIC_CERT", "").strip() + # saml_private_key = os.getenv("SAML_PRIVATE_KEY", "").strip() - if not saml_public_cert or not saml_private_key: - return Response( - {"detail": "SAML configuration is invalid: missing certificates."}, - status=status.HTTP_403_FORBIDDEN, - ) + # if not saml_public_cert or not saml_private_key: + # return Response( + # {"detail": "SAML configuration is invalid: missing certificates."}, + # status=status.HTTP_403_FORBIDDEN, + # ) # Build the SAML login URL using the configured API host api_host = os.getenv("API_BASE_URL") @@ -616,6 +616,15 @@ class TenantFinishACSView(FinishACSView): role=role, tenant_id=tenant.id, ) + membership, _ = Membership.objects.using(MainRouter.admin_db).get_or_create( + user=user, + tenant=tenant, + defaults={ + "user": user, + "tenant": tenant, + "role": Membership.RoleChoices.MEMBER, + }, + ) serializer = TokenSocialLoginSerializer(data={"email": user.email}) serializer.is_valid(raise_exception=True) diff --git a/api/src/backend/config/settings/social_login.py b/api/src/backend/config/settings/social_login.py index cf4e21d75b..ae9deb363a 100644 --- a/api/src/backend/config/settings/social_login.py +++ b/api/src/backend/config/settings/social_login.py @@ -25,9 +25,9 @@ SOCIALACCOUNT_EMAIL_AUTHENTICATION = True SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = True SOCIALACCOUNT_ADAPTER = "api.adapters.ProwlerSocialAccountAdapter" -# SAML keys -SAML_PUBLIC_CERT = env("SAML_PUBLIC_CERT", default="") -SAML_PRIVATE_KEY = env("SAML_PRIVATE_KEY", default="") +# SAML keys (TODO: Validate certificates) +# SAML_PUBLIC_CERT = env("SAML_PUBLIC_CERT", default="") +# SAML_PRIVATE_KEY = env("SAML_PRIVATE_KEY", default="") SOCIALACCOUNT_PROVIDERS = { "google": { @@ -60,12 +60,17 @@ SOCIALACCOUNT_PROVIDERS = { "entity_id": "urn:prowler.com:sp", }, "advanced": { - "x509cert": SAML_PUBLIC_CERT, - "private_key": SAML_PRIVATE_KEY, + # "x509cert": SAML_PUBLIC_CERT, + # "private_key": SAML_PRIVATE_KEY, + # "authn_request_signed": True, + # "want_assertion_signed": True, + # "want_message_signed": True, "name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", - "authn_request_signed": True, - "want_assertion_signed": True, - "want_message_signed": True, + "authn_request_signed": False, + "logout_request_signed": False, + "logout_response_signed": False, + "want_assertion_encrypted": False, + "want_name_id_encrypted": False, }, }, }