Skip to content

Commit b1fd8f7

Browse files
Joshua-AndersonJoshua Anderson
authored andcommitted
feat(controller): Add token regeneration endpoint
1 parent 3821218 commit b1fd8f7

5 files changed

Lines changed: 82 additions & 1 deletion

File tree

api/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
The **api** Django app presents a RESTful web API for interacting with the **deis** system.
33
"""
44

5-
__version__ = '1.4.0'
5+
__version__ = '1.5.0'

api/permissions.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,18 @@ def has_permission(self, request, view):
129129
if not auth_header:
130130
return False
131131
return auth_header == settings.BUILDER_KEY
132+
133+
134+
class CanRegenerateToken(permissions.BasePermission):
135+
"""
136+
Checks if a user can regenerate a token
137+
"""
138+
139+
def has_permission(self, request, view):
140+
"""
141+
Return `True` if permission is granted, `False` otherwise.
142+
"""
143+
if 'username' in request.data or 'all' in request.data:
144+
return request.user.is_superuser
145+
else:
146+
return True

api/tests/test_auth.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,3 +278,33 @@ def test_change_user_passwd(self):
278278
response = self.client.post(url, json.dumps(submit), content_type='application/json',
279279
HTTP_AUTHORIZATION='token {}'.format(self.user1_token))
280280
self.assertEqual(response.status_code, 200)
281+
282+
def test_regenerate(self):
283+
""" Test that token regeneration works"""
284+
285+
url = '/v1/auth/tokens/'
286+
287+
response = self.client.post(url, '{}', content_type='application/json',
288+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
289+
290+
self.assertEqual(response.status_code, 200)
291+
self.assertNotEqual(response.data['token'], self.admin_token)
292+
293+
self.admin_token = Token.objects.get(user=self.admin)
294+
295+
response = self.client.post(url, '{"username" : "autotest2"}',
296+
content_type='application/json',
297+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
298+
299+
self.assertEqual(response.status_code, 200)
300+
self.assertNotEqual(response.data['token'], self.user1_token)
301+
302+
response = self.client.post(url, '{"all" : "true"}',
303+
content_type='application/json',
304+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
305+
self.assertEqual(response.status_code, 200)
306+
307+
response = self.client.post(url, '{}', content_type='application/json',
308+
HTTP_AUTHORIZATION='token {}'.format(self.admin_token))
309+
310+
self.assertEqual(response.status_code, 401)

api/urls.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
views.UserManagementViewSet.as_view({'post': 'passwd'})),
8888
url(r'^auth/login/',
8989
'rest_framework.authtoken.views.obtain_auth_token'),
90+
url(r'^auth/tokens/',
91+
views.TokenManagementViewSet.as_view({'post': 'regenerate'})),
9092
# admin sharing
9193
url(r'^admin/perms/(?P<username>[-_\w]+)/?',
9294
views.AdminPermsViewSet.as_view({'delete': 'destroy'})),

api/views.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from rest_framework.permissions import IsAuthenticated
1414
from rest_framework.response import Response
1515
from rest_framework.viewsets import GenericViewSet
16+
from rest_framework.authtoken.models import Token
1617

1718
from api import authentication, models, permissions, serializers, viewsets
1819

@@ -51,6 +52,39 @@ def passwd(self, request, **kwargs):
5152
return Response({'status': 'password set'})
5253

5354

55+
class TokenManagementViewSet(GenericViewSet,
56+
mixins.DestroyModelMixin):
57+
serializer_class = serializers.UserSerializer
58+
permission_classes = [permissions.CanRegenerateToken]
59+
60+
def get_queryset(self):
61+
return User.objects.filter(pk=self.request.user.pk)
62+
63+
def get_object(self):
64+
return self.get_queryset()[0]
65+
66+
def regenerate(self, request, **kwargs):
67+
obj = self.get_object()
68+
69+
if 'all' in request.data:
70+
for user in User.objects.all():
71+
if not user.is_anonymous():
72+
token = Token.objects.get(user=user)
73+
token.delete()
74+
Token.objects.create(user=user)
75+
return Response("")
76+
77+
if 'username' in request.data:
78+
obj = get_object_or_404(User,
79+
username=request.data['username'])
80+
self.check_object_permissions(self.request, obj)
81+
82+
token = Token.objects.get(user=obj)
83+
token.delete()
84+
token = Token.objects.create(user=obj)
85+
return Response({'token': token.key})
86+
87+
5488
class BaseDeisViewSet(viewsets.OwnerViewSet):
5589
"""
5690
A generic ViewSet for objects related to Deis.

0 commit comments

Comments
 (0)