mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
* 11091 add permission to allow user to create api tokens for other users * 11091 update docs * 11091 fix for test * 11091 fix for test * 11091 test case for invalid token creation * 11091 add test for permission grant * Cleanup & fix serializer validation --------- Co-authored-by: jeremystretch <jstretch@netboxlabs.com>
This commit is contained in:
@ -584,11 +584,16 @@ Additionally, a token can be set to expire at a specific time. This can be usefu
|
||||
|
||||
#### Client IP Restriction
|
||||
|
||||
!!! note
|
||||
This feature was introduced in NetBox v3.3.
|
||||
|
||||
Each API token can optionally be restricted by client IP address. If one or more allowed IP prefixes/addresses is defined for a token, authentication will fail for any client connecting from an IP address outside the defined range(s). This enables restricting the use a token to a specific client. (By default, any client IP address is permitted.)
|
||||
|
||||
#### Creating Tokens for Other Users
|
||||
|
||||
It is possible to provision authentication tokens for other users via the REST API. To do, so the requesting user must have the `users.grant_token` permission assigned. While all users have inherent permission to create their own tokens, this permission is required to enable the creation of tokens for other users.
|
||||
|
||||

|
||||
|
||||
!!! warning "Exercise Caution"
|
||||
The ability to create tokens on behalf of other users enables the requestor to access the created token. This ability is intended e.g. for the provisioning of tokens by automated services, and should be used with extreme caution to avoid a security compromise.
|
||||
|
||||
### Authenticating to the API
|
||||
|
||||
|
BIN
docs/media/admin_ui_grant_permission.png
Normal file
BIN
docs/media/admin_ui_grant_permission.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 33 KiB |
@ -2,6 +2,7 @@ from django.conf import settings
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from netbox.api.fields import ContentTypeField, IPNetworkSerializer, SerializedPKRelatedField
|
||||
from netbox.api.serializers import ValidatedModelSerializer
|
||||
@ -91,6 +92,16 @@ class TokenSerializer(ValidatedModelSerializer):
|
||||
data['key'] = Token.generate_key()
|
||||
return super().to_internal_value(data)
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
# If the Token is being created on behalf of another user, enforce the grant_token permission.
|
||||
request = self.context.get('request')
|
||||
token_user = data.get('user')
|
||||
if token_user and token_user != request.user and not request.user.has_perm('users.grant_token'):
|
||||
raise PermissionDenied("This user does not have permission to create tokens for other users.")
|
||||
|
||||
return super().validate(data)
|
||||
|
||||
|
||||
class TokenProvisionSerializer(serializers.Serializer):
|
||||
username = serializers.CharField()
|
||||
|
@ -153,6 +153,26 @@ class TokenTest(
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_provision_token_other_user(self):
|
||||
"""
|
||||
Test provisioning a Token for a different User with & without the grant_token permission.
|
||||
"""
|
||||
self.add_permissions('users.add_token')
|
||||
user2 = User.objects.create_user(username='testuser2')
|
||||
data = {
|
||||
'user': user2.id,
|
||||
}
|
||||
url = reverse('users-api:token-list')
|
||||
|
||||
# Attempt to create a new Token for User2 *without* the grant_token permission
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
# Assign grant_token permission and successfully create a new Token for User2
|
||||
self.add_permissions('users.grant_token')
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
|
||||
|
||||
class ObjectPermissionTest(
|
||||
# No GraphQL support for ObjectPermission
|
||||
|
Reference in New Issue
Block a user