Skip to content

Commit

Permalink
chore(backend): force token expire (#969)
Browse files Browse the repository at this point in the history
* chore(token): token always expires

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* chore(migrations): alter bearer token expires at

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* chore(changelog): update changelog

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* chore(backend/users/migrations): alter bearertoken expires only if null

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* fix(users/migrations): chain AlterField migration operation

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* tests(views/test_views_token): add expiring date and test case

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* chore(users/migrations): field to models.DateTimeField()

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* chore(users/models/token): blank=False instead of default value

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

* tests(test_views_token): add test cases

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>

---------

Signed-off-by: Thibault Camalon <135698225+thbcmlowk@users.noreply.github.com>
  • Loading branch information
thbcmlowk committed Aug 29, 2024
1 parent 6dd0dea commit 7b1c1fa
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 7 deletions.
34 changes: 29 additions & 5 deletions backend/api/tests/views/test_views_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@

import pytest
from django.contrib.auth.models import User
from django.db.utils import IntegrityError
from django.utils import timezone
from rest_framework import status

from users.models.token import BearerToken


@pytest.mark.django_db
def test_cannot_create_non_expiring_token(authenticated_client):
authenticated_client.create_user()
user = authenticated_client.user
# create a token without expiration date
with pytest.raises(IntegrityError):
BearerToken.objects.create(user=user)


@pytest.mark.django_db
def test_delete_token(authenticated_client):
authenticated_client.create_user()
user = authenticated_client.user
token = BearerToken.objects.create(user=user)
token = BearerToken.objects.create(user=user, expires_at=timezone.now() + timedelta(days=1))

tokens_count = BearerToken.objects.count()
assert tokens_count == 1
Expand All @@ -29,8 +39,8 @@ def test_delete_token(authenticated_client):
def test_multiple_token(authenticated_client, api_client):
authenticated_client.create_user()
user = authenticated_client.user
token_1 = BearerToken.objects.create(user=user)
token_2 = BearerToken.objects.create(user=user)
token_1 = BearerToken.objects.create(user=user, expires_at=timezone.now() + timedelta(days=1))
token_2 = BearerToken.objects.create(user=user, expires_at=timezone.now() + timedelta(days=2))

tokens_count = BearerToken.objects.count()
assert tokens_count == 2
Expand Down Expand Up @@ -60,7 +70,7 @@ def test_multiple_token(authenticated_client, api_client):


@pytest.mark.django_db
def test_expiring_token(authenticated_client, api_client):
def test_expired_token(authenticated_client, api_client):
authenticated_client.create_user()
user = authenticated_client.user
# create a token that expired a day ago
Expand Down Expand Up @@ -88,7 +98,7 @@ def test_delete_token_other_user(authenticated_client):
other_user = User.objects.create(username="user-2")
other_user.set_password("p@sswr0d44")
other_user.save()
token = BearerToken.objects.create(user=other_user)
token = BearerToken.objects.create(user=other_user, expires_at=timezone.now() + timedelta(days=1))

tokens_count = BearerToken.objects.count()
assert tokens_count == 1
Expand All @@ -111,3 +121,17 @@ def test_token_creation_post(authenticated_client):

tokens_count = BearerToken.objects.count()
assert tokens_count == 1


@pytest.mark.django_db
def test_cannot_post_token_wo_expires_at(authenticated_client):
authenticated_client.create_user()
payload = {}
url = "/api-token/"
response = authenticated_client.post(url, payload)

assert response.json() == {"expires_at": ["This field is required."]}
assert response.status_code == status.HTTP_400_BAD_REQUEST

tokens_count = BearerToken.objects.count()
assert tokens_count == 0
25 changes: 25 additions & 0 deletions backend/users/migrations/0008_alter_bearertoken_expires_at.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 4.2.5 on 2024-08-14 15:28

import django.utils.timezone
from django.db import migrations
from django.db import models


def set_default_expires_at(apps, schema_editor):
BearerToken = apps.get_model("users", "BearerToken") # noqa
BearerToken.objects.filter(expires_at__isnull=True).update(expires_at=django.utils.timezone.now())


class Migration(migrations.Migration):
dependencies = [
("users", "0007_implicitbearertoken_id_and_more"),
]

operations = [
migrations.RunPython(set_default_expires_at),
migrations.AlterField(
model_name="bearertoken",
name="expires_at",
field=models.DateTimeField(),
),
]
2 changes: 1 addition & 1 deletion backend/users/models/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class BearerToken(Token):
note = models.TextField(null=True)
expires_at = models.DateTimeField(null=True)
expires_at = models.DateTimeField(null=False, blank=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="bearer_tokens", on_delete=models.CASCADE)
id = models.UUIDField(default=uuid.uuid4, editable=False)

Expand Down
2 changes: 1 addition & 1 deletion backend/users/serializers/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


class BearerTokenSerializer(serializers.ModelSerializer):
expires_at = serializers.DateTimeField(default_timezone=timezone.utc, allow_null=True)
expires_at = serializers.DateTimeField(default_timezone=timezone.utc, allow_null=False)
created_at = serializers.DateTimeField(default_timezone=timezone.utc, source="created", read_only=True)
token = serializers.CharField(source="key", read_only=True)

Expand Down
1 change: 1 addition & 0 deletions changes/969.changed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Disable never expiring users `BearerToken`

0 comments on commit 7b1c1fa

Please sign in to comment.