diff --git a/authentik/crypto/api.py b/authentik/crypto/api.py index 5bd2665347..ed31e82137 100644 --- a/authentik/crypto/api.py +++ b/authentik/crypto/api.py @@ -24,6 +24,7 @@ from rest_framework.fields import ( 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 @@ class CertificateKeyPairViewSet(UsedByMixin, ModelViewSet): 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 e2dc755e7c..bb0c1b4e31 100644 --- a/authentik/crypto/tests.py +++ b/authentik/crypto/tests.py @@ -89,6 +89,17 @@ class TestCrypto(APITestCase): 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())