Skip to content

Commit

Permalink
Remove deprecated API auth by GET parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
Guilouf committed Jan 10, 2025
1 parent 0dc2970 commit a64a402
Show file tree
Hide file tree
Showing 5 changed files with 4 additions and 121 deletions.
42 changes: 0 additions & 42 deletions lemarche/api/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,15 @@ class CustomBearerAuthentication(BaseAuthentication):
"""
Authentication via:
1. Authorization header: Bearer <token> (recommended).
2. URL parameter ?token=<token> (deprecated, temporary support).
"""

def authenticate(self, request):
token = None
warning_issued = False

# Priority to the Authorization header
auth_header = request.headers.get("Authorization")
if auth_header and auth_header.startswith("Bearer "):
token = auth_header.split("Bearer ")[1]
elif request.GET.get("token"): # Otherwise, try the URL parameter
token = request.GET.get("token")
warning_issued = True
logger.info("Authentication via URL token detected. This method is deprecated and less secure.")

# If no token is provided
if not token:
Expand All @@ -46,10 +40,6 @@ def authenticate(self, request):
except User.DoesNotExist:
raise AuthenticationFailed("Invalid or expired token")

# Add a warning in the response for URL tokens
if warning_issued:
request._deprecated_auth_warning = True # Marker for middleware or view

# Return the user and the token
return (user, token)

Expand All @@ -58,35 +48,3 @@ def authenticate_header(self, request):
Returns the expected header for 401 responses.
"""
return 'Bearer realm="api"'


class DeprecationWarningMiddleware:
"""
Middleware to inform users that authentication via URL `?token=` is deprecated.
This middleware checks if the request contains a deprecated authentication token
and adds a warning header to the response if it does.
Attributes:
get_response (callable): The next middleware or view in the chain.
Methods:
__call__(request):
Processes the request and adds a deprecation warning header to the response
if the request contains a deprecated authentication token.
"""

def __init__(self, get_response):
self.get_response = get_response

def __call__(self, request):
response = self.get_response(request)

# Add a warning if the marker is set in the request
if hasattr(request, "_deprecated_auth_warning") and request._deprecated_auth_warning:
response.headers["Deprecation-Warning"] = (
"URL token authentication is deprecated and will be removed on 2025/01. "
"Please use Authorization header with Bearer tokens."
)

return response
27 changes: 1 addition & 26 deletions lemarche/api/siaes/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.http import HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from drf_spectacular.utils import OpenApiParameter, extend_schema
from drf_spectacular.utils import extend_schema
from rest_framework import mixins, viewsets
from rest_framework.response import Response

Expand All @@ -23,11 +23,6 @@ class SiaeViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.Gen
@extend_schema(
summary="Lister toutes les structures",
tags=[Siae._meta.verbose_name_plural],
parameters=[
OpenApiParameter(
name="token", description="Token Utilisateur (pour compatibilité ancienne)", required=False, type=str
),
],
)
def list(self, request, format=None):
"""
Expand All @@ -49,11 +44,6 @@ def list(self, request, format=None):
@extend_schema(
summary="Détail d'une structure (par son id)",
tags=[Siae._meta.verbose_name_plural],
parameters=[
OpenApiParameter(
name="token", description="Token Utilisateur (pour compatibilité ancienne)", required=False, type=str
),
],
responses=SiaeDetailSerializer,
)
def retrieve(self, request, pk=None, format=None):
Expand All @@ -67,11 +57,6 @@ def retrieve(self, request, pk=None, format=None):
@extend_schema(
summary="Détail d'une structure (par son slug)",
tags=[Siae._meta.verbose_name_plural],
parameters=[
OpenApiParameter(
name="token", description="Token Utilisateur (pour compatibilité ancienne)", required=False, type=str
),
],
responses=SiaeDetailSerializer,
)
def retrieve_by_slug(self, request, slug=None, format=None):
Expand All @@ -86,11 +71,6 @@ def retrieve_by_slug(self, request, slug=None, format=None):
@extend_schema(
summary="Détail d'une structure (par son siren)",
tags=[Siae._meta.verbose_name_plural],
parameters=[
OpenApiParameter(
name="token", description="Token Utilisateur (pour compatibilité ancienne)", required=False, type=str
),
],
responses=SiaeDetailSerializer,
)
def retrieve_by_siren(self, request, siren=None, format=None):
Expand All @@ -106,11 +86,6 @@ def retrieve_by_siren(self, request, siren=None, format=None):
@extend_schema(
summary="Détail d'une structure (par son siret)",
tags=[Siae._meta.verbose_name_plural],
parameters=[
OpenApiParameter(
name="token", description="Token Utilisateur (pour compatibilité ancienne)", required=False, type=str
),
],
responses=SiaeDetailSerializer,
)
def retrieve_by_siret(self, request, siret=None, format=None):
Expand Down
5 changes: 1 addition & 4 deletions lemarche/api/tenders/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.conf import settings
from django.utils import timezone
from drf_spectacular.utils import OpenApiParameter, extend_schema
from drf_spectacular.utils import extend_schema
from rest_framework import mixins, viewsets
from rest_framework.permissions import IsAuthenticated

