Skip to content

Commit

Permalink
Add model for JWT refresh token
Browse files Browse the repository at this point in the history
  • Loading branch information
stveit committed Feb 4, 2025
1 parent dbf3893 commit 49c7894
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog.d/3268.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add database model for JWT refresh tokens
23 changes: 23 additions & 0 deletions python/nav/models/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from django.contrib.postgres.fields import HStoreField
from django.db import models
from django.urls import reverse
from django.utils import timezone

from nav.models.fields import VarcharField
from nav.models.profiles import Account
Expand Down Expand Up @@ -66,3 +67,25 @@ def get_absolute_url(self):

class Meta(object):
db_table = 'apitoken'


class JWTRefreshToken(models.Model):

name = VarcharField(unique=True)
description = models.TextField(null=True, blank=True)
expires = models.DateTimeField()
activates = models.DateTimeField()
hash = VarcharField()

def __str__(self):
return self.name

def is_active(self) -> bool:
"""True if token is active. A token is considered active when
`activates` is in the past and `expires` is in the future.
"""
now = timezone.now()
return now >= self.activates and now < self.expires

class Meta(object):
db_table = 'jwtrefreshtoken'
8 changes: 8 additions & 0 deletions python/nav/models/sql/changes/sc.05.13.0001.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE manage.JWTRefreshToken (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL UNIQUE,
description VARCHAR,
expires TIMESTAMP NOT NULL,
activates TIMESTAMP NOT NULL,
hash VARCHAR NOT NULL
);
58 changes: 58 additions & 0 deletions tests/unittests/models/jwtrefreshtoken_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from datetime import datetime, timedelta, timezone

from nav.models.api import JWTRefreshToken


class TestIsActive:
def test_should_return_false_if_token_activates_in_the_future(self):
now = datetime.now(tz=timezone.utc)
token = JWTRefreshToken(
name="testtoken",
hash="dummyhash",
expires=now + timedelta(hours=1),
activates=now + timedelta(hours=1),
)
assert not token.is_active()

def test_should_return_false_if_token_expires_in_the_past(self):
now = datetime.now(tz=timezone.utc)
token = JWTRefreshToken(
name="testtoken",
hash="dummyhash",
expires=now - timedelta(hours=1),
activates=now - timedelta(hours=1),
)
assert not token.is_active()

def test_should_return_true_if_token_activates_in_the_past_and_expires_in_the_future(
self,
):
now = datetime.now(tz=timezone.utc)
token = JWTRefreshToken(
name="testtoken",
hash="dummyhash",
expires=now + timedelta(hours=1),
activates=now - timedelta(hours=1),
)
assert token.is_active()

def test_should_return_true_if_token_activates_now_and_expires_in_the_future(self):
now = datetime.now(tz=timezone.utc)
token = JWTRefreshToken(
name="testtoken",
hash="dummyhash",
expires=now + timedelta(hours=1),
activates=now,
)
assert token.is_active()


def test_string_representation_should_match_name():
now = datetime.now(tz=timezone.utc)
token = JWTRefreshToken(
name="testtoken",
hash="dummyhash",
expires=now + timedelta(hours=1),
activates=now - timedelta(hours=1),
)
assert str(token) == token.name

0 comments on commit 49c7894

Please sign in to comment.