Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: migrate all sessions to the database #9736

Open
wants to merge 65 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
1620176
properly override modelbackend
rissson Dec 18, 2024
12b7573
wip
rissson Dec 18, 2024
cc2ab5e
Merge branch 'main' into database-sessions
rissson Jan 27, 2025
77de597
more session migration
rissson Jan 27, 2025
aa4c9a6
start migrations
rissson Jan 27, 2025
fe635a7
migrations
rissson Jan 27, 2025
803db07
lint
rissson Jan 27, 2025
ae2dd0c
fixup
rissson Jan 27, 2025
022a9b7
further migration fixes
rissson Jan 27, 2025
ece70a6
lint
rissson Jan 27, 2025
354a48e
add uuid pk
rissson Jan 27, 2025
8c4c7ad
Merge branch 'main' into database-sessions
rissson Jan 27, 2025
561be8f
make gen
rissson Jan 27, 2025
3e4a892
fixup
rissson Jan 27, 2025
afb691e
more migration fixes
rissson Jan 27, 2025
1980e92
lifecycle/migrate: don't migrate tenants if not enabled
rissson Jan 27, 2025
04d113b
wip
rissson Jan 27, 2025
b933482
wip
rissson Jan 27, 2025
be693e3
wip
rissson Jan 27, 2025
b169196
wip
rissson Jan 27, 2025
fd2e7a1
wip
rissson Jan 27, 2025
cffaf47
wip
rissson Jan 27, 2025
f17bb66
wip
rissson Jan 27, 2025
878fe49
wip
rissson Jan 27, 2025
1de2448
wip
rissson Jan 27, 2025
78d1758
wip
rissson Jan 27, 2025
f9cc1be
wip
rissson Jan 28, 2025
857db71
Merge branch 'main' into database-sessions
rissson Jan 31, 2025
1ecf041
wip
rissson Jan 31, 2025
d0efd42
wip
rissson Jan 31, 2025
432ca9a
wip
rissson Jan 31, 2025
72f4294
finally fix imgrations
rissson Jan 31, 2025
005f22b
fix [ermissions
rissson Jan 31, 2025
355569a
Merge branch 'main' into database-sessions
rissson Feb 17, 2025
c269dfa
remove migrations
rissson Feb 17, 2025
f117e90
wip
rissson Feb 21, 2025
c582026
Merge branch 'main' into database-sessions
rissson Feb 21, 2025
41a5d62
wip
rissson Feb 21, 2025
8118aaf
wip
rissson Feb 21, 2025
8775e29
wip
rissson Feb 21, 2025
dde3f5d
fix temp migrations
rissson Feb 21, 2025
cd9c52e
custom auth middleware
rissson Feb 21, 2025
412e18f
fix tests
rissson Feb 21, 2025
15e4c78
more test fixing we ball
rissson Feb 21, 2025
a9756f0
wip
rissson Feb 21, 2025
d6e26ab
fix more tests, prefetch interesting stuff
rissson Feb 21, 2025
db762f5
more prefetch
rissson Feb 21, 2025
56560a0
Merge branch 'main' into database-sessions
rissson Feb 21, 2025
d48c26c
wip
rissson Feb 21, 2025
09b5fc4
lint
rissson Feb 21, 2025
f9fbccd
fix
rissson Feb 21, 2025
4a6e941
fix migration
rissson Feb 21, 2025
d446c2f
wip
rissson Feb 21, 2025
85cc24b
wip
rissson Feb 21, 2025
e9500f4
wip
rissson Feb 21, 2025
4a2f974
wip
rissson Feb 21, 2025
e8d0425
api tweak
rissson Feb 21, 2025
a0646cb
more fixes
rissson Feb 21, 2025
d1b6856
wip
rissson Feb 21, 2025
6fa53de
wip
rissson Feb 21, 2025
b5b68c1
wip
rissson Feb 21, 2025
17db222
Merge branch 'main' into database-sessions
rissson Feb 21, 2025
b69a1d5
Merge branch 'main' into database-sessions
rissson Feb 24, 2025
07d0751
custom asgi auth middleware
rissson Feb 27, 2025
71f9ffa
Merge branch 'main' into database-sessions
rissson Feb 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions authentik/blueprints/v1/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
GroupSourceConnection,
PropertyMapping,
Provider,
Session,
Source,
User,
UserSourceConnection,
Expand Down Expand Up @@ -107,6 +108,7 @@ def excluded_models() -> list[type[Model]]:
Policy,
PolicyBindingModel,
# Classes that have other dependencies
Session,
AuthenticatedSession,
# Classes which are only internally managed
# FIXME: these shouldn't need to be explicitly listed, but rather based off of a mixin
Expand Down
9 changes: 9 additions & 0 deletions authentik/core/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ def authenticate(
self.set_method("password", request)
return user

async def aauthenticate(
self, request: HttpRequest, username: str | None, password: str | None, **kwargs: Any
) -> User | None:
user = await super().aauthenticate(request, username=username, password=password, **kwargs)
if not user:
return None
self.set_method("password", request)
return user

def set_method(self, method: str, request: HttpRequest | None, **kwargs):
"""Set method data on current flow, if possbiel"""
if not request:
Expand Down
11 changes: 11 additions & 0 deletions authentik/core/management/commands/clearsessions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Change user type"""

from authentik.core.models import Session
from authentik.tenants.management import TenantCommand


class Command(TenantCommand):
"""Delete all sessions"""

def handle_per_tenant(self, **options):
Session.objects.all().delete()
198 changes: 198 additions & 0 deletions authentik/core/migrations/0043_session_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
# Generated by Django 5.0.11 on 2025-01-27 12:58

import uuid
import pickle
import authentik.core.models
from django.db import migrations, models
import authentik.core.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 uuid import uuid4


SESSION_CACHE_ALIAS = "default"


def migrate_redis_sessions(apps, schema_editor):
from django.core.cache import caches

Session = apps.get_model("authentik_core", "Session")
db_alias = schema_editor.connection.alias
cache = caches[SESSION_CACHE_ALIAS]
# Not a redis cache, skipping
if not hasattr(cache, "keys"):
return
print("\nMigration Redis sessions to database, this might take a couple of minutes...")
Session.objects.using(db_alias).bulk_create([
Session(
session_key=key.removeprefix(KEY_PREFIX),
session_data=pickle.dumps(session_data, pickle.HIGHEST_PROTOCOL),
expires=now() + timedelta(seconds=cache.ttl(key)),
)
for key, session_data in progress_bar(cache.get_many(cache.keys(f"{KEY_PREFIX}*")).items())
])


def migrate_database_sessions(apps, schema_editor):
DjangoSession = apps.get_model("sessions", "Session")
Session = apps.get_model("authentik_core", "Session")
db_alias = schema_editor.connection.alias

print("\nMigration database sessions, this might take a couple of minutes...")
sessions_to_create = []
batch = 0
for django_session in progress_bar(DjangoSession.objects.using(db_alias).all()):
sessions_to_create.append(
Session(
uuid=uuid4(),
session_key=django_session.session_key,
session_data=django_session.session_data,
expires=django_session.expire_date,
)
)
batch += 1
if batch >= 500:
Session.objects.using(db_alias).bulk_create(sessions_to_create)
sessions_to_create = []
batch = 0
Session.objects.using(db_alias).bulk_create(sessions_to_create)


def migrate_authenticated_sessions(apps, schema_editor):
OldAuthenticatedSession = apps.get_model("authentik_core", "OldAuthenticatedSession")
AuthenticatedSession = apps.get_model("authentik_core", "AuthenticatedSession")
Session = apps.get_model("authentik_core", "Session")
db_alias = schema_editor.connection.alias

print("\nMigration database sessions, this might take a couple of minutes...")
for old_session in progress_bar(OldAuthenticatedSession.objects.using(db_alias).all()):
AuthenticatedSession.objects.create(
session_ptr=Session.objects.get(session_key=old_session.session_key),
user=old_session.user,
last_ip=old_session.last_ip,
last_user_agent=old_session.last_user_agent,
last_used=old_session.last_used,
)


class Migration(migrations.Migration):

dependencies = [
("sessions", "0001_initial"),
("authentik_core", "0042_authenticatedsession_authentik_c_expires_08251d_idx_and_more"),
("authentik_providers_oauth2", "0027_accesstoken_authentik_p_expires_9f24a5_idx_and_more"),
("authentik_providers_rac", "0006_connectiontoken_authentik_p_expires_91f148_idx_and_more"),
]

operations = [
# Rename AuthenticatedSession to OldAuthenticatedSession
migrations.RenameModel(
old_name="AuthenticatedSession",
new_name="OldAuthenticatedSession",
),
migrations.RenameIndex(
model_name="oldauthenticatedsession",
new_name="authentik_c_expires_cf4f72_idx",
old_name="authentik_c_expires_08251d_idx",
),
migrations.RenameIndex(
model_name="oldauthenticatedsession",
new_name="authentik_c_expirin_c1f17f_idx",
old_name="authentik_c_expirin_9cd839_idx",
),
migrations.RenameIndex(
model_name="oldauthenticatedsession",
new_name="authentik_c_expirin_e04f5d_idx",
old_name="authentik_c_expirin_195a84_idx",
),
migrations.RenameIndex(
model_name="oldauthenticatedsession",
new_name="authentik_c_session_a44819_idx",
old_name="authentik_c_session_d0f005_idx",
),
migrations.RunSQL(
"ALTER INDEX authentik_core_authenticatedsession_user_id_5055b6cf RENAME TO authentik_core_oldauthenticatedsession_user_id_5055b6cf",
"ALTER INDEX authentik_core_oldauthenticatedsession_user_id_5055b6cf RENAME TO authentik_core_authenticatedsession_user_id_5055b6cf",
),
# Create new Session and AuthenticatedSession models
migrations.CreateModel(
name="Session",
fields=[
(
"uuid",
models.UUIDField(
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
),
),
(
"session_key",
models.CharField(
max_length=40, db_index=True, verbose_name="session key"
),
),
("session_data", models.TextField(verbose_name="session data")),
("expires", models.DateTimeField(default=None, null=True)),
("expiring", models.BooleanField(default=True)),
],
options={
"verbose_name": "Session",
"verbose_name_plural": "Sessions",
},
managers=[
("objects", authentik.core.models.SessionManager()),
],
),
migrations.AddIndex(
model_name="session",
index=models.Index(fields=["expires"], name="authentik_c_expires_d2f607_idx"),
),
migrations.AddIndex(
model_name="session",
index=models.Index(fields=["expiring"], name="authentik_c_expirin_7c2cfb_idx"),
),
migrations.AddIndex(
model_name="session",
index=models.Index(
fields=["expiring", "expires"], name="authentik_c_expirin_1ab2e4_idx"
),
),
migrations.CreateModel(
name="AuthenticatedSession",
fields=[
(
"session_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="authentik_core.session",
),
),
("last_ip", models.TextField()),
("last_user_agent", models.TextField(blank=True)),
("last_used", models.DateTimeField(auto_now=True)),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
],
options={
"verbose_name": "Authenticated Session",
"verbose_name_plural": "Authenticated Sessions",
},
bases=("authentik_core.session",),
managers=[
("objects", authentik.core.models.SessionManager()),
],
),
migrations.RunPython(migrate_redis_sessions, migrations.RunPython.noop),
migrations.RunPython(migrate_database_sessions, migrations.RunPython.noop),
# migrations.RunPython(migrate_authenticated_sessions, migrations.RunPython.noop),
]
37 changes: 37 additions & 0 deletions authentik/core/migrations/0044_delete_oldauthenticatedsession.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Generated by Django 5.0.11 on 2025-01-27 13:02

from django.db import migrations
from django.contrib.sessions.backends.cache import KEY_PREFIX


def delete_redis_sessions(apps, schema_editor):
from django.core.cache import caches

cache = caches["default"]
# Not a redis cache, skipping
if not hasattr(cache, "keys"):
return
cache.delete_many(cache.keys(f"{KEY_PREFIX}*"))


def delete_old_database_sessions(apps, schema_editor):
DjangoSession = apps.get_model("sessions", "Session")
db_alias = schema_editor.connection.alias
DjangoSession.objects.using(db_alias).all().delete()


class Migration(migrations.Migration):

dependencies = [
("authentik_core", "0043_session_and_more"),
("authentik_providers_rac", "0007_migrate_session"),
("authentik_providers_oauth2", "0028_migrate_session"),
]

operations = [
# migrations.DeleteModel(
# name="OldAuthenticatedSession",
# ),
# migrations.RunPython(code=delete_redis_sessions),
# migrations.RunPython(code=delete_old_database_sessions),
]
Loading
Loading