diff --git a/apps/authentication/backends/oauth2/urls.py b/apps/authentication/backends/oauth2/urls.py index 94c044a7d..c44dd7c74 100644 --- a/apps/authentication/backends/oauth2/urls.py +++ b/apps/authentication/backends/oauth2/urls.py @@ -7,5 +7,6 @@ from . import views urlpatterns = [ path('login/', views.OAuth2AuthRequestView.as_view(), name='login'), - path('callback/', views.OAuth2AuthCallbackView.as_view(), name='login-callback') + path('callback/', views.OAuth2AuthCallbackView.as_view(), name='login-callback'), + path('logout/', views.OAuth2EndSessionView.as_view(), name='logout') ] diff --git a/apps/authentication/backends/oauth2/views.py b/apps/authentication/backends/oauth2/views.py index dd295fe86..d3f4865a2 100644 --- a/apps/authentication/backends/oauth2/views.py +++ b/apps/authentication/backends/oauth2/views.py @@ -1,6 +1,6 @@ from django.views import View from django.conf import settings -from django.contrib.auth import login +from django.contrib import auth from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.http import urlencode @@ -48,7 +48,7 @@ class OAuth2AuthCallbackView(View): user = authenticate(code=callback_params['code'], request=request) if user and user.is_valid: logger.debug(log_prompt.format('Login: {}'.format(user))) - login(self.request, user) + auth.login(self.request, user) logger.debug(log_prompt.format('Redirect')) return HttpResponseRedirect( settings.AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI @@ -56,3 +56,33 @@ class OAuth2AuthCallbackView(View): logger.debug(log_prompt.format('Redirect')) return HttpResponseRedirect(settings.AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI) + + +class OAuth2EndSessionView(View): + http_method_names = ['get', 'post', ] + + def get(self, request): + """ Processes GET requests. """ + log_prompt = "Process GET requests [OAuth2EndSessionView]: {}" + logger.debug(log_prompt.format('Start')) + return self.post(request) + + def post(self, request): + """ Processes POST requests. """ + log_prompt = "Process POST requests [OAuth2EndSessionView]: {}" + logger.debug(log_prompt.format('Start')) + + logout_url = settings.LOGOUT_REDIRECT_URL or '/' + + # Log out the current user. + if request.user.is_authenticated: + logger.debug(log_prompt.format('Log out the current user: {}'.format(request.user))) + auth.logout(request) + + if settings.AUTH_OAUTH2_LOGOUT_COMPLETELY: + logger.debug(log_prompt.format('Log out OAUTH2 platform user session synchronously')) + next_url = settings.AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT + return HttpResponseRedirect(next_url) + + logger.debug(log_prompt.format('Redirect')) + return HttpResponseRedirect(logout_url) diff --git a/apps/authentication/backends/saml2/views.py b/apps/authentication/backends/saml2/views.py index e91fd0660..9bc3ddc97 100644 --- a/apps/authentication/backends/saml2/views.py +++ b/apps/authentication/backends/saml2/views.py @@ -3,7 +3,7 @@ import copy from urllib import parse from django.views import View -from django.contrib import auth as auth +from django.contrib import auth from django.urls import reverse from django.conf import settings from django.views.decorators.csrf import csrf_exempt diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 88f580279..19720b587 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -330,6 +330,8 @@ class UserLogoutView(TemplateView): return settings.CAS_LOGOUT_URL_NAME elif 'saml2' in backend: return settings.SAML2_LOGOUT_URL_NAME + elif 'oauth2' in backend: + return settings.AUTH_OAUTH2_LOGOUT_URL_NAME return None def get(self, request, *args, **kwargs): diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 5ec858821..442089979 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -334,8 +334,10 @@ class Config(dict): 'AUTH_OAUTH2_CLIENT_ID': 'client-id', 'AUTH_OAUTH2_SCOPE': '', 'AUTH_OAUTH2_CLIENT_SECRET': '', + 'AUTH_OAUTH2_LOGOUT_COMPLETELY': True, 'AUTH_OAUTH2_PROVIDER_AUTHORIZATION_ENDPOINT': 'https://oauth2.example.com/authorize', 'AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT': 'https://oauth2.example.com/userinfo', + 'AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT': 'https://oauth2.example.com/logout', 'AUTH_OAUTH2_ACCESS_TOKEN_ENDPOINT': 'https://oauth2.example.com/access_token', 'AUTH_OAUTH2_ACCESS_TOKEN_METHOD': 'GET', 'AUTH_OAUTH2_USER_ATTR_MAP': { diff --git a/apps/jumpserver/settings/auth.py b/apps/jumpserver/settings/auth.py index 4aafdd35b..462aefc57 100644 --- a/apps/jumpserver/settings/auth.py +++ b/apps/jumpserver/settings/auth.py @@ -164,6 +164,7 @@ AUTH_OAUTH2_USER_ATTR_MAP = CONFIG.AUTH_OAUTH2_USER_ATTR_MAP AUTH_OAUTH2_AUTH_LOGIN_CALLBACK_URL_NAME = 'authentication:oauth2:login-callback' AUTH_OAUTH2_AUTHENTICATION_REDIRECT_URI = '/' AUTH_OAUTH2_AUTHENTICATION_FAILURE_REDIRECT_URI = '/' +AUTH_OAUTH2_LOGOUT_URL_NAME = "authentication:oauth2:logout" # 临时 token AUTH_TEMP_TOKEN = CONFIG.AUTH_TEMP_TOKEN diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py index a65266a1f..3c929e97a 100644 --- a/apps/jumpserver/settings/base.py +++ b/apps/jumpserver/settings/base.py @@ -142,6 +142,7 @@ WSGI_APPLICATION = 'jumpserver.wsgi.application' LOGIN_REDIRECT_URL = reverse_lazy('index') LOGIN_URL = reverse_lazy('authentication:login') +LOGOUT_REDIRECT_URL = CONFIG.LOGOUT_REDIRECT_URL SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN CSRF_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN diff --git a/apps/settings/serializers/auth/oauth2.py b/apps/settings/serializers/auth/oauth2.py index 279f3c34f..a2230750a 100644 --- a/apps/settings/serializers/auth/oauth2.py +++ b/apps/settings/serializers/auth/oauth2.py @@ -47,6 +47,10 @@ class OAuth2SettingSerializer(serializers.Serializer): AUTH_OAUTH2_PROVIDER_USERINFO_ENDPOINT = serializers.CharField( required=True, max_length=1024, label=_('Provider userinfo endpoint') ) + AUTH_OAUTH2_PROVIDER_END_SESSION_ENDPOINT = serializers.CharField( + required=False, max_length=1024, label=_('Provider end session endpoint') + ) + AUTH_OAUTH2_LOGOUT_COMPLETELY = serializers.BooleanField(required=False, label=_('Logout completely')) AUTH_OAUTH2_USER_ATTR_MAP = serializers.DictField( required=True, label=_('User attr map') )