mirror of
https://github.com/netbox-community/netbox.git
synced 2024-05-10 07:54:54 +00:00
Add a REST API endpoint to provision new tokens using username & password
This commit is contained in:
@ -70,6 +70,11 @@ class TokenSerializer(ValidatedModelSerializer):
|
|||||||
return super().to_internal_value(data)
|
return super().to_internal_value(data)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenProvisionSerializer(serializers.Serializer):
|
||||||
|
username = serializers.CharField()
|
||||||
|
password = serializers.CharField()
|
||||||
|
|
||||||
|
|
||||||
class ObjectPermissionSerializer(ValidatedModelSerializer):
|
class ObjectPermissionSerializer(ValidatedModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(view_name='users-api:objectpermission-detail')
|
url = serializers.HyperlinkedIdentityField(view_name='users-api:objectpermission-detail')
|
||||||
object_types = ContentTypeField(
|
object_types = ContentTypeField(
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
from netbox.api import OrderedDefaultRouter
|
from netbox.api import OrderedDefaultRouter
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
@ -19,4 +21,7 @@ router.register('permissions', views.ObjectPermissionViewSet)
|
|||||||
router.register('config', views.UserConfigViewSet, basename='userconfig')
|
router.register('config', views.UserConfigViewSet, basename='userconfig')
|
||||||
|
|
||||||
app_name = 'users-api'
|
app_name = 'users-api'
|
||||||
urlpatterns = router.urls
|
urlpatterns = [
|
||||||
|
path('tokens/provision/', views.TokenProvisionView.as_view(), name='token_provision'),
|
||||||
|
path('', include(router.urls)),
|
||||||
|
]
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
|
from django.contrib.auth import authenticate
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
from rest_framework.exceptions import AuthenticationFailed
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
|
from rest_framework.status import HTTP_201_CREATED
|
||||||
|
from rest_framework.views import APIView
|
||||||
from rest_framework.viewsets import ViewSet
|
from rest_framework.viewsets import ViewSet
|
||||||
|
|
||||||
from netbox.api.views import ModelViewSet
|
from netbox.api.views import ModelViewSet
|
||||||
@ -56,6 +60,34 @@ class TokenViewSet(ModelViewSet):
|
|||||||
return queryset.filter(user=self.request.user)
|
return queryset.filter(user=self.request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenProvisionView(APIView):
|
||||||
|
"""
|
||||||
|
Non-authenticated REST API endpoint via which a user may create a Token.
|
||||||
|
"""
|
||||||
|
permission_classes = []
|
||||||
|
swagger_schema = None # TODO: Generate a schema
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
serializer = serializers.TokenProvisionSerializer(data=request.data)
|
||||||
|
serializer.is_valid()
|
||||||
|
|
||||||
|
# Authenticate the user account based on the provided credentials
|
||||||
|
user = authenticate(
|
||||||
|
request=request,
|
||||||
|
username=serializer.data['username'],
|
||||||
|
password=serializer.data['password']
|
||||||
|
)
|
||||||
|
if user is None:
|
||||||
|
raise AuthenticationFailed("Invalid username/password")
|
||||||
|
|
||||||
|
# Create a new Token for the User
|
||||||
|
token = Token(user=user)
|
||||||
|
token.save()
|
||||||
|
data = serializers.TokenSerializer(token, context={'request': request}).data
|
||||||
|
|
||||||
|
return Response(data, status=HTTP_201_CREATED)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# ObjectPermissions
|
# ObjectPermissions
|
||||||
#
|
#
|
||||||
|
@ -106,6 +106,37 @@ class TokenTest(APIViewTestCases.APIViewTestCase):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def test_provision_token_valid(self):
|
||||||
|
"""
|
||||||
|
Test the provisioning of a new REST API token given a valid username and password.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'username': 'user1',
|
||||||
|
'password': 'abc123',
|
||||||
|
}
|
||||||
|
user = User.objects.create_user(**data)
|
||||||
|
url = reverse('users-api:token_provision')
|
||||||
|
|
||||||
|
response = self.client.post(url, **self.header, data=data)
|
||||||
|
self.assertEqual(response.status_code, 201)
|
||||||
|
self.assertIn('key', response.data)
|
||||||
|
self.assertEqual(len(response.data['key']), 40)
|
||||||
|
token = Token.objects.get(user=user)
|
||||||
|
self.assertEqual(token.key, response.data['key'])
|
||||||
|
|
||||||
|
def test_provision_token_invalid(self):
|
||||||
|
"""
|
||||||
|
Test the behavior of the token provisioning view when invalid credentials are supplied.
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'username': 'nonexistentuser',
|
||||||
|
'password': 'abc123',
|
||||||
|
}
|
||||||
|
url = reverse('users-api:token_provision')
|
||||||
|
|
||||||
|
response = self.client.post(url, **self.header, data=data)
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
|
||||||
class ObjectPermissionTest(APIViewTestCases.APIViewTestCase):
|
class ObjectPermissionTest(APIViewTestCases.APIViewTestCase):
|
||||||
model = ObjectPermission
|
model = ObjectPermission
|
||||||
|
Reference in New Issue
Block a user