This commit is contained in:
parent
b1412514f1
commit
0ee59d7371
|
|
@ -57,6 +57,24 @@ class TokenSerializer(ValidatedModelSerializer):
|
|||
|
||||
return super().validate(data)
|
||||
|
||||
def create(self, validated_data):
|
||||
instance = super().create(validated_data)
|
||||
# The plaintext token is only available in memory after save(); v2 tokens persist only an
|
||||
# HMAC digest, so it can't be recovered later. Stash it on the request so to_representation()
|
||||
# can return it even after the viewset re-fetches the instance from the database.
|
||||
if request := self.context.get('request'):
|
||||
if not hasattr(request, '_token_plaintexts'):
|
||||
request._token_plaintexts = {}
|
||||
request._token_plaintexts[instance.pk] = instance.token
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
if not data.get('token') and (request := self.context.get('request')):
|
||||
if plaintext := getattr(request, '_token_plaintexts', {}).get(instance.pk):
|
||||
data['token'] = plaintext
|
||||
return data
|
||||
|
||||
|
||||
class TokenProvisionSerializer(TokenSerializer):
|
||||
user = UserSerializer(
|
||||
|
|
|
|||
|
|
@ -310,6 +310,53 @@ class TokenTestCase(
|
|||
response = self.client.post(url, data, format='json', **self.header)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
|
||||
def test_create_token_returns_plaintext(self):
|
||||
"""
|
||||
Creating a Token via the REST API must return the usable plaintext value in the response.
|
||||
For v2 tokens this value cannot be recovered later because the database stores only an
|
||||
HMAC digest.
|
||||
"""
|
||||
self.add_permissions('users.add_token')
|
||||
user = User.objects.create_user(username='token_plaintext_user')
|
||||
url = reverse('users-api:token-list')
|
||||
|
||||
response = self.client.post(url, {'user': user.pk}, format='json', **self.header)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertIsNotNone(response.data['token'])
|
||||
self.assertEqual(len(response.data['token']), TOKEN_DEFAULT_LENGTH)
|
||||
|
||||
# The returned plaintext must authenticate against the stored token
|
||||
token = Token.objects.get(pk=response.data['id'])
|
||||
self.assertTrue(token.validate(response.data['token']))
|
||||
|
||||
def test_bulk_create_tokens_returns_plaintexts(self):
|
||||
"""
|
||||
Bulk-creating Tokens via the REST API must return the plaintext value for each created
|
||||
Token in the response.
|
||||
"""
|
||||
self.add_permissions('users.add_token')
|
||||
users = [
|
||||
User.objects.create_user(username='token_bulk_user1'),
|
||||
User.objects.create_user(username='token_bulk_user2'),
|
||||
]
|
||||
data = [{'user': u.pk} for u in users]
|
||||
url = reverse('users-api:token-list')
|
||||
|
||||
response = self.client.post(url, data, format='json', **self.header)
|
||||
self.assertEqual(response.status_code, 201)
|
||||
self.assertEqual(len(response.data), len(data))
|
||||
|
||||
plaintexts = set()
|
||||
for obj in response.data:
|
||||
self.assertIsNotNone(obj['token'])
|
||||
self.assertEqual(len(obj['token']), TOKEN_DEFAULT_LENGTH)
|
||||
plaintexts.add(obj['token'])
|
||||
token = Token.objects.get(pk=obj['id'])
|
||||
self.assertTrue(token.validate(obj['token']))
|
||||
|
||||
# Each token should be unique
|
||||
self.assertEqual(len(plaintexts), len(data))
|
||||
|
||||
def test_reassign_token(self):
|
||||
"""
|
||||
Check that a Token cannot be reassigned to another User.
|
||||
|
|
|
|||
Loading…
Reference in New Issue