From f49008bbb6ace42fbccfedb6d7bb34a300987288 Mon Sep 17 00:00:00 2001 From: "gcp-cherry-pick-bot[bot]" <98988430+gcp-cherry-pick-bot[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:05:59 +0100 Subject: [PATCH] crypto: validate that generated certificate's name is unique (cherry-pick #12015) (#12016) crypto: validate that generated certificate's name is unique (#12015) Signed-off-by: Jens Langhammer Co-authored-by: Jens L. --- authentik/crypto/api.py | 11 +++++++---- authentik/crypto/tests.py | 11 +++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/authentik/crypto/api.py b/authentik/crypto/api.py index 5bd2665347e7..ed31e82137bb 100644 --- a/authentik/crypto/api.py +++ b/authentik/crypto/api.py @@ -24,6 +24,7 @@ from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.request import Request from rest_framework.response import Response +from rest_framework.validators import UniqueValidator from rest_framework.viewsets import ModelViewSet from structlog.stdlib import get_logger @@ -181,7 +182,10 @@ class CertificateDataSerializer(PassiveSerializer): class CertificateGenerationSerializer(PassiveSerializer): """Certificate generation parameters""" - common_name = CharField() + common_name = CharField( + validators=[UniqueValidator(queryset=CertificateKeyPair.objects.all())], + source="name", + ) subject_alt_name = CharField(required=False, allow_blank=True, label=_("Subject-alt name")) validity_days = IntegerField(initial=365) alg = ChoiceField(default=PrivateKeyAlg.RSA, choices=PrivateKeyAlg.choices) @@ -242,11 +246,10 @@ def list(self, request, *args, **kwargs): def generate(self, request: Request) -> Response: """Generate a new, self-signed certificate-key pair""" data = CertificateGenerationSerializer(data=request.data) - if not data.is_valid(): - return Response(data.errors, status=400) + data.is_valid(raise_exception=True) raw_san = data.validated_data.get("subject_alt_name", "") sans = raw_san.split(",") if raw_san != "" else [] - builder = CertificateBuilder(data.validated_data["common_name"]) + builder = CertificateBuilder(data.validated_data["name"]) builder.alg = data.validated_data["alg"] builder.build( subject_alt_names=sans, diff --git a/authentik/crypto/tests.py b/authentik/crypto/tests.py index e2dc755e7ca0..bb0c1b4e31b6 100644 --- a/authentik/crypto/tests.py +++ b/authentik/crypto/tests.py @@ -89,6 +89,17 @@ def test_builder_api(self): self.assertIsInstance(ext[1], DNSName) self.assertEqual(ext[1].value, "baz") + def test_builder_api_duplicate(self): + """Test Builder (via API)""" + cert = create_test_cert() + self.client.force_login(create_test_admin_user()) + res = self.client.post( + reverse("authentik_api:certificatekeypair-generate"), + data={"common_name": cert.name, "subject_alt_name": "bar,baz", "validity_days": 3}, + ) + self.assertEqual(res.status_code, 400) + self.assertJSONEqual(res.content, {"common_name": ["This field must be unique."]}) + def test_builder_api_empty_san(self): """Test Builder (via API)""" self.client.force_login(create_test_admin_user())