misc: unplug auth_migrations_18 (#40685)

Native django.contrib.auth migrations can be used now, the only glitch
are permissions on the proxy model LDAPUser:

    A problem arose migrating proxy model permissions for custom_user_user to authentic2_ldapuser.

      Permission(s) for authentic2_ldapuser already existed.
      Codenames Q: (AND: ('codename__in', ['add_ldapuser', 'change_ldapuser', 'delete_ldapuser', 'view_ldapuser']))

    Ensure to audit ALL permissions for custom_user_user and authentic2_ldapuser.

It can be fixed with the following SQL command:

   DELETE FROM auth_permission WHERE content_type_id = (SELECT id FROM django_content_type WHERE model = 'ldapuser' AND app_label = 'authentic2');
This commit is contained in:
Benjamin Dauvergne 2023-12-13 19:49:43 +01:00
parent f101fdcd1b
commit 3f9829d261
16 changed files with 20 additions and 522 deletions

View File

@ -2,9 +2,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
]
dependencies = []
operations = [
migrations.CreateModel(
@ -424,15 +422,6 @@ class Migration(migrations.Migration):
on_delete=models.CASCADE,
),
),
(
'user',
models.ForeignKey(
related_name='user_alias_in_source',
verbose_name='user',
to='auth.User',
on_delete=models.CASCADE,
),
),
],
options={
'verbose_name': 'user alias from source',
@ -448,16 +437,6 @@ class Migration(migrations.Migration):
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('data', models.TextField(null=True, blank=True)),
(
'user',
models.OneToOneField(
related_name='user_attribute_profile',
null=True,
blank=True,
to='auth.User',
on_delete=models.CASCADE,
),
),
],
options={
'verbose_name': 'user attribute profile',

View File

@ -4,32 +4,7 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('attribute_aggregator', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='useraliasinsource',
name='user',
field=models.ForeignKey(
related_name='user_alias_in_source',
verbose_name='user',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
preserve_default=True,
),
migrations.AlterField(
model_name='userattributeprofile',
name='user',
field=models.OneToOneField(
related_name='user_attribute_profile',
null=True,
blank=True,
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
preserve_default=True,
),
]
operations = []

View File

@ -15,11 +15,4 @@ class Migration(migrations.Migration):
to='attribute_aggregator.AttributeItem', verbose_name='Attributes', blank=True
),
),
migrations.AlterField(
model_name='useraliasinsource',
name='user',
field=models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
),
]

View File

@ -40,17 +40,9 @@ class Migration(migrations.Migration):
migrations.DeleteModel(
name='AttributeSource',
),
migrations.RemoveField(
model_name='useraliasinsource',
name='user',
),
migrations.DeleteModel(
name='UserAliasInSource',
),
migrations.RemoveField(
model_name='userattributeprofile',
name='user',
),
migrations.DeleteModel(
name='UserAttributeProfile',
),

View File

@ -1,135 +0,0 @@
from django.core import validators
from django.db import migrations, models
from django.utils import timezone
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '__first__'),
]
operations = [
migrations.CreateModel(
name='Permission',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(max_length=50, verbose_name='name')),
(
'content_type',
models.ForeignKey(to='contenttypes.ContentType', to_field='id', on_delete=models.CASCADE),
),
('codename', models.CharField(max_length=100, verbose_name='codename')),
],
options={
'ordering': ('content_type__app_label', 'content_type__model', 'codename'),
'verbose_name': 'permission',
'verbose_name_plural': 'permissions',
},
),
migrations.CreateModel(
name='Group',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(unique=True, max_length=80, verbose_name='name')),
(
'permissions',
models.ManyToManyField(to='auth.Permission', verbose_name='permissions', blank=True),
),
],
options={
'verbose_name': 'group',
'verbose_name_plural': 'groups',
},
),
migrations.CreateModel(
name='User',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(default=timezone.now, verbose_name='last login')),
(
'is_superuser',
models.BooleanField(
default=False,
help_text=(
'Designates that this user has all permissions without explicitly assigning them.'
),
verbose_name='superuser status',
),
),
(
'username',
models.CharField(
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True,
max_length=30,
verbose_name='username',
validators=[
validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username.', 'invalid')
],
),
),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
(
'is_staff',
models.BooleanField(
default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status',
),
),
(
'is_active',
models.BooleanField(
default=True,
help_text=(
'Designates whether this user should be treated as active. Unselect this instead'
' of deleting accounts.'
),
verbose_name='active',
),
),
('date_joined', models.DateTimeField(default=timezone.now, verbose_name='date joined')),
(
'groups',
models.ManyToManyField(
to='auth.Group',
verbose_name='groups',
blank=True,
help_text=(
'The groups this user belongs to. A user will get all permissions granted to each'
' of his/her group.'
),
related_name='+',
related_query_name='+',
),
),
(
'user_permissions',
models.ManyToManyField(
to='auth.Permission',
verbose_name='user permissions',
blank=True,
help_text='Specific permissions for this user.',
related_name='+',
related_query_name='+',
),
),
],
options={
'verbose_name': 'user',
'verbose_name_plural': 'users',
},
),
]

