From c31dcbc64a70300e540bfc95806c05be05a6e291 Mon Sep 17 00:00:00 2001 From: Nassim Tabchiche Date: Thu, 18 Apr 2024 12:05:45 +0200 Subject: [PATCH] Implement django-rest-knox token authentication --- backend/ciso_assistant/settings.py | 23 +++++++++++++++-------- backend/ciso_assistant/urls.py | 23 ++++++++++++++++------- backend/core/apps.py | 2 -- backend/core/signals.py | 9 --------- backend/iam/knox/__init__.py | 0 backend/iam/knox/views.py | 16 ++++++++++++++++ backend/iam/urls.py | 23 ++++++++++++++++++++--- 7 files changed, 67 insertions(+), 29 deletions(-) delete mode 100644 backend/core/signals.py create mode 100644 backend/iam/knox/__init__.py create mode 100644 backend/iam/knox/views.py diff --git a/backend/ciso_assistant/settings.py b/backend/ciso_assistant/settings.py index c21e66d834..c433fa26c1 100644 --- a/backend/ciso_assistant/settings.py +++ b/backend/ciso_assistant/settings.py @@ -30,12 +30,12 @@ CISO_ASSISTANT_URL = os.environ.get("CISO_ASSISTANT_URL", "http://localhost:5173") + def set_ciso_assistant_url(_, __, event_dict): event_dict["ciso_assistant_url"] = CISO_ASSISTANT_URL return event_dict - LOGGING = { "version": 1, "disable_existing_loggers": False, @@ -112,6 +112,14 @@ def set_ciso_assistant_url(_, __, event_dict): PAGINATE_BY = os.environ.get("PAGINATE_BY", default=500) +AUTHENTICATION_METHOD = os.environ.get("AUTHENTICATION_METHOD", "session") + +AUTHENTICATION_CLASS = { + "session": "rest_framework.authentication.SessionAuthentication", + "knox": "knox.auth.TokenAuthentication", +} + +logger.info("AUTHENTICATION_METHOD: %s", AUTHENTICATION_METHOD) # Application definition @@ -130,8 +138,8 @@ def set_ciso_assistant_url(_, __, event_dict): "library", "serdes", "rest_framework", + "knox", "drf_spectacular", - "rest_framework.authtoken", ] MIDDLEWARE = [ @@ -181,8 +189,7 @@ def set_ciso_assistant_url(_, __, event_dict): "rest_framework.renderers.JSONRenderer", ], "DEFAULT_AUTHENTICATION_CLASSES": [ - "rest_framework.authentication.SessionAuthentication", - "rest_framework.authentication.TokenAuthentication", + AUTHENTICATION_CLASS[AUTHENTICATION_METHOD], ], "DEFAULT_PERMISSION_CLASSES": [ "rest_framework.permissions.IsAuthenticated", @@ -308,9 +315,9 @@ def set_ciso_assistant_url(_, __, event_dict): ] SPECTACULAR_SETTINGS = { - 'TITLE': 'CISO Assistant API', - 'DESCRIPTION': 'CISO Assistant - API Documentation', - 'VERSION': '1.0.0', - 'SERVE_INCLUDE_SCHEMA': False, + "TITLE": "CISO Assistant API", + "DESCRIPTION": "CISO Assistant - API Documentation", + "VERSION": "1.0.0", + "SERVE_INCLUDE_SCHEMA": False, # OTHER SETTINGS } diff --git a/backend/ciso_assistant/urls.py b/backend/ciso_assistant/urls.py index 0bba1a75f0..c9cbb2067c 100644 --- a/backend/ciso_assistant/urls.py +++ b/backend/ciso_assistant/urls.py @@ -14,17 +14,26 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.urls import include, path -from ciso_assistant import settings -from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView -from rest_framework.authtoken import views +from drf_spectacular.views import ( + SpectacularAPIView, + SpectacularRedocView, + SpectacularSwaggerView, +) # beware of the order of url patterns, this can change de behavior in case of multiple matches and avoid giving identical paths that could cause conflicts urlpatterns = [ path("api/", include("core.urls")), - path('api-token-auth/', views.obtain_auth_token), path("serdes/", include("serdes.urls")), path("i18n/", include("django.conf.urls.i18n")), - path('api/schema/', SpectacularAPIView.as_view(), name='schema'), - path('api/schema/swagger/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger'), - path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), + path("api/schema/", SpectacularAPIView.as_view(), name="schema"), + path( + "api/schema/swagger/", + SpectacularSwaggerView.as_view(url_name="schema"), + name="swagger", + ), + path( + "api/schema/redoc/", + SpectacularRedocView.as_view(url_name="schema"), + name="redoc", + ), ] diff --git a/backend/core/apps.py b/backend/core/apps.py index 6f93e48fe1..4846d60cce 100644 --- a/backend/core/apps.py +++ b/backend/core/apps.py @@ -359,5 +359,3 @@ def ready(self): # avoid post_migrate handler if we are in the main, as it interferes with restore if not os.environ.get("RUN_MAIN"): post_migrate.connect(startup, sender=self) - - import core.signals diff --git a/backend/core/signals.py b/backend/core/signals.py deleted file mode 100644 index 697a46916f..0000000000 --- a/backend/core/signals.py +++ /dev/null @@ -1,9 +0,0 @@ -from django.conf import settings -from django.db.models.signals import post_save -from django.dispatch import receiver -from rest_framework.authtoken.models import Token - -@receiver(post_save, sender=settings.AUTH_USER_MODEL) -def create_auth_token(sender, instance=None, created=False, **kwargs): - if created: - Token.objects.create(user=instance) \ No newline at end of file diff --git a/backend/iam/knox/__init__.py b/backend/iam/knox/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/iam/knox/views.py b/backend/iam/knox/views.py new file mode 100644 index 0000000000..0efaaaa620 --- /dev/null +++ b/backend/iam/knox/views.py @@ -0,0 +1,16 @@ +from django.contrib.auth import login + +from rest_framework import permissions +from rest_framework.authtoken.serializers import AuthTokenSerializer +from knox.views import LoginView as KnoxLoginView + + +class LoginView(KnoxLoginView): + permission_classes = (permissions.AllowAny,) + + def post(self, request, format=None): + serializer = AuthTokenSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + user = serializer.validated_data["user"] + login(request, user) + return super(LoginView, self).post(request, format=None) diff --git a/backend/iam/urls.py b/backend/iam/urls.py index 474ef2f2ae..fda4e7f507 100644 --- a/backend/iam/urls.py +++ b/backend/iam/urls.py @@ -1,13 +1,26 @@ from django.urls import path +from ciso_assistant.settings import AUTHENTICATION_METHOD from core.views import FirstConnexionPasswordConfirmView from .views import * +from .knox.views import LoginView as KnoxLoginView +import knox.views as knox_views + +authentication_urls = { + "session": [ + path("login/", LoginView.as_view(), name="login"), + path("logout/", LogoutView.as_view(), name="logout"), + path("current-user/", CurrentUserView.as_view(), name="current-user"), + ], + "knox": [ + path(r"login/", KnoxLoginView.as_view(), name="knox_login"), + path(r"logout/", knox_views.LogoutView.as_view(), name="knox_logout"), + path(r"logoutall/", knox_views.LogoutAllView.as_view(), name="knox_logoutall"), + ], +} urlpatterns = [ - path("login/", LoginView.as_view(), name="login"), - path("logout/", LogoutView.as_view(), name="logout"), - path("current-user/", CurrentUserView.as_view(), name="current-user"), path("change-password/", ChangePasswordView.as_view(), name="change-password"), path("password-reset/", PasswordResetView.as_view(), name="password-reset"), path( @@ -22,3 +35,7 @@ name="first_connexion_confirm", ), ] + +urlpatterns += authentication_urls[AUTHENTICATION_METHOD] + +print(urlpatterns)