Merge "Create Horizon session control logic"

This commit is contained in:
Zuul 2022-03-03 18:57:00 +00:00 committed by Gerrit Code Review
commit ba079cbe38
7 changed files with 144 additions and 0 deletions

View File

@ -953,6 +953,22 @@ menu and the api access panel.
`OPENSTACK_CLOUDS_YAML_CUSTOM_TEMPLATE`_ to provide a custom
``clouds.yaml``.
SIMULTANEOUS_SESSIONS
---------------------
.. versionadded:: 21.1.0(Yoga)
Default: ``allow``
Controls whether a user can have multiple simultaneous sessions.
Valid values are ``allow`` and ``disconnect``.
The value ``allow`` enables more than one simultaneous sessions for a user.
The Value ``disconnect`` disables more than one simultaneous sessions for
a user. Only one active session is allowed. The newer session will be
considered as the valid one and any existing session will be disconnected
after a subsequent successful login.
THEME_COLLECTION_DIR
--------------------

View File

@ -86,6 +86,9 @@ OPERATION_LOG_OPTIONS = {
),
}
# Control whether a same user can have multiple action sessions.
SIMULTANEOUS_SESSIONS = 'allow'
OPENSTACK_PROFILER = {
'enabled': False,
'facility_name': 'horizon',

View File

@ -14,6 +14,8 @@
from horizon.middleware import base
from horizon.middleware import operation_log
from horizon.middleware import simultaneous_sessions as sessions
HorizonMiddleware = base.HorizonMiddleware
OperationLogMiddleware = operation_log.OperationLogMiddleware
SimultaneousSessionsMiddleware = sessions.SimultaneousSessionsMiddleware

View File

@ -0,0 +1,50 @@
# Copyright (c) 2021 Wind River Systems Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import importlib
import logging
from django.conf import settings
from django.core.cache import caches
LOG = logging.getLogger(__name__)
class SimultaneousSessionsMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
self.simultaneous_sessions = settings.SIMULTANEOUS_SESSIONS
def __call__(self, request):
self._process_request(request)
response = self.get_response(request)
return response
def _process_request(self, request):
cache = caches['default']
cache_key = ('user_pk_{}_restrict').format(request.user.pk)
cache_value = cache.get(cache_key)
if cache_value and self.simultaneous_sessions == 'disconnect':
if request.session.session_key != cache_value:
LOG.info('The user %s is already logged in, '
'the last session will be disconnected.',
request.user.id)
engine = importlib.import_module(settings.SESSION_ENGINE)
session = engine.SessionStore(session_key=cache_value)
session.delete()
cache.set(cache_key, request.session.session_key,
settings.SESSION_TIMEOUT)
else:
cache.set(cache_key, request.session.session_key,
settings.SESSION_TIMEOUT)

View File

@ -0,0 +1,61 @@
# Copyright (c) 2021 Wind River Systems Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from unittest import mock
from django.conf import settings
from django.contrib.sessions.backends import signed_cookies
from django import test as django_test
from django.test.utils import override_settings
from horizon import middleware
from horizon.test import helpers as test
class SimultaneousSessionsMiddlewareTest(django_test.TestCase):
def setUp(self):
self.url = settings.LOGIN_URL
self.factory = test.RequestFactoryWithMessages()
self.get_response = mock.Mock()
self.request = self.factory.get(self.url)
self.request.user.pk = '123'
super().setUp()
@mock.patch.object(signed_cookies.SessionStore, 'delete', return_value=None)
def test_simultaneous_sessions(self, mock_delete):
mw = middleware.SimultaneousSessionsMiddleware(
self.get_response)
self.request.session._set_session_key('123456789')
mw._process_request(self.request)
mock_delete.assert_not_called()
self.request.session._set_session_key('987654321')
mw._process_request(self.request)
mock_delete.assert_not_called()
@override_settings(SIMULTANEOUS_SESSIONS='disconnect')
@mock.patch.object(signed_cookies.SessionStore, 'delete', return_value=None)
def test_disconnect_simultaneous_sessions(self, mock_delete):
mw = middleware.SimultaneousSessionsMiddleware(
self.get_response)
self.request.session._set_session_key('123456789')
mw._process_request(self.request)
mock_delete.assert_not_called()
self.request.session._set_session_key('987654321')
mw._process_request(self.request)
mock_delete.assert_called_once_with()

View File

@ -82,6 +82,7 @@ MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'horizon.middleware.OperationLogMiddleware',
'horizon.middleware.SimultaneousSessionsMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'horizon.middleware.HorizonMiddleware',
'horizon.themes.ThemeMiddleware',

View File

@ -0,0 +1,11 @@
features:
- |
[:blueprint:`handle-multiple-login-sessions-from-same-user-in-horizon`]
This blueprint allows operators to control if multiple simultaneous
dashboard sessions are allowed or not for a user. A new setting
``SIMULTANEOUS_SESSIONS`` controls the behavior. The default behavior
allows multiple dashboard sessions for a user. The new setting allows
operators to configure horizon to disallow multiple sessions per user.
When multiple simultaneous sessions are disabled, the most recent
authenticated session will be considered as the valid one and
the previous session will be invalidated.