View File

@ -1,30 +0,0 @@
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(
help_text=(
'Required, 255 characters or fewer. Only letters, numbers, and @, ., +, -, or _'
' characters.'
),
unique=True,
max_length=255,
verbose_name='username',
validators=[
django.core.validators.RegexValidator(
'^[\\w.@+-]+$', 'Enter a valid username.', 'invalid'
)
],
),
preserve_default=True,
),
]

View File

@ -1,13 +0,0 @@
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('auth', '0002_auto_20150323_1720'),
]
operations = [
migrations.DeleteModel('User'),
]

View File

@ -1,111 +0,0 @@
import django.core.validators
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0003_auto_20150410_1657'),
('authentic2', '__first__'),
('authentic2_idp_cas', '__first__'),
('saml', '__first__'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('password', models.CharField(max_length=128, verbose_name='password')),
(
'last_login',
models.DateTimeField(default=django.utils.timezone.now, verbose_name='last login'),
),
(
'is_superuser',
models.BooleanField(
default=False,
help_text=(
'Designates that this user has all permissions without explicitly assigning them.'
),
verbose_name='superuser status',
),
),
(
'username',
models.CharField(
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True,
max_length=30,
verbose_name='username',
validators=[
django.core.validators.RegexValidator(
'^[\\w.@+-]+$', 'Enter a valid username.', 'invalid'
)
],
),
),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=75, verbose_name='email address', blank=True)),
(
'is_staff',
models.BooleanField(
default=False,
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status',
),
),
(
'is_active',
models.BooleanField(
default=True,
help_text=(
'Designates whether this user should be treated as active. Unselect this instead'
' of deleting accounts.'
),
verbose_name='active',
),
),
(
'date_joined',
models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined'),
),
(
'groups',
models.ManyToManyField(
related_query_name='user',
related_name='user_set',
to='auth.Group',
blank=True,
help_text=(
'The groups this user belongs to. A user will get all permissions granted to each'
' of his/her group.'
),
verbose_name='groups',
),
),
(
'user_permissions',
models.ManyToManyField(
related_query_name='user',
related_name='user_set',
to='auth.Permission',
blank=True,
help_text='Specific permissions for this user.',
verbose_name='user permissions',
),
),
],
options={
'abstract': False,
'verbose_name': 'user',
'swappable': 'AUTH_USER_MODEL',
'verbose_name_plural': 'users',
},
bases=(models.Model,),
),
]

View File

@ -1,79 +0,0 @@
import django.contrib.auth.models
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0004_user'),
]
operations = [
migrations.AlterModelManagers(
name='group',
managers=[
('objects', django.contrib.auth.models.GroupManager()),
],
),
migrations.AlterModelManagers(
name='permission',
managers=[
('objects', django.contrib.auth.models.PermissionManager()),
],
),
migrations.AlterModelManagers(
name='user',
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.AlterField(
model_name='permission',
name='name',
field=models.CharField(max_length=255, verbose_name='name'),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=254, verbose_name='email address', blank=True),
),
migrations.AlterField(
model_name='user',
name='groups',
field=models.ManyToManyField(
related_query_name='user',
related_name='user_set',
to='auth.Group',
blank=True,
help_text=(
'The groups this user belongs to. A user will get all permissions granted to each of'
' their groups.'
),
verbose_name='groups',
),
),
migrations.AlterField(
model_name='user',
name='last_login',
field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
),
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(
error_messages={'unique': 'A user with that username already exists.'},
max_length=30,
validators=[
django.core.validators.RegexValidator(
'^[\\w.@+-]+$',
'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_'
' characters.',
'invalid',
)
],
help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.',
unique=True,
verbose_name='username',
),
),
]

View File

