Skip to content

Commit

Permalink
Add a REST API endpoint to provision new tokens using username & pass…
Browse files Browse the repository at this point in the history
…word
  • Loading branch information
jeremystretch committed Jun 11, 2021
1 parent 48b4bf1 commit b038b1f
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 1 deletion.
5 changes: 5 additions & 0 deletions netbox/users/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ def to_internal_value(self, data):
return super().to_internal_value(data)


class TokenProvisionSerializer(serializers.Serializer):
username = serializers.CharField()
password = serializers.CharField()


class ObjectPermissionSerializer(ValidatedModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='users-api:objectpermission-detail')
object_types = ContentTypeField(
Expand Down
7 changes: 6 additions & 1 deletion netbox/users/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from django.urls import include, path

from netbox.api import OrderedDefaultRouter
from . import views

Expand All @@ -19,4 +21,7 @@
router.register('config', views.UserConfigViewSet, basename='userconfig')

app_name = 'users-api'
urlpatterns = router.urls
urlpatterns = [
path('tokens/provision/', views.TokenProvisionView.as_view(), name='token_provision'),
path('', include(router.urls)),
]
32 changes: 32 additions & 0 deletions netbox/users/api/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from django.contrib.auth import authenticate
from django.contrib.auth.models import Group, User
from django.db.models import Count
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
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 netbox.api.views import ModelViewSet
Expand Down Expand Up @@ -56,6 +60,34 @@ def get_queryset(self):
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
#
Expand Down
31 changes: 31 additions & 0 deletions netbox/users/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,37 @@ def setUp(self):
},
]

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):
model = ObjectPermission
Expand Down

0 comments on commit b038b1f

Please sign in to comment.