2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-04-14 07:18:44 +00:00

Merge commit from fork

* Ensure the MeUserSerializer correctly marks fields as read-only

* Bump API version

* Add unit tests for the "me" endpoint

* Additional unit tests

* Add OPTIONS test
This commit is contained in:
Oliver
2026-04-08 08:19:39 +10:00
committed by GitHub
parent 427a323914
commit 76b5cfcca2
3 changed files with 131 additions and 5 deletions

View File

@@ -1,11 +1,14 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 471
INVENTREE_API_VERSION = 472
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v472 -> 2026-04-01 : https://github.com/inventree/InvenTree/pull/xxxx
- Fixes writable fields on the user detail endpoint
v471 -> 2026-04-07 : https://github.com/inventree/InvenTree/pull/11685
- Adds data importer support for the "SalesOrderShipment" model

View File

@@ -393,6 +393,9 @@ class MeUserSerializer(ExtendedUserSerializer):
but ensures that certain fields are read-only.
"""
# Remove the 'group_ids' field, as this is not relevant for the 'me' endpoint
fields = [f for f in ExtendedUserSerializer.Meta.fields if f != 'group_ids']
read_only_fields = [
*ExtendedUserSerializer.Meta.read_only_fields,
'is_active',
@@ -402,6 +405,28 @@ class MeUserSerializer(ExtendedUserSerializer):
profile = UserProfileSerializer(many=False, read_only=True)
# Redefine the fields from ExtendedUserSerializer, to ensure they are marked as read-only
is_staff = serializers.BooleanField(
label=_('Staff'),
help_text=_('Does this user have staff permissions'),
required=False,
read_only=True,
)
is_superuser = serializers.BooleanField(
label=_('Superuser'),
help_text=_('Is this user a superuser'),
required=False,
read_only=True,
)
is_active = serializers.BooleanField(
label=_('Active'),
help_text=_('Is this user account active'),
required=False,
read_only=True,
)
def make_random_password(length=14):
"""Generate a random password of given length."""

View File

@@ -44,7 +44,7 @@ class UserAPITests(InvenTreeAPITestCase):
)
def test_api_url(self):
"""Test the 'api_url attribute in related API endpoints.
"""Test the 'api_url' attribute in related API endpoints.
Ref: https://github.com/inventree/InvenTree/pull/10182
"""
@@ -129,6 +129,19 @@ class UserAPITests(InvenTreeAPITestCase):
self.assertIn('Only a superuser can adjust this field', str(response.data))
# Try again, but with superuser access
self.user.is_superuser = True
self.user.save()
response = self.post(
url,
data={**data, 'username': 'Superuser', 'is_superuser': True},
expected_code=201,
)
self.assertEqual(response.data['username'], 'Superuser')
self.assertEqual(response.data['is_superuser'], True)
def test_user_detail(self):
"""Test the UserDetail API endpoint."""
user = User.objects.first()
@@ -143,7 +156,7 @@ class UserAPITests(InvenTreeAPITestCase):
self.get(url, expected_code=200)
# Let's try to update the user
data = {'is_active': False, 'is_staff': False}
data = {'is_active': True, 'is_staff': False}
self.patch(url, data=data, expected_code=403)
@@ -158,6 +171,26 @@ class UserAPITests(InvenTreeAPITestCase):
self.patch(url, data=data, expected_code=200)
# Try to change the "is_superuser" field - only a superuser can do this
data['is_superuser'] = True
response = self.patch(url, data=data, expected_code=403)
self.assertIn(
'You do not have permission to perform this action', str(response.data)
)
self.user.is_staff = True
self.user.is_superuser = True
self.user.save()
for val in [True, False]:
data['is_staff'] = True
data['is_superuser'] = val
response = self.patch(url, data=data, expected_code=200)
self.assertEqual(response.data['is_superuser'], val)
self.assertEqual(response.data['is_staff'], True)
# Try again, but logged out - expect no access to the endpoint
self.logout()
self.get(url, expected_code=401)
@@ -229,6 +262,71 @@ class UserAPITests(InvenTreeAPITestCase):
self.assertEqual(len(data['permissions']), len(perms) + len(build_perms))
def test_me_endpoint(self):
"""Test against the users /me/ endpoint."""
url = reverse('api-user-me')
# Test endpoint options
response = self.options(url, expected_code=200)
# Check that particular fields are present, and have the correct attributes
fields = response.data['actions']['PUT']
for name in [
'pk',
'username',
'email',
'groups',
'is_active',
'is_staff',
'is_superuser',
]:
self.assertIn(name, fields)
for name in ['is_active', 'is_staff', 'is_superuser']:
self.assertTrue(fields[name]['read_only'])
# Perform a GET request against the endpoint
response = self.get(url, expected_code=200)
for field in [
'pk',
'username',
'email',
'is_active',
'is_staff',
'is_superuser',
]:
self.assertIn(field, response.data)
# Change their own username
for name in ['Henry', 'Sally']:
response = self.patch(url, data={'username': name}, expected_code=200)
self.assertEqual(response.data['username'], name)
# Defined starting point for the user
for v in [True, False]:
self.user.is_staff = v
self.user.is_superuser = v
self.user.save()
for key in ['is_staff', 'is_superuser']:
for val in [True, False]:
response = self.patch(url, data={key: val}, expected_code=200)
# Check that the field was *NOT CHANGED*
self.assertEqual(response.data[key], v)
# Ensure we cannot change the "is_active" field either
response = self.patch(url, data={'is_active': False}, expected_code=200)
self.assertEqual(response.data['is_active'], True)
self.user.is_active = False
self.user.save()
# User cannot fetch their own details if they are not active
response = self.get(url, expected_code=401)
class SuperuserAPITests(InvenTreeAPITestCase):
"""Tests for user API endpoints that require superuser rights."""
@@ -245,7 +343,7 @@ class SuperuserAPITests(InvenTreeAPITestCase):
resp = self.put(url, {'password': 1}, expected_code=400)
self.assertContains(resp, 'This password is too short', status_code=400)
# now with overwerite
# now with overwrite
resp = self.put(
url, {'password': 1, 'override_warning': True}, expected_code=200
)
@@ -422,7 +520,7 @@ class UserTokenTests(InvenTreeAPITestCase):
self.assertEqual(ApiToken.objects.count(), 1)
class GroupDetialTests(InvenTreeAPITestCase):
class GroupDetailTests(InvenTreeAPITestCase):
"""Tests for the GroupDetail API endpoint."""
fixtures = ['users']