Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/api-guide/serializers.md
Original file line number Diff line number Diff line change
Expand Up @@ -1189,6 +1189,10 @@ The [drf-writable-nested][drf-writable-nested] package provides writable nested

The [drf-encrypt-content][drf-encrypt-content] package helps you encrypt your data, serialized through ModelSerializer. It also contains some helper functions. Which helps you to encrypt your data.

## Shapeless Serializers

The [drf-shapeless-serializers][drf-shapeless-serializers] package provides dynamic serializer configuration capabilities, allowing runtime field selection, renaming, attribute modification, and nested relationship configuration without creating multiple serializer classes. It helps eliminate serializer boilerplate while providing flexible API responses.


[cite]: https://groups.google.com/d/topic/django-users/sVFaOfQi4wY/discussion
[relations]: relations.md
Expand All @@ -1212,3 +1216,4 @@ The [drf-encrypt-content][drf-encrypt-content] package helps you encrypt your da
[djangorestframework-queryfields]: https://djangorestframework-queryfields.readthedocs.io/
[drf-writable-nested]: https://github.com/beda-software/drf-writable-nested
[drf-encrypt-content]: https://github.com/oguzhancelikarslan/drf-encrypt-content
[drf-shapeless-serializers]: https://github.com/khaledsukkar2/drf-shapeless-serializers
2 changes: 2 additions & 0 deletions docs/community/third-party-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ To submit new content, [create a pull request][drf-create-pr].
* [djangorestframework-dataclasses][djangorestframework-dataclasses] - Serializer providing automatic field generation for Python dataclasses, like the built-in ModelSerializer does for models.
* [django-restql][django-restql] - Turn your REST API into a GraphQL like API(It allows clients to control which fields will be sent in a response, uses GraphQL like syntax, supports read and write on both flat and nested fields).
* [graphwrap][graphwrap] - Transform your REST API into a fully compliant GraphQL API with just two lines of code. Leverages [Graphene-Django](https://docs.graphene-python.org/projects/django/en/latest/) to dynamically build, at runtime, a GraphQL ObjectType for each view in your API.
* [drf-shapeless-serializers][drf-shapeless-serializers] - Dynamically assemble, configure, and shape your Django Rest Framework serializers at runtime, much like connecting Lego bricks.

### Serializer fields

Expand Down Expand Up @@ -259,3 +260,4 @@ To submit new content, [create a pull request][drf-create-pr].
[drf-redesign]: https://github.com/youzarsiph/drf-redesign
[drf-material]: https://github.com/youzarsiph/drf-material
[django-pyoidc]: https://github.com/makinacorpus/django_pyoidc
[drf-shapeless-serializers]: https://github.com/khaledsukkar2/drf-shapeless-serializers
5 changes: 2 additions & 3 deletions rest_framework/authtoken/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import binascii
import os
import secrets

from django.conf import settings
from django.db import models
Expand Down Expand Up @@ -34,7 +33,7 @@ def save(self, *args, **kwargs):

@classmethod
def generate_key(cls):
return binascii.hexlify(os.urandom(20)).decode()
return secrets.token_hex(20)

def __str__(self):
return self.key
Expand Down
39 changes: 39 additions & 0 deletions tests/authentication/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ def put(self, request):
@override_settings(ROOT_URLCONF=__name__)
class BasicAuthTests(TestCase):
"""Basic authentication"""

def setUp(self):
self.csrf_client = APIClient(enforce_csrf_checks=True)
self.username = 'john'
Expand Down Expand Up @@ -198,6 +199,7 @@ def test_decoding_of_utf8_credentials(self):
@override_settings(ROOT_URLCONF=__name__)
class SessionAuthTests(TestCase):
"""User session authentication"""

def setUp(self):
self.csrf_client = APIClient(enforce_csrf_checks=True)
self.non_csrf_client = APIClient(enforce_csrf_checks=False)
Expand Down Expand Up @@ -418,6 +420,41 @@ def test_generate_key_accessible_as_classmethod(self):
key = self.model.generate_key()
assert isinstance(key, str)

def test_generate_key_returns_valid_format(self):
"""Ensure generate_key returns a valid token format"""
key = self.model.generate_key()
assert len(key) == 40
# Should contain only valid hexadecimal characters
assert all(c in '0123456789abcdef' for c in key)

def test_generate_key_produces_unique_values(self):
"""Ensure generate_key produces unique values across multiple calls"""
keys = set()
for _ in range(100):
key = self.model.generate_key()
assert key not in keys, f"Duplicate key generated: {key}"
keys.add(key)

def test_generate_key_collision_resistance(self):
"""Test collision resistance with reasonable sample size"""
keys = set()
for _ in range(500):
key = self.model.generate_key()
assert key not in keys, f"Collision found: {key}"
keys.add(key)
assert len(keys) == 500, f"Expected 500 unique keys, got {len(keys)}"

def test_generate_key_randomness_quality(self):
"""Test basic randomness properties of generated keys"""
keys = [self.model.generate_key() for _ in range(10)]
# Consecutive keys should be different
for i in range(len(keys) - 1):
assert keys[i] != keys[i + 1], "Consecutive keys should be different"
# Keys should not follow obvious patterns
for key in keys:
# Should not be all same character
assert not all(c == key[0] for c in key), f"Key has all same characters: {key}"

def test_token_login_json(self):
"""Ensure token login view using JSON POST works."""
client = APIClient(enforce_csrf_checks=True)
Expand Down Expand Up @@ -480,6 +517,7 @@ def test_incorrect_credentials(self):
authentication should run and error, even if no permissions
are set on the view.
"""

class IncorrectCredentialsAuth(BaseAuthentication):
def authenticate(self, request):
raise exceptions.AuthenticationFailed('Bad credentials')
Expand Down Expand Up @@ -571,6 +609,7 @@ def test_basic_authentication_raises_error_if_user_not_active(self):

class MockUser:
is_active = False

old_authenticate = authentication.authenticate
authentication.authenticate = lambda **kwargs: MockUser()
try:
Expand Down