mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-08 11:18:28 +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
|
||||||
REST_FRAMEWORK = {
|
REST_FRAMEWORK = {
|
||||||
"DEFAULT_AUTHENTICATION_CLASSES": [
|
"DEFAULT_AUTHENTICATION_CLASSES": [
|
||||||
"rest_framework.authentication.TokenAuthentication",
|
"bookmarks.api.auth.LinkdingTokenAuthentication",
|
||||||
"rest_framework.authentication.SessionAuthentication",
|
"rest_framework.authentication.SessionAuthentication",
|
||||||
],
|
],
|
||||||
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
|
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
|
||||||
|
Reference in New Issue
Block a user