Use a custom session serializer

Customize the JSONSerializer to make it possible to serialize and
deserialize Horizon-specific objects.

Change-Id: Icb7771d6abe81c510724bdef369a7620e222a32e
Signed-off-by: Radomir Dopieralski <openstack@dopieralski.pl>
This commit is contained in:
Takashi Kajinami
2025-10-31 14:38:31 +01:00
committed by Radomir Dopieralski
parent 131ac8ce53
commit 77412a02e8
5 changed files with 93 additions and 10 deletions
+85
View File
@@ -0,0 +1,85 @@
# 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 datetime
import json
from keystoneauth1.access import access
from keystoneauth1.access import service_catalog
from keystoneauth1.access import service_providers
from openstack_auth import user as auth_user
# The keys to identify serialized objects by.
TOKEN_KEYS = {'user', 'id', 'project', 'domain', 'roles', 'serviceCatalog'}
ACCESS_INFO_KEYS = {'_data', '_auth_token', '_service_catalog',
'_service_providers'}
def decode_datetime(data):
return datetime.datetime.fromisoformat(data) if data else None
class JSONEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, datetime.datetime):
return o.isoformat()
if o.__class__.__name__ == '__proxy__':
return str(o)
if isinstance(o, auth_user.Token):
return o.__dict__
if isinstance(o, access.AccessInfoV3):
return o.__dict__
if isinstance(o, service_catalog.ServiceCatalogV3):
return o._catalog
if isinstance(o, service_providers.ServiceProviders):
return o._service_providers
return super().default(o)
class JSONDecoder(json.JSONDecoder):
def __init__(self, object_hook=None, *args, **kwargs):
super().__init__(object_hook=self.object_hook, *args, **kwargs)
def object_hook(self, dct):
if all(key in dct for key in TOKEN_KEYS):
token = auth_user.Token()
for key, value in dct.items():
setattr(token, key, value)
token.expires = decode_datetime(token.expires)
token.user['password_expires_at'] = decode_datetime(
token.user['password_expires_at'])
return token
if all(key in dct for key in ACCESS_INFO_KEYS):
auth_info = access.AccessInfoV3(
body=dct['_data'],
auth_token=dct['_auth_token'],
)
for key, value in dct.items():
setattr(auth_info, key, value)
auth_info._service_catalog = service_catalog.ServiceCatalogV3(
auth_info._service_catalog)
providers = service_providers.ServiceProviders([])
providers._service_providers = auth_info._service_providers
auth_info._service_providers = providers
return auth_info
return dct
class HorizonSerializer:
def dumps(self, obj):
return json.dumps(
obj, separators=(",", ":"), cls=JSONEncoder).encode("latin-1")
def loads(self, data):
return json.loads(data.decode("latin-1"), cls=JSONDecoder)
+1 -3
View File
@@ -50,9 +50,7 @@ USE_TZ = True
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = 'domain'
# NOTE(saschpe): The openstack_auth.user.Token object isn't
# JSON-serializable ATM
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_SERIALIZER = 'openstack_auth.serializers.HorizonSerializer'
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
POLICY_FILES_PATH = os.path.join(TEST_DIR, "conf")
+5 -1
View File
@@ -81,7 +81,11 @@ class Token(object):
Added for maintaining backward compatibility with horizon that expects
Token object in the user object.
"""
def __init__(self, auth_ref, unscoped_token=None):
def __init__(self, auth_ref=None, unscoped_token=None):
if auth_ref is not None:
self.from_auth_ref(auth_ref, unscoped_token)
def from_auth_ref(self, auth_ref, unscoped_token=None):
# User-related attributes
user = {'id': auth_ref.user_id, 'name': auth_ref.username}
data = getattr(auth_ref, '_data', {})
+1 -4
View File
@@ -180,10 +180,7 @@ SESSION_COOKIE_HTTPONLY = True
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_SECURE = False
# when doing upgrades, it may be wise to stick to PickleSerializer
# NOTE(berendt): Check during the K-cycle if this variable can be removed.
# https://bugs.launchpad.net/horizon/+bug/1349463
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_SERIALIZER = 'openstack_auth.serializers.HorizonSerializer'
CSRF_FAILURE_VIEW = 'openstack_dashboard.views.csrf_failure'
+1 -2
View File
@@ -204,8 +204,7 @@ POLICY_FILES = {
# when we would like to test the policy check feature itself.
POLICY_CHECK_FUNCTION = None
# The openstack_auth.user.Token object isn't JSON-serializable ATM
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_SERIALIZER = 'openstack_auth.serializers.HorizonSerializer'
REST_API_SETTING_1 = 'foo'
REST_API_SETTING_2 = 'bar'