|
|
|
@ -1,126 +1,9 @@
|
|
|
|
|
# Generated by Django 5.0.11 on 2025-01-27 12:58
|
|
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
import pickle # nosec
|
|
|
|
|
from django.core import signing
|
|
|
|
|
from django.contrib.auth import BACKEND_SESSION_KEY, HASH_SESSION_KEY, SESSION_KEY
|
|
|
|
|
from django.db import migrations, models
|
|
|
|
|
import django.db.models.deletion
|
|
|
|
|
from django.conf import settings
|
|
|
|
|
from django.contrib.sessions.backends.cache import KEY_PREFIX
|
|
|
|
|
from django.utils.timezone import now, timedelta
|
|
|
|
|
from authentik.lib.migrations import progress_bar
|
|
|
|
|
from authentik.root.middleware import ClientIPMiddleware
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SESSION_CACHE_ALIAS = "default"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PickleSerializer:
|
|
|
|
|
"""
|
|
|
|
|
Simple wrapper around pickle to be used in signing.dumps()/loads() and
|
|
|
|
|
cache backends.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, protocol=None):
|
|
|
|
|
self.protocol = pickle.HIGHEST_PROTOCOL if protocol is None else protocol
|
|
|
|
|
|
|
|
|
|
def dumps(self, obj):
|
|
|
|
|
"""Pickle data to be stored in redis"""
|
|
|
|
|
return pickle.dumps(obj, self.protocol)
|
|
|
|
|
|
|
|
|
|
def loads(self, data):
|
|
|
|
|
"""Unpickle data to be loaded from redis"""
|
|
|
|
|
try:
|
|
|
|
|
return pickle.loads(data) # nosec
|
|
|
|
|
except Exception:
|
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _migrate_session(
|
|
|
|
|
apps,
|
|
|
|
|
db_alias,
|
|
|
|
|
session_key,
|
|
|
|
|
session_data,
|
|
|
|
|
expires,
|
|
|
|
|
):
|
|
|
|
|
Session = apps.get_model("authentik_core", "Session")
|
|
|
|
|
OldAuthenticatedSession = apps.get_model("authentik_core", "OldAuthenticatedSession")
|
|
|
|
|
AuthenticatedSession = apps.get_model("authentik_core", "AuthenticatedSession")
|
|
|
|
|
|
|
|
|
|
old_auth_session = (
|
|
|
|
|
OldAuthenticatedSession.objects.using(db_alias).filter(session_key=session_key).first()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
args = {
|
|
|
|
|
"session_key": session_key,
|
|
|
|
|
"expires": expires,
|
|
|
|
|
"last_ip": ClientIPMiddleware.default_ip,
|
|
|
|
|
"last_user_agent": "",
|
|
|
|
|
"session_data": {},
|
|
|
|
|
}
|
|
|
|
|
for k, v in session_data.items():
|
|
|
|
|
if k == "authentik/stages/user_login/last_ip":
|
|
|
|
|
args["last_ip"] = v
|
|
|
|
|
elif k in ["last_user_agent", "last_used"]:
|
|
|
|
|
args[k] = v
|
|
|
|
|
elif args in [SESSION_KEY, BACKEND_SESSION_KEY, HASH_SESSION_KEY]:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
args["session_data"][k] = v
|
|
|
|
|
if old_auth_session:
|
|
|
|
|
args["last_user_agent"] = old_auth_session.last_user_agent
|
|
|
|
|
args["last_used"] = old_auth_session.last_used
|
|
|
|
|
|
|
|
|
|
args["session_data"] = pickle.dumps(args["session_data"])
|
|
|
|
|
session = Session.objects.using(db_alias).create(**args)
|
|
|
|
|
|
|
|
|
|
if old_auth_session:
|
|
|
|
|
AuthenticatedSession.objects.using(db_alias).create(
|
|
|
|
|
session=session,
|
|
|
|
|
user=old_auth_session.user,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def migrate_redis_sessions(apps, schema_editor):
|
|
|
|
|
from django.core.cache import caches
|
|
|
|
|
|
|
|
|
|
db_alias = schema_editor.connection.alias
|
|
|
|
|
cache = caches[SESSION_CACHE_ALIAS]
|
|
|
|
|
|
|
|
|
|
# Not a redis cache, skipping
|
|
|
|
|
if not hasattr(cache, "keys"):
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
print("\nMigrating Redis sessions to database, this might take a couple of minutes...")
|
|
|
|
|
for key, session_data in progress_bar(cache.get_many(cache.keys(f"{KEY_PREFIX}*")).items()):
|
|
|
|
|
_migrate_session(
|
|
|
|
|
apps=apps,
|
|
|
|
|
db_alias=db_alias,
|
|
|
|
|
session_key=key.removeprefix(KEY_PREFIX),
|
|
|
|
|
session_data=session_data,
|
|
|
|
|
expires=now() + timedelta(seconds=cache.ttl(key)),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def migrate_database_sessions(apps, schema_editor):
|
|
|
|
|
DjangoSession = apps.get_model("sessions", "Session")
|
|
|
|
|
db_alias = schema_editor.connection.alias
|
|
|
|
|
|
|
|
|
|
print("\nMigration database sessions, this might take a couple of minutes...")
|
|
|
|
|
for django_session in progress_bar(DjangoSession.objects.using(db_alias).all()):
|
|
|
|
|
session_data = signing.loads(
|
|
|
|
|
django_session.session_data,
|
|
|
|
|
salt="django.contrib.sessions.SessionStore",
|
|
|
|
|
serializer=PickleSerializer,
|
|
|
|
|
)
|
|
|
|
|
_migrate_session(
|
|
|
|
|
apps=apps,
|
|
|
|
|
db_alias=db_alias,
|
|
|
|
|
session_key=django_session.session_key,
|
|
|
|
|
session_data=session_data,
|
|
|
|
|
expires=django_session.expire_date,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
@ -230,12 +113,4 @@ class Migration(migrations.Migration):
|
|
|
|
|
"verbose_name_plural": "Authenticated Sessions",
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
migrations.RunPython(
|
|
|
|
|
code=migrate_redis_sessions,
|
|
|
|
|
reverse_code=migrations.RunPython.noop,
|
|
|
|
|
),
|
|
|
|
|
migrations.RunPython(
|
|
|
|
|
code=migrate_database_sessions,
|
|
|
|
|
reverse_code=migrations.RunPython.noop,
|
|
|
|
|
),
|
|
|
|
|
]
|
|
|
|
|