@ -5,80 +5,6 @@ import authentic2.utils.misc
import authentic2.validators
def noop(apps, schema_editor):
pass
def copy_old_users_to_custom_user_model(apps, schema_editor):
OldUser = apps.get_model('auth', 'User')
NewUser = apps.get_model('custom_user', 'User')
fields = [
'id',
'username',
'email',
'first_name',
'last_name',
'is_staff',
'is_active',
'date_joined',
'is_superuser',
'last_login',
'password',
]
old_users = OldUser.objects.prefetch_related('groups', 'user_permissions').order_by('id')
new_users = []
for old_user in old_users:
new_user = NewUser()
for field in fields:
setattr(new_user, field, getattr(old_user, field))
new_users.append(new_user)
# mass create of new users
NewUser.objects.bulk_create(new_users)
new_groups = []
new_permissions = []
GroupThrough = NewUser.groups.through
PermissionThrough = NewUser.user_permissions.through
new_users = NewUser.objects.order_by('id')
for old_user, new_user in zip(old_users, new_users):
assert old_user.id == new_user.id
for group in old_user.groups.all():
new_groups.append(GroupThrough(user_id=new_user.id, group_id=group.id))
for dummy in old_user.user_permissions.all():
new_permissions.append(PermissionThrough(user_id=new_user.id, group_id=group.id))
# mass create group and permission relations
GroupThrough.objects.bulk_create(new_groups)
PermissionThrough.objects.bulk_create(new_permissions)
# Reset sequences
if schema_editor.connection.vendor == 'postgresql':
schema_editor.execute(
'SELECT setval(pg_get_serial_sequence(\'"custom_user_user_groups"\',\'id\'), coalesce(max("id"),'
' 1), max("id") IS NOT null) FROM "custom_user_user_groups";'
)
schema_editor.execute(
'SELECT setval(pg_get_serial_sequence(\'"custom_user_user_user_permissions"\',\'id\'),'
' coalesce(max("id"), 1), max("id") IS NOT null) FROM "custom_user_user_user_permissions";'
)
schema_editor.execute(
'SELECT setval(pg_get_serial_sequence(\'"custom_user_user"\',\'id\'), coalesce(max("id"), 1),'
' max("id") IS NOT null) FROM "custom_user_user";'
)
elif schema_editor.connection.vendor == 'sqlite':
schema_editor.execute(
'UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM custom_user_user) WHERE name ='
' "custom_user_user";'
)
schema_editor.execute(
'UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM custom_user_user_groups) WHERE name ='
' "custom_user_user_groups";'
)
schema_editor.execute(
'UPDATE sqlite_sequence SET seq = (SELECT MAX(id) FROM custom_user_user_user_permissions) WHERE'
' name = "custom_user_user_permissions";'
)
else:
raise NotImplementedError()
class Migration(migrations.Migration):
dependencies = [
('auth', '__first__'),
@ -187,5 +113,4 @@ class Migration(migrations.Migration):
},
bases=(models.Model,),
),
migrations.RunPython(copy_old_users_to_custom_user_model, reverse_code=noop),
]

View File

@ -40,10 +40,6 @@ class Migration(migrations.Migration):
('admin', '__first__'),
]
run_before = [
('auth', '0003_auto_20150410_1657'),
]
operations = [
# Django admin log
ThirdPartyAlterField(

View File

@ -3,7 +3,8 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
('auth', '__first__'),
('custom_user', '__first__'),
('contenttypes', '0001_initial'),
]
@ -98,7 +99,10 @@ class Migration(migrations.Migration):
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('creation', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('user', models.ForeignKey(verbose_name='user', to='auth.User', on_delete=models.CASCADE)),
(
'user',
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
options={
'verbose_name': 'user to delete',
@ -183,7 +187,10 @@ class Migration(migrations.Migration):
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('user', models.ForeignKey(verbose_name='user', to='auth.User', on_delete=models.CASCADE)),
(
'user',
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
options={
'verbose_name': 'password reset',
@ -202,7 +209,10 @@ class Migration(migrations.Migration):
('external_id', models.CharField(max_length=256, verbose_name='external id')),
('created', models.DateTimeField(auto_now_add=True, verbose_name='creation date')),
('updated', models.DateTimeField(auto_now=True, verbose_name='last update date')),
('user', models.ForeignKey(verbose_name='user', to='auth.User', on_delete=models.CASCADE)),
(
'user',
models.ForeignKey(verbose_name='user', to='custom_user.User', on_delete=models.CASCADE),
),
],
options={
'verbose_name': 'user external id',

View File

@ -7,7 +7,7 @@ import authentic2.saml.models
class Migration(migrations.Migration):
dependencies = [
('auth', '__first__'),
('custom_user', '__first__'),
('idp', '__first__'),
('contenttypes', '0001_initial'),
]
@ -757,7 +757,7 @@ class Migration(migrations.Migration):
model_name='libertyfederation',
name='user',
field=models.ForeignKey(
on_delete=django.db.models.deletion.SET_NULL, blank=True, to='auth.User', null=True
on_delete=django.db.models.deletion.SET_NULL, blank=True, to='custom_user.User', null=True
),
preserve_default=True,
),

View File

@ -312,10 +312,6 @@ LOGGING = {
},
}
MIGRATION_MODULES = {
'auth': 'authentic2.auth_migrations_18',
}
# Django REST Framework
REST_FRAMEWORK = {
'NON_FIELD_ERRORS_KEY': '__all__',

View File

@ -5,7 +5,7 @@ import authentic2_idp_cas.models
class Migration(migrations.Migration):
dependencies = [
('auth', '0002_auto_20150323_1720'),
('custom_user', '__first__'),
]
operations = [
@ -128,7 +128,7 @@ class Migration(migrations.Migration):
'user',
models.ForeignKey(
blank=True,
to='auth.User',
to='custom_user.User',
max_length=128,
null=True,
verbose_name='user',