Skip to content

Commit 86098a6

Browse files
feat(superuser): Allow super user creation using signup endpoint (#5266)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 8b4be6c commit 86098a6

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

api/custom_auth/serializers.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Any
22

3+
from common.core.utils import is_saas
34
from django.conf import settings
45
from djoser.serializers import UserCreateSerializer # type: ignore[import-untyped]
56
from rest_framework import serializers
@@ -52,6 +53,11 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
5253
if not settings.COOKIE_AUTH_ENABLED:
5354
self.fields["key"] = serializers.SerializerMethodField()
5455

56+
if not is_saas():
57+
self.fields["superuser"] = serializers.BooleanField(
58+
write_only=True, required=False
59+
)
60+
5561
class Meta(UserCreateSerializer.Meta): # type: ignore[misc]
5662
fields = UserCreateSerializer.Meta.fields + (
5763
"is_active",
@@ -75,6 +81,17 @@ class Meta(UserCreateSerializer.Meta): # type: ignore[misc]
7581
def validate(self, attrs): # type: ignore[no-untyped-def]
7682
attrs = super().validate(attrs)
7783
email = attrs.get("email")
84+
if attrs.get("superuser"):
85+
if FFAdminUser.objects.exists():
86+
raise serializers.ValidationError(
87+
{
88+
"superuser": (
89+
"A superuser can only be created through this "
90+
"endpoint if no other users exist."
91+
)
92+
}
93+
)
94+
7895
if settings.AUTH_CONTROLLER_INSTALLED:
7996
from auth_controller.controller import ( # type: ignore[import-not-found,import-untyped,unused-ignore]
8097
is_authentication_method_valid,

api/tests/integration/custom_auth/end_to_end/test_custom_auth_integration.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,3 +617,87 @@ def test_register_with_sign_up_type(client, db, settings): # type: ignore[no-un
617617
assert response_json["sign_up_type"] == sign_up_type
618618

619619
assert FFAdminUser.objects.filter(email=email, sign_up_type=sign_up_type).exists()
620+
621+
622+
def test_can_create_superuser(
623+
db: None, api_client: APIClient, mocker: MockerFixture
624+
) -> None:
625+
# Given
626+
mocker.patch("custom_auth.serializers.is_saas", return_value=False)
627+
628+
email = "test@example.com"
629+
password = FFAdminUser.objects.make_random_password()
630+
register_data = {
631+
"email": email,
632+
"password": password,
633+
"re_password": password,
634+
"first_name": "user",
635+
"last_name": "test",
636+
"superuser": True,
637+
"other_field": "meh",
638+
}
639+
url = reverse("api-v1:custom_auth:ffadminuser-list")
640+
641+
# When
642+
response = api_client.post(url, data=register_data)
643+
644+
# Then
645+
assert response.status_code == status.HTTP_201_CREATED
646+
user = FFAdminUser.objects.get(email=email)
647+
assert user.superuser is True
648+
649+
650+
def test_cannot_create_superuser_on_saas_build(
651+
db: None, api_client: APIClient, mocker: MockerFixture
652+
) -> None:
653+
# Given
654+
mocker.patch("custom_auth.serializers.is_saas", return_value=True)
655+
656+
email = "test@example.com"
657+
password = FFAdminUser.objects.make_random_password()
658+
register_data = {
659+
"email": email,
660+
"password": password,
661+
"re_password": password,
662+
"first_name": "user",
663+
"last_name": "test",
664+
"superuser": True,
665+
}
666+
url = reverse("api-v1:custom_auth:ffadminuser-list")
667+
668+
# When
669+
response = api_client.post(url, data=register_data)
670+
671+
# Then
672+
assert response.status_code == status.HTTP_201_CREATED
673+
user = FFAdminUser.objects.get(email=email)
674+
assert user.superuser is False
675+
676+
677+
def test_cannot_create_superuser_if_any_user_exists(
678+
admin_user: FFAdminUser, api_client: APIClient, mocker: MockerFixture
679+
) -> None:
680+
# Given
681+
mocker.patch("custom_auth.serializers.is_saas", return_value=False)
682+
683+
email = "test@example.com"
684+
password = FFAdminUser.objects.make_random_password()
685+
register_data = {
686+
"email": email,
687+
"password": password,
688+
"re_password": password,
689+
"first_name": "user",
690+
"last_name": "test",
691+
"superuser": True,
692+
}
693+
url = reverse("api-v1:custom_auth:ffadminuser-list")
694+
695+
# When
696+
response = api_client.post(url, data=register_data)
697+
698+
# Then
699+
assert response.status_code == status.HTTP_400_BAD_REQUEST
700+
assert response.json()["superuser"] == [
701+
"A superuser can only be created through this endpoint if no other users exist."
702+
]
703+
assert FFAdminUser.objects.filter(email=email).exists() is False

api/users/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ class Meta:
131131
def __str__(self): # type: ignore[no-untyped-def]
132132
return self.email
133133

134+
@property
135+
def superuser(self) -> bool:
136+
return self.is_staff and self.is_superuser
137+
138+
@superuser.setter
139+
def superuser(self, value: bool) -> None:
140+
self.is_staff = value
141+
self.is_superuser = value
142+
134143
@hook(AFTER_CREATE) # type: ignore[misc]
135144
def schedule_hubspot_tracking(self) -> None:
136145
if settings.ENABLE_HUBSPOT_LEAD_TRACKING:

0 commit comments

Comments
 (0)