mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-07 10:58:25 +02:00
Allow providing REST API authentication token with Bearer keyword (#995)
This commit is contained in:
34
bookmarks/api/auth.py
Normal file
34
bookmarks/api/auth.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import exceptions
|
||||
from rest_framework.authentication import TokenAuthentication, get_authorization_header
|
||||
|
||||
|
||||
class LinkdingTokenAuthentication(TokenAuthentication):
|
||||
"""
|
||||
Extends DRF TokenAuthentication to add support for multiple keywords
|
||||
"""
|
||||
|
||||
keywords = [keyword.lower().encode() for keyword in ["Token", "Bearer"]]
|
||||
|
||||
def authenticate(self, request):
|
||||
auth = get_authorization_header(request).split()
|
||||
|
||||
if not auth or auth[0].lower() not in self.keywords:
|
||||
return None
|
||||
|
||||
if len(auth) == 1:
|
||||
msg = _("Invalid token header. No credentials provided.")
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
elif len(auth) > 2:
|
||||
msg = _("Invalid token header. Token string should not contain spaces.")
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
try:
|
||||
token = auth[1].decode()
|
||||
except UnicodeError:
|
||||
msg = _(
|
||||
"Invalid token header. Token string should not contain invalid characters."
|
||||
)
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
return self.authenticate_credentials(token)
|
32
bookmarks/tests/test_auth_api.py
Normal file
32
bookmarks/tests/test_auth_api.py
Normal file
@@ -0,0 +1,32 @@
|
||||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.authtoken.models import Token
|
||||
|
||||
from bookmarks.tests.helpers import LinkdingApiTestCase, BookmarkFactoryMixin
|
||||
|
||||
|
||||
class AuthApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||
|
||||
def authenticate(self, keyword):
|
||||
self.api_token = Token.objects.get_or_create(
|
||||
user=self.get_or_create_test_user()
|
||||
)[0]
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f"{keyword} {self.api_token.key}")
|
||||
|
||||
def test_auth_with_token_keyword(self):
|
||||
self.authenticate("Token")
|
||||
|
||||
url = reverse("bookmarks:user-profile")
|
||||
self.get(url, expected_status_code=status.HTTP_200_OK)
|
||||
|
||||
def test_auth_with_bearer_keyword(self):
|
||||
self.authenticate("Bearer")
|
||||
|
||||
url = reverse("bookmarks:user-profile")
|
||||
self.get(url, expected_status_code=status.HTTP_200_OK)
|
||||
|
||||
def test_auth_with_unknown_keyword(self):
|
||||
self.authenticate("Key")
|
||||
|
||||
url = reverse("bookmarks:user-profile")
|
||||
self.get(url, expected_status_code=status.HTTP_401_UNAUTHORIZED)
|
@@ -131,7 +131,7 @@ STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||
# REST framework
|
||||
REST_FRAMEWORK = {
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||
"rest_framework.authentication.TokenAuthentication",
|
||||
"bookmarks.api.auth.LinkdingTokenAuthentication",
|
||||
"rest_framework.authentication.SessionAuthentication",
|
||||
],
|
||||
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
|
||||
|
Reference in New Issue
Block a user