Expand All @@ -23,9 +23,6 @@ class TenderViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
@extend_schema(
summary="Déposer un besoin d'achat",
tags=[Tender._meta.verbose_name_plural],
parameters=[
OpenApiParameter(name="token", description="Token Utilisateur", required=True, type=str),
],
)
def create(self, request, *args, **kwargs):
return super().create(request, args, kwargs)
Expand Down
44 changes: 1 addition & 43 deletions lemarche/api/tests.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from django.http import HttpResponse
from django.test import RequestFactory, TestCase
from rest_framework.exceptions import AuthenticationFailed

from lemarche.api.authentication import CustomBearerAuthentication, DeprecationWarningMiddleware
from lemarche.api.authentication import CustomBearerAuthentication
from lemarche.api.utils import generate_random_string
from lemarche.users.factories import UserFactory

Expand Down Expand Up @@ -117,44 +116,3 @@ def test_authentication_with_no_token(self):
result = self.authentication.authenticate(request)

self.assertIsNone(result)


class DeprecationWarningMiddlewareTest(TestCase):
def setUp(self):
self.factory = RequestFactory()
self.middleware = DeprecationWarningMiddleware(lambda request: HttpResponse("Test response"))

def test_no_deprecation_warning(self):
"""
Test that no deprecation warning is present in the response.
This test sends a GET request to a specific API endpoint and checks
that the response does not contain a 'Deprecation-Warning' attribute.
"""
request = self.factory.get("/api/some-endpoint/")
response = self.middleware(request)

self.assertFalse(hasattr(response, "Deprecation-Warning"))

def test_with_deprecation_warning(self):
"""
Test that a deprecation warning is included in the response when the request
contains the _deprecated_auth_warning marker.
This test simulates a request to an endpoint with the _deprecated_auth_warning
marker set to True. It then checks that the response includes a "Deprecation-Warning"
header with the expected deprecation message indicating that URL token authentication
is deprecated and will be removed by January 2025, and advises to use the Authorization
header with Bearer tokens instead.
"""
request = self.factory.get("/api/some-endpoint/")
request._deprecated_auth_warning = True # Ajouter le marqueur

response = self.middleware(request)

self.assertIn("Deprecation-Warning", response)
self.assertEqual(
response["Deprecation-Warning"],
"URL token authentication is deprecated and will be removed on 2025/01. "
"Please use Authorization header with Bearer tokens.",
)
7 changes: 1 addition & 6 deletions lemarche/templates/api/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,7 @@ <h4 class="h2">Comment obtenir un token ?</h4>

<h4 class="h2">Comment se servir du token ?</h4>

<p>Rien de plus simple, il suffit de rajouter <code>token=&lt;VOTRE-TOKEN&gt;</code> dans l'URL de vos requêtes API</p>
<p>
Exemples :<br />
- <code>https://lemarche.inclusion.beta.gouv.fr/api/siae/?token=test</code><br />
- <code>https://lemarche.inclusion.beta.gouv.fr/api/siae/?department=38&kind=ESAT&token=test</code>
</p>
<p>Rien de plus simple, il suffit de rajouter <code>token=&lt;VOTRE-TOKEN&gt;</code> dans le header "authorization" de vos requêtes API</p>
</div>
</div>

Expand Down

0 comments on commit a64a402

Please sign in to comment.