Skip to content

Commit b7cc4c4

Browse files
Fixes #20476: Prohibit changing a token's owner (#20576)
1 parent 37a9d03 commit b7cc4c4

File tree

3 files changed

+42
-3
lines changed

3 files changed

+42
-3
lines changed

netbox/users/api/serializers_/tokens.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ class Meta:
3737
read_only_fields = ('key',)
3838
brief_fields = ('id', 'url', 'display', 'version', 'key', 'write_enabled', 'description')
3939

40+
def get_fields(self):
41+
fields = super().get_fields()
42+
43+
# Make user field read-only if updating an existing Token.
44+
if self.instance is not None:
45+
fields['user'].read_only = True
46+
47+
return fields
48+
4049
def validate(self, data):
4150

4251
# If the Token is being created on behalf of another user, enforce the grant_token permission.

netbox/users/forms/model_forms.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,13 @@ class Meta(UserTokenForm.Meta):
177177
'version', 'token', 'user', 'write_enabled', 'expires', 'description', 'allowed_ips',
178178
]
179179

180+
def __init__(self, *args, **kwargs):
181+
super().__init__(*args, **kwargs)
182+
183+
# If not creating a new Token, disable the user field
184+
if self.instance and not self.instance._state.adding:
185+
self.fields['user'].disabled = True
186+
180187

181188
class UserForm(forms.ModelForm):
182189
password = forms.CharField(

netbox/users/tests/test_api.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,9 @@ def setUp(self):
212212
@classmethod
213213
def setUpTestData(cls):
214214
users = (
215-
create_test_user('User1'),
216-
create_test_user('User2'),
217-
create_test_user('User3'),
215+
create_test_user('User 1'),
216+
create_test_user('User 2'),
217+
create_test_user('User 3'),
218218
)
219219

220220
tokens = (
@@ -238,6 +238,10 @@ def setUpTestData(cls):
238238
},
239239
]
240240

241+
cls.update_data = {
242+
'description': 'Token 1',
243+
}
244+
241245
def test_provision_token_valid(self):
242246
"""
243247
Test the provisioning of a new REST API token given a valid username and password.
@@ -300,6 +304,25 @@ def test_provision_token_other_user(self):
300304
response = self.client.post(url, data, format='json', **self.header)
301305
self.assertEqual(response.status_code, 201)
302306

307+
def test_reassign_token(self):
308+
"""
309+
Check that a Token cannot be reassigned to another User.
310+
"""
311+
user1 = User.objects.get(username='User 1')
312+
user2 = User.objects.get(username='User 2')
313+
token1 = Token.objects.filter(user=user1).first()
314+
self.add_permissions('users.change_token')
315+
316+
data = {
317+
'user': user2.pk,
318+
}
319+
url = self._get_detail_url(token1)
320+
response = self.client.patch(url, data, format='json', **self.header)
321+
# Response should succeed because the read-only `user` field is ignored
322+
self.assertEqual(response.status_code, 200)
323+
token1.refresh_from_db()
324+
self.assertEqual(token1.user, user1, "Token's user should not have changed")
325+
303326

304327
class ObjectPermissionTest(
305328
# No GraphQL support for ObjectPermission

0 commit comments

Comments
 (0)