Skip to content

Commit fd974c5

Browse files
committed
feat: Add framework for token based authentication
1 parent 84705e3 commit fd974c5

File tree

3 files changed

+52
-0
lines changed

3 files changed

+52
-0
lines changed

purple/permissions.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright The IETF Trust 2025, All Rights Reserved
2+
3+
from rest_framework import permissions
4+
5+
from purple.utils import is_valid_token
6+
7+
8+
class HasApiKey(permissions.BasePermission):
9+
"""Permissions class that validates a token using is_valid_token
10+
11+
The view class must indicate the relevant endpoint by setting `api_key_endpoint`.
12+
Must be used with an Authentication class that puts a token in request.auth.
13+
"""
14+
15+
def has_permission(self, request, view):
16+
endpoint = getattr(view, "api_key_endpoint", None)
17+
auth_token = getattr(request, "auth", None)
18+
if endpoint is not None and auth_token is not None:
19+
return is_valid_token(endpoint, auth_token)
20+
return False

purple/tests_utils.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright The IETF Trust 2025, All Rights Reserve
2+
3+
from django.test import TestCase
4+
from django.test.utils import override_settings
5+
6+
from purple.utils import is_valid_token
7+
8+
9+
class UtilsTests(TestCase):
10+
@override_settings(APP_API_TOKENS={"purple.api.foobar": ["valid-token"]})
11+
def test_is_valid_token(self):
12+
self.assertFalse(is_valid_token("purple.fake.endpoint", "valid-token"))
13+
self.assertFalse(is_valid_token("purple.api.foobar", "invalid-token"))
14+
self.assertTrue(is_valid_token("purple.api.foobar", "valid-token"))

purple/utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright The IETF Trust 2025, All Rights Reserved
2+
3+
from django.conf import settings
4+
5+
6+
def is_valid_token(endpoint, token):
7+
# This is where we would consider integration with vault
8+
# Settings implementation for now.
9+
if hasattr(settings, "APP_API_TOKENS"):
10+
token_store = settings.APP_API_TOKENS
11+
if endpoint in token_store:
12+
endpoint_tokens = token_store[endpoint]
13+
# Be sure endpoints is a list or tuple so we don't accidentally use substring matching!
14+
if not isinstance(endpoint_tokens, (list, tuple)):
15+
endpoint_tokens = [endpoint_tokens]
16+
if token in endpoint_tokens:
17+
return True
18+
return False

0 commit comments

Comments
 (0)