mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-04-15 01:28:30 +00:00
Introduced endpoint to create a new user as admin
This commit is contained in:
parent
db27dcb5ad
commit
3679d09cf4
10 changed files with 507 additions and 36 deletions
backend/src/baserow/core/user
changelog/entries/unreleased/feature
premium/backend
src/baserow_premium
tests/baserow_premium_tests
web-frontend
|
@ -104,6 +104,55 @@ class UserHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
except User.DoesNotExist:
|
||||
raise UserNotFound("The user with the provided parameters is not found.")
|
||||
|
||||
def force_create_user(self, email, name, password, **kwargs):
|
||||
"""
|
||||
Creates a new user and their profile.
|
||||
|
||||
:param email: The username/email of the new user.
|
||||
:param name: The full name of the new user.
|
||||
:param password: The password of the new user.
|
||||
:param kwargs: Additional kwargs that must be added when creating the User
|
||||
object.
|
||||
:raises UserAlreadyExist: When the user with the provided email already exists.
|
||||
:raises PasswordDoesNotMatchValidation: When a provided password does not match
|
||||
password validation.
|
||||
:raises DeactivatedUserException: When a user with the provided email exists but
|
||||
has been deactivated.
|
||||
:return: The newly created user object.
|
||||
"""
|
||||
|
||||
language = settings.LANGUAGE_CODE
|
||||
if "language" in kwargs:
|
||||
language = kwargs.pop("language") or settings.LANGUAGE_CODE
|
||||
|
||||
email = normalize_email_address(email)
|
||||
user_query = User.objects.filter(Q(email=email) | Q(username=email))
|
||||
if user_query.exists():
|
||||
user = user_query.first()
|
||||
if user.is_active:
|
||||
raise UserAlreadyExist(f"A user with email {email} already exists.")
|
||||
else:
|
||||
raise DeactivatedUserException(
|
||||
f"User with email {email} has been deactivated."
|
||||
)
|
||||
|
||||
user = User(first_name=name, email=email, username=email, **kwargs)
|
||||
|
||||
if password is not None:
|
||||
try:
|
||||
validate_password(password, user)
|
||||
except ValidationError as e:
|
||||
raise PasswordDoesNotMatchValidation(e.messages)
|
||||
user.set_password(password)
|
||||
|
||||
user.save()
|
||||
|
||||
# Immediately create the one-to-one relationship with the user profile
|
||||
# so we can safely use it everywhere else in the code.
|
||||
UserProfile.objects.create(user=user, language=language)
|
||||
|
||||
return user
|
||||
|
||||
def create_user(
|
||||
self,
|
||||
name: str,
|
||||
|
@ -135,24 +184,11 @@ class UserHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
:raises WorkspaceInvitationEmailMismatch: If the workspace invitation email
|
||||
does not match the one of the user.
|
||||
:raises SignupDisabledError: If signing up is disabled.
|
||||
:raises PasswordDoesNotMatchValidation: When a provided password does not match
|
||||
password validation.
|
||||
:return: The user object.
|
||||
"""
|
||||
|
||||
core_handler = CoreHandler()
|
||||
|
||||
email = normalize_email_address(email)
|
||||
user_query = User.objects.filter(Q(email=email) | Q(username=email))
|
||||
if user_query.exists():
|
||||
user = user_query.first()
|
||||
if user.is_active:
|
||||
raise UserAlreadyExist(f"A user with email {email} already exists.")
|
||||
else:
|
||||
raise DeactivatedUserException(
|
||||
f"User with email {email} has been deactivated."
|
||||
)
|
||||
|
||||
workspace_invitation = None
|
||||
workspace_user = None
|
||||
|
||||
|
@ -176,32 +212,21 @@ class UserHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
if not (allow_new_signups or allow_signup_for_invited_user):
|
||||
raise DisabledSignupError("Sign up is disabled.")
|
||||
|
||||
user = User(first_name=name, email=email, username=email)
|
||||
|
||||
if password is not None:
|
||||
try:
|
||||
validate_password(password, user)
|
||||
except ValidationError as e:
|
||||
raise PasswordDoesNotMatchValidation(e.messages)
|
||||
user.set_password(password)
|
||||
|
||||
if not User.objects.exists():
|
||||
user = self.force_create_user(
|
||||
email=email,
|
||||
name=name,
|
||||
password=password,
|
||||
# This is the first ever user created in this baserow instance and
|
||||
# therefore the administrator user, lets give them staff rights so they
|
||||
# can set baserow wide settings.
|
||||
user.is_staff = True
|
||||
is_staff=not User.objects.exists(),
|
||||
language=language,
|
||||
)
|
||||
|
||||
if instance_settings.show_admin_signup_page:
|
||||
instance_settings.show_admin_signup_page = False
|
||||
instance_settings.save()
|
||||
|
||||
user.save()
|
||||
|
||||
# Immediately create the one-to-one relationship with the user profile
|
||||
# so we can safely use it everywhere else in the code.
|
||||
language = language or settings.LANGUAGE_CODE
|
||||
UserProfile.objects.create(user=user, language=language)
|
||||
|
||||
# If we have an invitation to a workspace, then accept it.
|
||||
if workspace_invitation_token:
|
||||
workspace_user = core_handler.accept_workspace_invitation(
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "feature",
|
||||
"message": "Introduced endpoint to create a new user as admin.",
|
||||
"issue_number": null,
|
||||
"bullet_points": [],
|
||||
"created_at": "2023-10-12"
|
||||
}
|
|
@ -3,6 +3,7 @@ from typing import Optional
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.password_validation import validate_password
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db.models import Q
|
||||
|
||||
from baserow_premium.admin.users.exceptions import (
|
||||
CannotDeactivateYourselfException,
|
||||
|
@ -14,12 +15,54 @@ from baserow_premium.license.handler import LicenseHandler
|
|||
|
||||
from baserow.core.exceptions import IsNotAdminError
|
||||
from baserow.core.signals import before_user_deleted
|
||||
from baserow.core.user.exceptions import PasswordDoesNotMatchValidation
|
||||
from baserow.core.user.exceptions import (
|
||||
PasswordDoesNotMatchValidation,
|
||||
UserAlreadyExist,
|
||||
)
|
||||
from baserow.core.user.handler import UserHandler
|
||||
from baserow.core.user.utils import normalize_email_address
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class UserAdminHandler:
|
||||
def create_user(
|
||||
self,
|
||||
requesting_user: User,
|
||||
username: str,
|
||||
name: str,
|
||||
password: str,
|
||||
is_active: bool = True,
|
||||
is_staff: bool = False,
|
||||
):
|
||||
"""
|
||||
Creates a new user with the provided values if the requesting user has admin
|
||||
access. The user will be created, even if the signups are disabled.
|
||||
|
||||
:param requesting_user: The user who is making the request to creata a user, the
|
||||
user must be a staff member or else an exception will be raised.
|
||||
:param username: New username/email to set for the user.
|
||||
:param name: New name to set on the user.
|
||||
:param password: New password to securely set for the user.
|
||||
:param is_staff: Value used to set if the user is an admin or not.
|
||||
:param is_active: Value to disable or enable login for the user.
|
||||
"""
|
||||
|
||||
LicenseHandler.raise_if_user_doesnt_have_feature_instance_wide(
|
||||
PREMIUM, requesting_user
|
||||
)
|
||||
self._raise_if_not_permitted(requesting_user)
|
||||
|
||||
user = UserHandler().force_create_user(
|
||||
email=username,
|
||||
name=name,
|
||||
password=password,
|
||||
is_staff=is_staff,
|
||||
is_active=is_active,
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
def update_user(
|
||||
self,
|
||||
requesting_user: User,
|
||||
|
@ -46,6 +89,7 @@ class UserAdminHandler:
|
|||
:param username: Optional new username/email to set for the user.
|
||||
:raises PasswordDoesNotMatchValidation: When the provided password value is not
|
||||
a valid password.
|
||||
:raises UserAlreadyExist: If a user with that username already exists.
|
||||
"""
|
||||
|
||||
LicenseHandler.raise_if_user_doesnt_have_feature_instance_wide(
|
||||
|
@ -74,8 +118,17 @@ class UserAdminHandler:
|
|||
if name is not None:
|
||||
user.first_name = name
|
||||
if username is not None:
|
||||
user.email = username
|
||||
user.username = username
|
||||
email = normalize_email_address(username)
|
||||
user_query = User.objects.filter(
|
||||
Q(email=email) | Q(username=email), ~Q(id=user.id)
|
||||
)
|
||||
if email != user.email and user_query.exists():
|
||||
raise UserAlreadyExist(
|
||||
f"A user with the username {email} already exists."
|
||||
)
|
||||
|
||||
user.email = email
|
||||
user.username = email
|
||||
|
||||
user.save()
|
||||
return user
|
||||
|
|
|
@ -17,3 +17,9 @@ USER_ADMIN_UNKNOWN_USER = (
|
|||
HTTP_400_BAD_REQUEST,
|
||||
"Unknown user supplied.",
|
||||
)
|
||||
|
||||
USER_ADMIN_ALREADY_EXISTS = (
|
||||
"USER_ADMIN_ALREADY_EXISTS",
|
||||
HTTP_400_BAD_REQUEST,
|
||||
"A user with that username/email already exists.",
|
||||
)
|
||||
|
|
|
@ -66,6 +66,27 @@ class UserAdminResponseSerializer(ModelSerializer):
|
|||
extra_kwargs = _USER_ADMIN_SERIALIZER_API_DOC_KWARGS
|
||||
|
||||
|
||||
class UserAdminCreateSerializer(
|
||||
UnknownFieldRaisesExceptionSerializerMixin, ModelSerializer
|
||||
):
|
||||
"""
|
||||
Serializes a request body for creating a new user. Do not use for returning user
|
||||
data as the password will be returned also.
|
||||
"""
|
||||
|
||||
# Max length set to match django user models first_name fields max length
|
||||
name = CharField(source="first_name", max_length=150, required=True)
|
||||
username = EmailField(required=True)
|
||||
password = CharField(validators=[password_validation], required=True)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "name", "is_active", "is_staff", "password")
|
||||
extra_kwargs = {
|
||||
**_USER_ADMIN_SERIALIZER_API_DOC_KWARGS,
|
||||
}
|
||||
|
||||
|
||||
class UserAdminUpdateSerializer(
|
||||
UnknownFieldRaisesExceptionSerializerMixin, ModelSerializer
|
||||
):
|
||||
|
|
|
@ -8,11 +8,13 @@ from baserow_premium.admin.users.exceptions import (
|
|||
)
|
||||
from baserow_premium.admin.users.handler import UserAdminHandler
|
||||
from baserow_premium.api.admin.users.errors import (
|
||||
USER_ADMIN_ALREADY_EXISTS,
|
||||
USER_ADMIN_CANNOT_DEACTIVATE_SELF,
|
||||
USER_ADMIN_CANNOT_DELETE_SELF,
|
||||
USER_ADMIN_UNKNOWN_USER,
|
||||
)
|
||||
from baserow_premium.api.admin.users.serializers import (
|
||||
UserAdminCreateSerializer,
|
||||
UserAdminResponseSerializer,
|
||||
UserAdminUpdateSerializer,
|
||||
)
|
||||
|
@ -32,6 +34,7 @@ from baserow.api.decorators import map_exceptions, validate_body
|
|||
from baserow.api.schemas import get_error_schema
|
||||
from baserow.api.user.schemas import authenticate_user_schema
|
||||
from baserow.api.user.serializers import get_all_user_data_serialized
|
||||
from baserow.core.user.exceptions import DeactivatedUserException, UserAlreadyExist
|
||||
from baserow.core.user.utils import generate_session_tokens_for_user
|
||||
|
||||
from .serializers import BaserowImpersonateAuthTokenSerializer
|
||||
|
@ -71,6 +74,41 @@ class UsersAdminView(AdminListingView):
|
|||
)
|
||||
return super().get(request)
|
||||
|
||||
@extend_schema(
|
||||
tags=["Admin"],
|
||||
request=UserAdminCreateSerializer,
|
||||
operation_id="admin_create_user",
|
||||
description=(
|
||||
"Creates and returns a new user if the requesting user is staff. This "
|
||||
"works even if new signups are disabled. \n\nThis is a **premium** feature."
|
||||
),
|
||||
responses={
|
||||
200: UserAdminResponseSerializer(),
|
||||
400: get_error_schema(
|
||||
[
|
||||
"ERROR_REQUEST_BODY_VALIDATION",
|
||||
"ERROR_FEATURE_NOT_AVAILABLE",
|
||||
"USER_ADMIN_ALREADY_EXISTS",
|
||||
]
|
||||
),
|
||||
},
|
||||
)
|
||||
@validate_body(UserAdminCreateSerializer)
|
||||
@map_exceptions(
|
||||
{
|
||||
UserAlreadyExist: USER_ADMIN_ALREADY_EXISTS,
|
||||
DeactivatedUserException: USER_ADMIN_ALREADY_EXISTS,
|
||||
}
|
||||
)
|
||||
@transaction.atomic
|
||||
def post(self, request, data) -> Response:
|
||||
"""Creates a new user with the supplied attributes."""
|
||||
|
||||
handler = UserAdminHandler()
|
||||
user = handler.create_user(request.user, **data)
|
||||
|
||||
return Response(UserAdminResponseSerializer(user).data)
|
||||
|
||||
|
||||
class UserAdminView(APIView):
|
||||
permission_classes = (IsAdminUser,)
|
||||
|
@ -97,6 +135,7 @@ class UserAdminView(APIView):
|
|||
"ERROR_REQUEST_BODY_VALIDATION",
|
||||
"USER_ADMIN_CANNOT_DEACTIVATE_SELF",
|
||||
"USER_ADMIN_UNKNOWN_USER",
|
||||
"USER_ADMIN_ALREADY_EXISTS",
|
||||
"ERROR_FEATURE_NOT_AVAILABLE",
|
||||
]
|
||||
),
|
||||
|
@ -108,6 +147,7 @@ class UserAdminView(APIView):
|
|||
{
|
||||
CannotDeactivateYourselfException: USER_ADMIN_CANNOT_DEACTIVATE_SELF,
|
||||
UserDoesNotExistException: USER_ADMIN_UNKNOWN_USER,
|
||||
UserAlreadyExist: USER_ADMIN_ALREADY_EXISTS,
|
||||
}
|
||||
)
|
||||
@transaction.atomic
|
||||
|
|
|
@ -11,7 +11,10 @@ from baserow_premium.admin.users.handler import UserAdminHandler
|
|||
from baserow_premium.license.exceptions import FeaturesNotAvailableError
|
||||
|
||||
from baserow.core.exceptions import IsNotAdminError
|
||||
from baserow.core.user.exceptions import PasswordDoesNotMatchValidation
|
||||
from baserow.core.user.exceptions import (
|
||||
PasswordDoesNotMatchValidation,
|
||||
UserAlreadyExist,
|
||||
)
|
||||
|
||||
User = get_user_model()
|
||||
invalid_passwords = [
|
||||
|
@ -166,6 +169,128 @@ def test_admin_can_deactive_and_unstaff_other_users(premium_data_fixture):
|
|||
assert not active_user.is_active
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_create_user(premium_data_fixture):
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
user = handler.create_user(
|
||||
requesting_user=admin_user,
|
||||
username="new@test.nl",
|
||||
name="Test",
|
||||
password="password",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
user = User.objects.get(pk=user.id)
|
||||
assert user.username == "new@test.nl"
|
||||
assert user.email == "new@test.nl"
|
||||
assert user.first_name == "Test"
|
||||
assert user.is_active is True
|
||||
assert user.is_staff is True
|
||||
assert user.check_password("password")
|
||||
assert user.profile.id
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_create_user_as_non_admin(premium_data_fixture):
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=False,
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
|
||||
with pytest.raises(IsNotAdminError):
|
||||
handler.create_user(
|
||||
requesting_user=admin_user,
|
||||
username="new@test.nl",
|
||||
name="Test",
|
||||
password="password",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_create_user_without_license(premium_data_fixture):
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=False,
|
||||
has_active_premium_license=False,
|
||||
)
|
||||
|
||||
with pytest.raises(FeaturesNotAvailableError):
|
||||
handler.create_user(
|
||||
requesting_user=admin_user,
|
||||
username="new@test.nl",
|
||||
name="Test",
|
||||
password="password",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_create_user_that_already_exists(premium_data_fixture):
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
|
||||
with pytest.raises(UserAlreadyExist):
|
||||
handler.create_user(
|
||||
requesting_user=admin_user,
|
||||
username="test@test.nl",
|
||||
name="Test",
|
||||
password="password",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_create_user_with_invalid_password(premium_data_fixture):
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
|
||||
with pytest.raises(PasswordDoesNotMatchValidation):
|
||||
handler.create_user(
|
||||
requesting_user=admin_user,
|
||||
username="test2@test.nl",
|
||||
name="Test",
|
||||
password="t",
|
||||
is_active=True,
|
||||
is_staff=True,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_updating_a_users_password_uses_djangos_built_in_smart_set_password(
|
||||
|
@ -357,3 +482,39 @@ def test_raises_exception_when_updating_an_unknown_user(premium_data_fixture):
|
|||
)
|
||||
with pytest.raises(UserDoesNotExistException):
|
||||
handler.update_user(admin_user, 99999, username="new_password")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_raises_exception_when_changing_to_an_existing_user(premium_data_fixture):
|
||||
premium_data_fixture.create_user(email="existing@test.nl")
|
||||
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
is_active=True,
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
with pytest.raises(UserAlreadyExist):
|
||||
handler.update_user(admin_user, admin_user.id, username="existing@test.nl")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_does_not_raise_exception_when_changing_to_same_username(premium_data_fixture):
|
||||
handler = UserAdminHandler()
|
||||
admin_user = premium_data_fixture.create_user(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
is_active=True,
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
assert (
|
||||
handler.update_user(admin_user, admin_user.id, username="test@test.nl").email
|
||||
== "test@test.nl"
|
||||
)
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import json
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.shortcuts import reverse
|
||||
from django.test.utils import override_settings
|
||||
from django.utils import timezone
|
||||
from django.utils.datetime_safe import datetime
|
||||
|
||||
import pytest
|
||||
from freezegun import freeze_time
|
||||
from rest_framework.status import (
|
||||
HTTP_200_OK,
|
||||
HTTP_204_NO_CONTENT,
|
||||
|
@ -20,6 +22,7 @@ from baserow.core.models import (
|
|||
WORKSPACE_USER_PERMISSION_MEMBER,
|
||||
)
|
||||
|
||||
User = get_user_model()
|
||||
invalid_passwords = [
|
||||
"a",
|
||||
"ab",
|
||||
|
@ -554,6 +557,132 @@ def test_non_admin_cannot_patch_user_without_premium_license(
|
|||
assert non_admin_user.email == "test@test.nl"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_admin_cannot_create_user_without_body(api_client, premium_data_fixture):
|
||||
user, token = premium_data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
date_joined=datetime(2021, 4, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
url = reverse("api:premium:admin:users:list")
|
||||
response = api_client.post(
|
||||
url,
|
||||
{},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
user.refresh_from_db()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
response_json = response.json()
|
||||
assert response_json["error"] == "ERROR_REQUEST_BODY_VALIDATION"
|
||||
assert response_json["detail"]["name"][0]["code"] == "required"
|
||||
assert response_json["detail"]["password"][0]["code"] == "required"
|
||||
assert response_json["detail"]["username"][0]["code"] == "required"
|
||||
assert User.objects.all().count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_admin_cannot_create_user_without_license(api_client, premium_data_fixture):
|
||||
user, token = premium_data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
date_joined=datetime(2021, 4, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
|
||||
has_active_premium_license=False,
|
||||
)
|
||||
url = reverse("api:premium:admin:users:list")
|
||||
response = api_client.post(
|
||||
url,
|
||||
{"username": "test2@test.nl", "password": "Test1234", "name": "Test1"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
user.refresh_from_db()
|
||||
print(response.json())
|
||||
assert response.status_code == HTTP_402_PAYMENT_REQUIRED
|
||||
response_json = response.json()
|
||||
assert response_json["error"] == "ERROR_FEATURE_NOT_AVAILABLE"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_admin_cannot_create_user_that_already_exists(api_client, premium_data_fixture):
|
||||
user, token = premium_data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
date_joined=datetime(2021, 4, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
premium_data_fixture.create_user(email="test2@test.nl")
|
||||
url = reverse("api:premium:admin:users:list")
|
||||
response = api_client.post(
|
||||
url,
|
||||
{"username": "test2@test.nl", "password": "Test1234", "name": "Test1"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
user.refresh_from_db()
|
||||
assert response.status_code == HTTP_400_BAD_REQUEST
|
||||
response_json = response.json()
|
||||
assert response_json["error"] == "USER_ADMIN_ALREADY_EXISTS"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_admin_can_create_user(api_client, premium_data_fixture):
|
||||
user, token = premium_data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
date_joined=datetime(2021, 4, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
url = reverse("api:premium:admin:users:list")
|
||||
with freeze_time("2020-01-02 12:00"):
|
||||
response = api_client.post(
|
||||
url,
|
||||
{
|
||||
"username": "test2@test.nl",
|
||||
"password": "Test1234",
|
||||
"name": "Test1",
|
||||
"is_staff": True,
|
||||
"is_active": True,
|
||||
},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
user = User.objects.all().last()
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert response.json() == {
|
||||
"date_joined": "2020-01-02T12:00:00Z",
|
||||
"name": "Test1",
|
||||
"username": "test2@test.nl",
|
||||
"groups": [], # GroupDeprecation
|
||||
"workspaces": [],
|
||||
"id": user.id,
|
||||
"is_staff": True,
|
||||
"is_active": True,
|
||||
"last_login": None,
|
||||
}
|
||||
|
||||
response = api_client.post(
|
||||
reverse("api:user:token_auth"),
|
||||
{"email": "test@test.nl", "password": "password"},
|
||||
format="json",
|
||||
)
|
||||
assert response.status_code == HTTP_200_OK
|
||||
assert "access_token" in response.json()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_admin_can_patch_user(api_client, premium_data_fixture):
|
||||
|
@ -626,6 +755,29 @@ def test_admin_can_patch_user_without_providing_password(
|
|||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
def test_admin_update_to_existing_user(api_client, premium_data_fixture):
|
||||
user_to_change = premium_data_fixture.create_user()
|
||||
user, token = premium_data_fixture.create_user_and_token(
|
||||
email="test@test.nl",
|
||||
password="password",
|
||||
first_name="Test1",
|
||||
is_staff=True,
|
||||
date_joined=datetime(2021, 4, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
|
||||
has_active_premium_license=True,
|
||||
)
|
||||
url = reverse("api:premium:admin:users:edit", kwargs={"user_id": user_to_change.id})
|
||||
response = api_client.patch(
|
||||
url,
|
||||
{"username": "test@test.nl"},
|
||||
format="json",
|
||||
HTTP_AUTHORIZATION=f"JWT {token}",
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert response.json()["error"] == "USER_ADMIN_ALREADY_EXISTS"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@override_settings(DEBUG=True)
|
||||
@pytest.mark.parametrize("invalid_password", invalid_passwords)
|
||||
|
|
|
@ -283,7 +283,9 @@
|
|||
"maxLocksPerTransactionExceededDescription": "Baserow attempted to permanently delete the trashed items, but exceeded the available locks specified in `max_locks_per_transaction`.",
|
||||
"disabledPasswordProviderMessage": "Please use another authentication provider.",
|
||||
"lastAdminTitle": "Can't remove last workspace admin",
|
||||
"lastAdminMessage": "A workspace has to have at least one admin."
|
||||
"lastAdminMessage": "A workspace has to have at least one admin.",
|
||||
"adminAlreadyExistsTitle": "Can't use that username",
|
||||
"adminAlreadyExistsDescription": "That username can't be used because it already exists."
|
||||
},
|
||||
"importerType": {
|
||||
"csv": "Import a CSV file",
|
||||
|
|
|
@ -64,6 +64,10 @@ export class ClientErrorMap {
|
|||
app.i18n.t('clientHandler.adminCannotDeleteSelfTitle'),
|
||||
app.i18n.t('clientHandler.adminCannotDeleteSelfDescription')
|
||||
),
|
||||
USER_ADMIN_ALREADY_EXISTS: new ResponseErrorMessage(
|
||||
app.i18n.t('clientHandler.adminAlreadyExistsTitle'),
|
||||
app.i18n.t('clientHandler.adminAlreadyExistsDescription')
|
||||
),
|
||||
ERROR_MAX_FIELD_COUNT_EXCEEDED: new ResponseErrorMessage(
|
||||
app.i18n.t('clientHandler.maxFieldCountExceededTitle'),
|
||||
app.i18n.t('clientHandler.maxFieldCountExceededDescription')
|
||||
|
|
Loading…
Add table
Reference in a new issue