mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-19 05:25:42 +00:00
[PUI] SSO Support (#6333)
* Add sso buttons Fixes #5753 * Added more icons * fix callback url * made heading dynamic * allow either sso or normal reg * Added SSO registration * added divider * added preferred ui API * fix test * fix update function * refactor * fix naming * fix import * add coverage ignore * more ignore * fixed missing key * renamed button * revert coverage statements * set prefered mode before sso login * added dynamic login redirect * fixed test assert * use API Endpoints instead of hardcoding * fix lookup
This commit is contained in:
@ -1148,5 +1148,4 @@ if CUSTOM_FLAGS:
|
||||
|
||||
# Magic login django-sesame
|
||||
SESAME_MAX_AGE = 300
|
||||
# LOGIN_REDIRECT_URL = f"/{FRONTEND_URL_BASE}/logged-in/"
|
||||
LOGIN_REDIRECT_URL = '/index/'
|
||||
LOGIN_REDIRECT_URL = '/api/auth/login-redirect/'
|
||||
|
@ -1247,9 +1247,6 @@ class MagicLoginTest(InvenTreeTestCase):
|
||||
# Check that the login works
|
||||
resp = self.client.get(reverse('sesame-login') + '?sesame=' + token)
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
self.assertEqual(resp.url, '/index/')
|
||||
# Note: 2023-08-08 - This test has been changed because "platform UI" is not generally available yet
|
||||
# TODO: In the future, the URL comparison will need to be reverted
|
||||
# self.assertEqual(resp.url, f'/{settings.FRONTEND_URL_BASE}/logged-in/')
|
||||
self.assertEqual(resp.url, '/api/auth/login-redirect/')
|
||||
# And we should be logged in again
|
||||
self.assertEqual(resp.wsgi_request.user, self.user)
|
||||
|
@ -35,6 +35,7 @@ from order.urls import order_urls
|
||||
from part.urls import part_urls
|
||||
from plugin.urls import get_plugin_urls
|
||||
from stock.urls import stock_urls
|
||||
from web.urls import api_urls as web_api_urls
|
||||
from web.urls import urlpatterns as platform_urls
|
||||
|
||||
from .api import APISearchView, InfoView, NotFoundView, VersionTextView, VersionView
|
||||
@ -84,6 +85,7 @@ apipatterns = [
|
||||
path('report/', include(report.api.report_api_urls)),
|
||||
path('user/', include(users.api.user_urls)),
|
||||
path('admin/', include(common.api.admin_api_urls)),
|
||||
path('web/', include(web_api_urls)),
|
||||
# Plugin endpoints
|
||||
path('', include(plugin.api.plugin_api_urls)),
|
||||
# Common endpoints endpoint
|
||||
@ -149,6 +151,11 @@ apipatterns = [
|
||||
name='social_account_disconnect',
|
||||
),
|
||||
path('logout/', users.api.Logout.as_view(), name='api-logout'),
|
||||
path(
|
||||
'login-redirect/',
|
||||
users.api.LoginRedirect.as_view(),
|
||||
name='api-login-redirect',
|
||||
),
|
||||
path('', include('dj_rest_auth.urls')),
|
||||
]),
|
||||
),
|
||||
|
@ -6,6 +6,7 @@ import logging
|
||||
from django.contrib.auth import get_user, login
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.urls import include, path, re_path
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from dj_rest_auth.views import LogoutView
|
||||
from rest_framework import exceptions, permissions
|
||||
@ -22,6 +23,7 @@ from InvenTree.mixins import (
|
||||
RetrieveUpdateDestroyAPI,
|
||||
)
|
||||
from InvenTree.serializers import ExendedUserSerializer, UserCreateSerializer
|
||||
from InvenTree.settings import FRONTEND_URL_BASE
|
||||
from users.models import ApiToken, Owner, RuleSet, check_user_role
|
||||
from users.serializers import GroupSerializer, OwnerSerializer
|
||||
|
||||
@ -279,6 +281,17 @@ class GetAuthToken(APIView):
|
||||
raise exceptions.NotAuthenticated()
|
||||
|
||||
|
||||
class LoginRedirect(RedirectView):
|
||||
"""Redirect to the correct starting page after backend login."""
|
||||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
"""Return the URL to redirect to."""
|
||||
session = self.request.session
|
||||
if session.get('preferred_method', 'cui') == 'pui':
|
||||
return f'/{FRONTEND_URL_BASE}/logged-in/'
|
||||
return '/index/'
|
||||
|
||||
|
||||
user_urls = [
|
||||
path('roles/', RoleDetails.as_view(), name='api-user-roles'),
|
||||
path('token/', GetAuthToken.as_view(), name='api-token'),
|
||||
|
@ -5,8 +5,10 @@ import os
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from django.urls import reverse
|
||||
|
||||
from InvenTree.config import get_frontend_settings
|
||||
from InvenTree.unit_test import InvenTreeTestCase
|
||||
from InvenTree.unit_test import InvenTreeAPITestCase, InvenTreeTestCase
|
||||
|
||||
from .templatetags import spa_helper
|
||||
|
||||
@ -73,3 +75,26 @@ class TemplateTagTest(InvenTreeTestCase):
|
||||
rsp = get_frontend_settings(False)
|
||||
self.assertFalse('show_server_selector' in rsp)
|
||||
self.assertEqual(rsp['server_list'], ['aa', 'bb'])
|
||||
|
||||
|
||||
class TestWebHelpers(InvenTreeAPITestCase):
|
||||
"""Tests for the web helpers."""
|
||||
|
||||
def test_ui_preference(self):
|
||||
"""Test the UI preference API."""
|
||||
url = reverse('api-ui-preference')
|
||||
|
||||
# Test default
|
||||
resp = self.get(url)
|
||||
data = json.loads(resp.content)
|
||||
self.assertTrue(data['cui'])
|
||||
self.assertFalse(data['pui'])
|
||||
self.assertEqual(data['preferred_method'], 'cui')
|
||||
|
||||
# Set to PUI
|
||||
resp = self.put(url, {'preferred_method': 'pui'})
|
||||
data = json.loads(resp.content)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertFalse(data['cui'])
|
||||
self.assertTrue(data['pui'])
|
||||
self.assertEqual(data['preferred_method'], 'pui')
|
||||
|
@ -1,11 +1,16 @@
|
||||
"""URLs for web app."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import include, path, re_path
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from rest_framework import permissions, serializers
|
||||
|
||||
from InvenTree.mixins import RetrieveUpdateAPI
|
||||
|
||||
|
||||
class RedirectAssetView(TemplateView):
|
||||
"""View to redirect to static asset."""
|
||||
@ -17,6 +22,55 @@ class RedirectAssetView(TemplateView):
|
||||
)
|
||||
|
||||
|
||||
class PreferredSerializer(serializers.Serializer):
|
||||
"""Serializer for the preferred serializer session setting."""
|
||||
|
||||
preferred_method = serializers.ChoiceField(choices=['cui', 'pui'])
|
||||
pui = serializers.SerializerMethodField(read_only=True)
|
||||
cui = serializers.SerializerMethodField(read_only=True)
|
||||
|
||||
def get_pui(self, obj):
|
||||
"""Return true if preferred method is PUI."""
|
||||
return obj['preferred_method'] == 'pui'
|
||||
|
||||
def get_cui(self, obj):
|
||||
"""Return true if preferred method is CUI."""
|
||||
return obj['preferred_method'] == 'cui'
|
||||
|
||||
class Meta:
|
||||
"""Meta class for PreferedSerializer."""
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class PreferredUiView(RetrieveUpdateAPI):
|
||||
"""Set preferred UI (CIU/PUI)."""
|
||||
|
||||
permission_classes = [permissions.AllowAny]
|
||||
serializer_class = PreferredSerializer
|
||||
http_method_names = ['get', 'post', 'put', 'head', 'options']
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
"""Retrieve the preferred UI method."""
|
||||
session = self.request.session
|
||||
session['preferred_method'] = session.get('preferred_method', 'cui')
|
||||
serializer = self.get_serializer(data=dict(session))
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return JsonResponse(serializer.data)
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
"""Update the preferred UI method."""
|
||||
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
# Run update
|
||||
session = self.request.session
|
||||
session['preferred_method'] = serializer.validated_data['preferred_method']
|
||||
session.modified = True
|
||||
|
||||
return JsonResponse(serializer.data)
|
||||
|
||||
|
||||
spa_view = ensure_csrf_cookie(TemplateView.as_view(template_name='web/index.html'))
|
||||
assets_path = path('assets/<path:path>', RedirectAssetView.as_view())
|
||||
|
||||
@ -37,3 +91,8 @@ urlpatterns = [
|
||||
assets_path,
|
||||
path(settings.FRONTEND_URL_BASE, spa_view, name='platform'),
|
||||
]
|
||||
|
||||
api_urls = [
|
||||
# UI Preference
|
||||
path('ui_preference/', PreferredUiView.as_view(), name='api-ui-preference')
|
||||
]
|
||||
|
Reference in New Issue
Block a user