mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2026-02-10 17:16:22 +01:00
Restructure Alliance Auth package (#867)
* Refactor allianceauth into its own package * Add setup * Add missing default_app_config declarations * Fix timerboard namespacing * Remove obsolete future imports * Remove py2 mock support * Remove six * Add experimental 3.7 support and multiple Dj versions * Remove python_2_unicode_compatible * Add navhelper as local package * Update requirements
This commit is contained in:
52
allianceauth/authentication/migrations/0001_initial.py
Normal file
52
allianceauth/authentication/migrations/0001_initial.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-05 21:38
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AuthServicesInfo',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('ipboard_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('ipboard_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('xenforo_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('xenforo_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('forum_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('forum_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('jabber_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('jabber_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('mumble_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('mumble_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('teamspeak3_uid', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('teamspeak3_perm_key', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('discord_uid', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('discourse_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('discourse_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('ips4_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('ips4_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('ips4_id', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('smf_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('smf_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('market_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('market_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('pathfinder_username', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('pathfinder_password', models.CharField(blank=True, default=b'', max_length=254)),
|
||||
('main_char_id', models.CharField(blank=True, default=b'', max_length=64)),
|
||||
('is_blue', models.BooleanField(default=False)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-07 19:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='pathfinder_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='pathfinder_username',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 20:29
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0002_auto_20160907_1914'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='authservicesinfo',
|
||||
name='state',
|
||||
field=models.CharField(blank=True, choices=[(None, b'None'), (b'Blue', b'Blue'), (b'Member', b'Member')], default=None, max_length=10, null=True),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def create_permissions(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ct = ContentType.objects.get_for_model(User)
|
||||
Permission.objects.get_or_create(codename="member", content_type=ct, name="member")
|
||||
Permission.objects.get_or_create(codename="group_management", content_type=ct, name="group_management")
|
||||
Permission.objects.get_or_create(codename="jabber_broadcast", content_type=ct, name="jabber_broadcast")
|
||||
Permission.objects.get_or_create(codename="jabber_broadcast_all", content_type=ct, name="jabber_broadcast_all")
|
||||
Permission.objects.get_or_create(codename="fleetactivitytracking", content_type=ct, name="fleetactivitytracking")
|
||||
Permission.objects.get_or_create(codename="fleetactivitytracking_statistics", content_type=ct, name="fleetactivitytracking_statistics")
|
||||
Permission.objects.get_or_create(codename="human_resources", content_type=ct, name="human_resources")
|
||||
Permission.objects.get_or_create(codename="blue_member", content_type=ct, name="blue_member")
|
||||
Permission.objects.get_or_create(codename="alliance_apis", content_type=ct, name="alliance_apis")
|
||||
Permission.objects.get_or_create(codename="corp_apis", content_type=ct, name="corp_apis")
|
||||
Permission.objects.get_or_create(codename="timer_management", content_type=ct, name="timer_management")
|
||||
Permission.objects.get_or_create(codename="timer_view", content_type=ct, name="timer_view")
|
||||
Permission.objects.get_or_create(codename="srp_management", content_type=ct, name="srp_management")
|
||||
Permission.objects.get_or_create(codename="signature_management", content_type=ct, name="signature_management")
|
||||
Permission.objects.get_or_create(codename="signature_view", content_type=ct, name="signature_view")
|
||||
Permission.objects.get_or_create(codename="optimer_management", content_type=ct, name="optimer_management")
|
||||
Permission.objects.get_or_create(codename="optimer_view", content_type=ct, name="optimer_view")
|
||||
Permission.objects.get_or_create(codename="logging_notifications", content_type=ct, name="logging_notifications")
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
#too lazy
|
||||
pass
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0003_authservicesinfo_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_permissions, reverse)
|
||||
]
|
||||
31
allianceauth/authentication/migrations/0005_delete_perms.py
Normal file
31
allianceauth/authentication/migrations/0005_delete_perms.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def delete_permissions(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ct = ContentType.objects.get_for_model(User)
|
||||
Permission.objects.filter(content_type=ct).filter(codename__in=['member', 'blue_member', 'signature_management', 'signature_view']).delete()
|
||||
|
||||
def create_permissions(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ct = ContentType.objects.get_for_model(User)
|
||||
Permission.objects.get_or_create(codename="member", content_type=ct, name="member")
|
||||
Permission.objects.get_or_create(codename="blue_member", content_type=ct, name="blue_member")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0004_create_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(delete_permissions, create_permissions),
|
||||
]
|
||||
@@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-10 05:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0005_delete_perms'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='discourse_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='forum_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='ipboard_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='ips4_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='jabber_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='market_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='mumble_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='smf_password',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='xenforo_password',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-10 21:50
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0006_auto_20160910_0542'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='is_blue',
|
||||
),
|
||||
]
|
||||
17
allianceauth/authentication/migrations/0008_set_state.py
Normal file
17
allianceauth/authentication/migrations/0008_set_state.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-12 13:04
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0007_remove_authservicesinfo_is_blue'),
|
||||
('eveonline', '0001_initial'),
|
||||
('auth', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-10-21 02:28
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0008_set_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='discourse_username',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='authservicesinfo',
|
||||
name='discourse_enabled',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-07 06:47
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
def count_completed_fields(model):
|
||||
return len([True for key, value in model.__dict__.items() if bool(value)])
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
# this ensures only one model exists per user
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
users = set([a.user for a in AuthServicesInfo.objects.all()])
|
||||
for u in users:
|
||||
auths = AuthServicesInfo.objects.filter(user=u)
|
||||
if auths.count() > 1:
|
||||
pk = auths[0].pk
|
||||
largest = 0
|
||||
for auth in auths:
|
||||
completed = count_completed_fields(auth)
|
||||
if completed > largest:
|
||||
largest = completed
|
||||
pk = auth.pk
|
||||
auths.exclude(pk=pk).delete()
|
||||
|
||||
# ensure all users have a model
|
||||
User = apps.get_model('auth', 'User')
|
||||
for u in User.objects.exclude(pk__in=[user.pk for user in users]):
|
||||
AuthServicesInfo.objects.create(user=u)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0009_auto_20161021_0228'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward, migrations.RunPython.noop)
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2017-01-07 07:11
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0010_only_one_authservicesinfo'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='authservicesinfo',
|
||||
name='user',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-01-12 00:59
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
def remove_permissions(apps, schema_editor):
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
|
||||
# delete the add and remove permissions for AuthServicesInfo
|
||||
ct = ContentType.objects.get_for_model(AuthServicesInfo)
|
||||
Permission.objects.filter(content_type=ct).filter(codename__in=['add_authservicesinfo', 'delete_authservicesinfo']).delete()
|
||||
|
||||
|
||||
def add_permissions(apps, schema_editor):
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
|
||||
# recreate the add and remove permissions for AuthServicesInfo
|
||||
ct = ContentType.objects.get_for_model(AuthServicesInfo)
|
||||
Permission.objects.create(content_type=ct, codename='add_authservicesinfo', name='Can add auth services info')
|
||||
Permission.objects.create(content_type=ct, codename='delete_authservicesinfo', name='Can delete auth services info')
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0011_authservicesinfo_user_onetoonefield'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='authservicesinfo',
|
||||
options={'default_permissions': ('change',)},
|
||||
),
|
||||
migrations.RunPython(remove_permissions, add_permissions),
|
||||
]
|
||||
321
allianceauth/authentication/migrations/0013_service_modules.py
Normal file
321
allianceauth/authentication/migrations/0013_service_modules.py
Normal file
@@ -0,0 +1,321 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.2 on 2016-12-11 23:14
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def optional_dependencies():
|
||||
"""
|
||||
Only require these migrations if the given app
|
||||
is installed. If the app isn't installed then
|
||||
the relevant AuthServicesInfo field will be LOST
|
||||
when the data migration is run.
|
||||
"""
|
||||
installed_apps = settings.INSTALLED_APPS
|
||||
|
||||
dependencies = []
|
||||
|
||||
# Skip adding module dependencies if the settings specifies that services have been migrated
|
||||
if not getattr(settings, 'SERVICES_MIGRATED', False):
|
||||
if 'services.modules.xenforo' in installed_apps:
|
||||
dependencies.append(('xenforo', '0001_initial'))
|
||||
if 'services.modules.discord' in installed_apps:
|
||||
dependencies.append(('discord', '0001_initial'))
|
||||
if 'services.modules.discourse' in installed_apps:
|
||||
dependencies.append(('discourse', '0001_initial'))
|
||||
if 'services.modules.ipboard' in installed_apps:
|
||||
dependencies.append(('ipboard', '0001_initial'))
|
||||
if 'services.modules.ips4' in installed_apps:
|
||||
dependencies.append(('ips4', '0001_initial'))
|
||||
if 'services.modules.market' in installed_apps:
|
||||
dependencies.append(('market', '0001_initial'))
|
||||
if 'services.modules.openfire' in installed_apps:
|
||||
dependencies.append(('openfire', '0001_initial'))
|
||||
if 'services.modules.smf' in installed_apps:
|
||||
dependencies.append(('smf', '0001_initial'))
|
||||
if 'services.modules.teamspeak3' in installed_apps:
|
||||
dependencies.append(('teamspeak3', '0003_teamspeak3user'))
|
||||
if 'services.modules.mumble' in installed_apps:
|
||||
dependencies.append(('mumble', '0003_mumbleuser_user'))
|
||||
if 'services.modules.phpbb3' in installed_apps:
|
||||
dependencies.append(('phpbb3', '0001_initial'))
|
||||
|
||||
return dependencies
|
||||
|
||||
|
||||
class DatalossException(Exception):
|
||||
def __init__(self, field, app):
|
||||
message = "This migration would cause a loss of data for the %s field, ensure the %s app is installed and" \
|
||||
" run the migration again" % (field, app)
|
||||
super(Exception, self).__init__(message)
|
||||
|
||||
|
||||
def check_for_dataloss(authservicesinfo):
|
||||
"""
|
||||
Check if any of the authservicesinfo contain a field which contains data
|
||||
that would be lost because the target module for migration is not installed.
|
||||
If a record is found with no matching target installed an exception is raised.
|
||||
:param authservicesinfo: AuthServicesInfo records to check
|
||||
"""
|
||||
installed_apps = settings.INSTALLED_APPS
|
||||
|
||||
for authinfo in authservicesinfo:
|
||||
if authinfo.xenforo_username and 'services.modules.xenforo' not in installed_apps:
|
||||
raise DatalossException('xenforo_username', 'services.modules.xenforo')
|
||||
if authinfo.discord_uid and 'services.modules.discord' not in installed_apps:
|
||||
raise DatalossException('discord_uid', 'services.modules.discord')
|
||||
if authinfo.discourse_enabled and 'services.modules.discourse' not in installed_apps:
|
||||
raise DatalossException('discourse_enabled', 'services.modules.discourse')
|
||||
if authinfo.ipboard_username and 'services.modules.ipboard' not in installed_apps:
|
||||
raise DatalossException('ipboard_username', 'services.modules.ipboard')
|
||||
if authinfo.ips4_id and 'services.modules.ips4' not in installed_apps:
|
||||
raise DatalossException('ips4_id', 'services.modules.ips4')
|
||||
if authinfo.market_username and 'services.modules.market' not in installed_apps:
|
||||
raise DatalossException('market_username', 'services.modules.market')
|
||||
if authinfo.jabber_username and 'services.modules.openfire' not in installed_apps:
|
||||
raise DatalossException('jabber_username', 'services.modules.openfire')
|
||||
if authinfo.smf_username and 'services.modules.smf' not in installed_apps:
|
||||
raise DatalossException('smf_username', 'services.modules.smf')
|
||||
if authinfo.teamspeak3_uid and 'services.modules.teamspeak3' not in installed_apps:
|
||||
raise DatalossException('teamspeak3_uid', 'services.modules.teamspeak3')
|
||||
if authinfo.mumble_username and 'services.modules.mumble' not in installed_apps:
|
||||
raise DatalossException('mumble_username', 'services.modules.mumble')
|
||||
if authinfo.forum_username and 'services.modules.phpbb3' not in installed_apps:
|
||||
raise DatalossException('forum_username', 'services.modules.phpbb3')
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
installed_apps = settings.INSTALLED_APPS
|
||||
AuthServicesInfo = apps.get_model("authentication", "AuthServicesInfo")
|
||||
|
||||
# Check if any records would result in dataloss
|
||||
check_for_dataloss(AuthServicesInfo.objects.all())
|
||||
|
||||
XenforoUser = apps.get_model('xenforo', 'XenforoUser') if 'services.modules.xenforo' in installed_apps else None
|
||||
DiscordUser = apps.get_model('discord', 'DiscordUser') if 'services.modules.discord' in installed_apps else None
|
||||
DiscourseUser = apps.get_model('discourse', 'DiscourseUser') if 'services.modules.discourse' in installed_apps else None
|
||||
IpboardUser = apps.get_model('ipboard', 'IpboardUser') if 'services.modules.ipboard' in installed_apps else None
|
||||
Ips4User = apps.get_model('ips4', 'Ips4User') if 'services.modules.ips4' in installed_apps else None
|
||||
MarketUser = apps.get_model('market', 'MarketUser') if 'services.modules.market' in installed_apps else None
|
||||
OpenfireUser = apps.get_model('openfire', 'OpenfireUser') if 'services.modules.openfire' in installed_apps else None
|
||||
SmfUser = apps.get_model('smf', 'SmfUser') if 'services.modules.smf' in installed_apps else None
|
||||
Teamspeak3User = apps.get_model('teamspeak3', 'Teamspeak3User') if 'services.modules.teamspeak3' in installed_apps else None
|
||||
MumbleUser = apps.get_model('mumble', 'MumbleUser') if 'services.modules.mumble' in installed_apps else None
|
||||
Phpbb3User = apps.get_model('phpbb3', 'Phpbb3User') if 'services.modules.phpbb3' in installed_apps else None
|
||||
|
||||
for authinfo in AuthServicesInfo.objects.all():
|
||||
user = authinfo.user
|
||||
|
||||
if XenforoUser is not None and authinfo.xenforo_username:
|
||||
logging.debug('Updating Xenforo info for %s' % user.username)
|
||||
xfu = XenforoUser()
|
||||
xfu.user = user
|
||||
xfu.username = authinfo.xenforo_username
|
||||
xfu.save()
|
||||
|
||||
if DiscordUser is not None and authinfo.discord_uid:
|
||||
logging.debug('Updating Discord info for %s' % user.username)
|
||||
du = DiscordUser()
|
||||
du.user = user
|
||||
du.uid = authinfo.discord_uid
|
||||
du.save()
|
||||
|
||||
if DiscourseUser is not None and authinfo.discourse_enabled:
|
||||
logging.debug('Updating Discourse info for %s' % user.username)
|
||||
du = DiscourseUser()
|
||||
du.user = user
|
||||
du.enabled = authinfo.discourse_enabled
|
||||
du.save()
|
||||
|
||||
if IpboardUser is not None and authinfo.ipboard_username:
|
||||
logging.debug('Updating IPBoard info for %s' % user.username)
|
||||
ipb = IpboardUser()
|
||||
ipb.user = user
|
||||
ipb.username = authinfo.ipboard_username
|
||||
ipb.save()
|
||||
|
||||
if Ips4User is not None and authinfo.ips4_id:
|
||||
logging.debug('Updating Ips4 info for %s' % user.username)
|
||||
ips = Ips4User()
|
||||
ips.user = user
|
||||
ips.id = authinfo.ips4_id
|
||||
ips.username = authinfo.ips4_username
|
||||
ips.save()
|
||||
|
||||
if MarketUser is not None and authinfo.market_username:
|
||||
logging.debug('Updating Market info for %s' % user.username)
|
||||
mkt = MarketUser()
|
||||
mkt.user = user
|
||||
mkt.username = authinfo.market_username
|
||||
mkt.save()
|
||||
|
||||
if OpenfireUser is not None and authinfo.jabber_username:
|
||||
logging.debug('Updating Openfire (jabber) info for %s' % user.username)
|
||||
ofu = OpenfireUser()
|
||||
ofu.user = user
|
||||
ofu.username = authinfo.jabber_username
|
||||
ofu.save()
|
||||
|
||||
if SmfUser is not None and authinfo.smf_username:
|
||||
logging.debug('Updating SMF info for %s' % user.username)
|
||||
smf = SmfUser()
|
||||
smf.user = user
|
||||
smf.username = authinfo.smf_username
|
||||
smf.save()
|
||||
|
||||
if Teamspeak3User is not None and authinfo.teamspeak3_uid:
|
||||
logging.debug('Updating Teamspeak3 info for %s' % user.username)
|
||||
ts3 = Teamspeak3User()
|
||||
ts3.user = user
|
||||
ts3.uid = authinfo.teamspeak3_uid
|
||||
ts3.perm_key = authinfo.teamspeak3_perm_key
|
||||
ts3.save()
|
||||
|
||||
if MumbleUser is not None and authinfo.mumble_username:
|
||||
logging.debug('Updating mumble info for %s' % user.username)
|
||||
try:
|
||||
mbl = MumbleUser.objects.get(username=authinfo.mumble_username)
|
||||
mbl.user = user
|
||||
mbl.save()
|
||||
except ObjectDoesNotExist:
|
||||
logger.warn('AuthServiceInfo mumble_username for {} but no '
|
||||
'corresponding record in MumbleUser, dropping'.format(user.username))
|
||||
|
||||
if Phpbb3User is not None and authinfo.forum_username:
|
||||
logging.debug('Updating phpbb3 info for %s' % user.username)
|
||||
phb = Phpbb3User()
|
||||
phb.user = user
|
||||
phb.username = authinfo.forum_username
|
||||
phb.save()
|
||||
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
AuthServicesInfo = apps.get_model("authentication", "AuthServicesInfo")
|
||||
|
||||
for user in User.objects.all():
|
||||
authinfo, c = AuthServicesInfo.objects.get_or_create(user=user)
|
||||
|
||||
if hasattr(user, 'xenforo'):
|
||||
logging.debug('Reversing xenforo for %s' % user.username)
|
||||
authinfo.xenforo_username = user.xenforo.username
|
||||
|
||||
if hasattr(user, 'discord'):
|
||||
logging.debug('Reversing discord for %s' % user.username)
|
||||
authinfo.discord_uid = user.discord.uid
|
||||
|
||||
if hasattr(user, 'discourse'):
|
||||
logging.debug('Reversing discourse for %s' % user.username)
|
||||
authinfo.discourse_enabled = user.discourse.enabled
|
||||
|
||||
if hasattr(user, 'ipboard'):
|
||||
logging.debug('Reversing ipboard for %s' % user.username)
|
||||
authinfo.ipboard_username = user.ipboard.username
|
||||
|
||||
if hasattr(user, 'ips4'):
|
||||
logging.debug('Reversing ips4 for %s' % user.username)
|
||||
authinfo.ips4_id = user.ips4.id
|
||||
authinfo.ips4_username = user.ips4.username
|
||||
|
||||
if hasattr(user, 'market'):
|
||||
logging.debug('Reversing market for %s' % user.username)
|
||||
authinfo.market_username = user.market.username
|
||||
|
||||
if hasattr(user, 'openfire'):
|
||||
logging.debug('Reversing openfire (jabber) for %s' % user.username)
|
||||
authinfo.jabber_username = user.openfire.username
|
||||
|
||||
if hasattr(user, 'smf'):
|
||||
logging.debug('Reversing smf for %s' % user.username)
|
||||
authinfo.smf_username = user.smf.username
|
||||
|
||||
if hasattr(user, 'teamspeak3'):
|
||||
logging.debug('Reversing teamspeak3 for %s' % user.username)
|
||||
authinfo.teamspeak3_uid = user.teamspeak3.uid
|
||||
authinfo.teamspeak3_perm_key = user.teamspeak3.perm_key
|
||||
|
||||
if hasattr(user, 'mumble'):
|
||||
logging.debug('Reversing mumble for %s' % user.username)
|
||||
try:
|
||||
authinfo.mumble_username = user.mumble.all()[:1].get().username
|
||||
except ObjectDoesNotExist:
|
||||
logging.debug('Failed to reverse mumble for %s' % user.username)
|
||||
|
||||
if hasattr(user, 'phpbb3'):
|
||||
logging.debug('Reversing phpbb3 for %s' % user.username)
|
||||
authinfo.forum_username = user.phpbb3.username
|
||||
|
||||
logging.debug('Saving AuthServicesInfo for %s ' % user.username)
|
||||
authinfo.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = optional_dependencies() + [
|
||||
('authentication', '0012_remove_add_delete_authservicesinfo_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Migrate data
|
||||
migrations.RunPython(forward, reverse),
|
||||
# Remove fields
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='discord_uid',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='discourse_enabled',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='forum_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='ipboard_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='ips4_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='ips4_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='jabber_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='market_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='mumble_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='smf_username',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='teamspeak3_perm_key',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='teamspeak3_uid',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='xenforo_username',
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.1 on 2016-09-09 23:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_permission(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
Permission = apps.get_model('auth', 'Permission')
|
||||
ct = ContentType.objects.get_for_model(User)
|
||||
Permission.objects.get_or_create(codename="view_fleetup", content_type=ct, name="view_fleetup")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentication', '0013_service_modules'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_permission, migrations.RunPython.noop)
|
||||
]
|
||||
263
allianceauth/authentication/migrations/0015_user_profiles.py
Normal file
263
allianceauth/authentication/migrations/0015_user_profiles.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.5 on 2017-03-22 23:09
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import allianceauth.authentication.models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def create_guest_state(apps, schema_editor):
|
||||
State = apps.get_model('authentication', 'State')
|
||||
State.objects.update_or_create(name='Guest', defaults={'priority': 0, 'public': True})
|
||||
|
||||
|
||||
def create_member_state(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
|
||||
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
|
||||
s = State.objects.update_or_create(name=member_state_name, defaults={'priority': 100, 'public': False})[0]
|
||||
try:
|
||||
# move group permissions to state
|
||||
g = Group.objects.get(name=member_state_name)
|
||||
[s.permissions.add(p.pk) for p in g.permissions.all()]
|
||||
g.delete()
|
||||
except Group.DoesNotExist:
|
||||
pass
|
||||
|
||||
# auto-populate member IDs
|
||||
CORP_IDS = getattr(settings, 'CORP_IDS', [])
|
||||
ALLIANCE_IDS = getattr(settings, 'ALLIANCE_IDS', [])
|
||||
[s.member_corporations.add(c.pk) for c in EveCorporationInfo.objects.filter(corporation_id__in=CORP_IDS)]
|
||||
[s.member_alliances.add(a.pk) for a in EveAllianceInfo.objects.filter(alliance_id__in=ALLIANCE_IDS)]
|
||||
|
||||
|
||||
def create_member_group(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
member_state_name = getattr(settings, 'DEFAULT_AUTH_GROUP', 'Member')
|
||||
|
||||
try:
|
||||
g = Group.objects.get(name=member_state_name)
|
||||
# move permissions back
|
||||
state = State.objects.get(name=member_state_name)
|
||||
[g.permissions.add(p.pk) for p in state.permissions.all()]
|
||||
|
||||
# move users back
|
||||
for profile in state.userprofile_set.all().select_related('user'):
|
||||
profile.user.groups.add(g.pk)
|
||||
except (Group.DoesNotExist, State.DoesNotExist):
|
||||
pass
|
||||
|
||||
|
||||
def create_blue_state(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
EveAllianceInfo = apps.get_model('eveonline', 'EveAllianceInfo')
|
||||
EveCorporationInfo = apps.get_model('eveonline', 'EveCorporationInfo')
|
||||
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
|
||||
|
||||
s = State.objects.update_or_create(name=blue_state_name, defaults={'priority': 50, 'public': False})[0]
|
||||
try:
|
||||
# move group permissions to state
|
||||
g = Group.objects.get(name=blue_state_name)
|
||||
[s.permissions.add(p.pk) for p in g.permissions.all()]
|
||||
g.permissions.clear()
|
||||
except Group.DoesNotExist:
|
||||
pass
|
||||
|
||||
# auto-populate blue member IDs
|
||||
BLUE_CORP_IDS = getattr(settings, 'BLUE_CORP_IDS', [])
|
||||
BLUE_ALLIANCE_IDS = getattr(settings, 'BLUE_ALLIANCE_IDS', [])
|
||||
[s.member_corporations.add(c.pk) for c in EveCorporationInfo.objects.filter(corporation_id__in=BLUE_CORP_IDS)]
|
||||
[s.member_alliances.add(a.pk) for a in EveAllianceInfo.objects.filter(alliance_id__in=BLUE_ALLIANCE_IDS)]
|
||||
|
||||
|
||||
def create_blue_group(apps, schema_editor):
|
||||
Group = apps.get_model('auth', 'Group')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
blue_state_name = getattr(settings, 'DEFAULT_BLUE_GROUP', 'Blue')
|
||||
|
||||
try:
|
||||
g = Group.objects.get(name=blue_state_name)
|
||||
# move permissions back
|
||||
state = State.objects.get(name=blue_state_name)
|
||||
[g.permissions.add(p.pk) for p in state.permissions.all()]
|
||||
|
||||
# move users back
|
||||
for profile in state.userprofile_set.all().select_related('user'):
|
||||
profile.user.groups.add(g.pk)
|
||||
except (Group.DoesNotExist, State.DoesNotExist):
|
||||
pass
|
||||
|
||||
|
||||
def populate_ownerships(apps, schema_editor):
|
||||
Token = apps.get_model('esi', 'Token')
|
||||
CharacterOwnership = apps.get_model('authentication', 'CharacterOwnership')
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
|
||||
unique_character_owners = [t['character_id'] for t in
|
||||
Token.objects.all().values('character_id').annotate(n=models.Count('user')) if
|
||||
t['n'] == 1 and EveCharacter.objects.filter(character_id=t['character_id'].exists())]
|
||||
|
||||
tokens = Token.objects.filter(character_id__in=unique_character_owners)
|
||||
for c_id in unique_character_owners:
|
||||
ts = tokens.filter(character_id=c_id).order_by('created')
|
||||
for t in ts:
|
||||
if t.can_refresh:
|
||||
# find newest refreshable token and use it as basis for CharacterOwnership
|
||||
CharacterOwnership.objecs.create_by_token(t)
|
||||
break
|
||||
|
||||
|
||||
def create_profiles(apps, schema_editor):
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
State = apps.get_model('authentication', 'State')
|
||||
UserProfile = apps.get_model('authentication', 'UserProfile')
|
||||
EveCharacter = apps.get_model('eveonline', 'EveCharacter')
|
||||
|
||||
# grab AuthServicesInfo if they have a unique main_char_id and the EveCharacter exists
|
||||
unique_mains = [auth['main_char_id'] for auth in
|
||||
AuthServicesInfo.objects.exclude(main_char_id='').values('main_char_id').annotate(
|
||||
n=models.Count('main_char_id')) if
|
||||
auth['n'] == 1 and EveCharacter.objects.filter(character_id=auth['main_char_id']).exists()]
|
||||
|
||||
auths = AuthServicesInfo.objects.filter(main_char_id__in=unique_mains).select_related('user')
|
||||
for auth in auths:
|
||||
# carry states and mains forward
|
||||
state = State.objects.get(name=auth.state if auth.state else 'Guest')
|
||||
char = EveCharacter.objects.get(character_id=auth.main_char_id)
|
||||
UserProfile.objects.create(user=auth.user, state=state, main_character=char)
|
||||
for auth in AuthServicesInfo.objects.exclude(main_char_id__in=unique_mains).select_related('user'):
|
||||
# prepare empty profiles
|
||||
state = State.objects.get(name='Guest')
|
||||
UserProfile.objects.create(user=auth.user, state=state)
|
||||
|
||||
|
||||
def recreate_authservicesinfo(apps, schema_editor):
|
||||
AuthServicesInfo = apps.get_model('authentication', 'AuthServicesInfo')
|
||||
UserProfile = apps.get_model('authentication', 'UserProfile')
|
||||
User = apps.get_model('auth', 'User')
|
||||
|
||||
# recreate all missing AuthServicesInfo models
|
||||
AuthServicesInfo.objects.bulk_create([AuthServicesInfo(user=u.pk) for u in User.objects.all()])
|
||||
|
||||
# repopulate main characters
|
||||
for profile in UserProfile.objects.exclude(main_character__isnull=True).select_related('user', 'main_character'):
|
||||
AuthServicesInfo.objects.update_or_create(user=profile.user,
|
||||
defaults={'main_char_id': profile.main_character.character_id})
|
||||
|
||||
# repopulate states we understand
|
||||
for profile in UserProfile.objects.exclude(state__name='Guest').filter(
|
||||
state__name__in=['Member', 'Blue']).select_related('user', 'state'):
|
||||
AuthServicesInfo.objects.update_or_create(user=profile.user, defaults={'state': profile.state.name})
|
||||
|
||||
|
||||
def disable_passwords(apps, schema_editor):
|
||||
User = apps.get_model('auth', 'User')
|
||||
for u in User.objects.exclude(is_staff=True):
|
||||
# remove passwords for non-staff users to prevent password-based authentication
|
||||
# set_unusable_password is unavailable in migrations because :reasons:
|
||||
u.password = make_password(None)
|
||||
u.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('auth', '0008_alter_user_username_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('eveonline', '0008_remove_apikeys'),
|
||||
('authentication', '0014_fleetup_permission'),
|
||||
('esi', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CharacterOwnership',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('owner_hash', models.CharField(max_length=28, unique=True)),
|
||||
('character', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='character_ownership', to='eveonline.EveCharacter')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='character_ownerships', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('change', 'delete'),
|
||||
'ordering': ['user', 'character__character_name'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='State',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=20, unique=True)),
|
||||
('priority', models.IntegerField(help_text='Users get assigned the state with the highest priority available to them.', unique=True)),
|
||||
('public', models.BooleanField(default=False, help_text='Make this state available to any character.')),
|
||||
('member_alliances', models.ManyToManyField(blank=True, help_text='Alliances to whose members this state is available.', to='eveonline.EveAllianceInfo')),
|
||||
('member_characters', models.ManyToManyField(blank=True, help_text='Characters to which this state is available.', to='eveonline.EveCharacter')),
|
||||
('member_corporations', models.ManyToManyField(blank=True, help_text='Corporations to whose members this state is available.', to='eveonline.EveCorporationInfo')),
|
||||
('permissions', models.ManyToManyField(blank=True, to='auth.Permission')),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('change',),
|
||||
'ordering': ['-priority'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserProfile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('main_character', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='eveonline.EveCharacter')),
|
||||
('state', models.ForeignKey(default=allianceauth.authentication.models.get_guest_state_pk, on_delete=django.db.models.deletion.SET_DEFAULT, to='authentication.State')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'default_permissions': ('change',),
|
||||
},
|
||||
),
|
||||
migrations.RunPython(create_guest_state, migrations.RunPython.noop),
|
||||
migrations.RunPython(create_member_state, create_member_group),
|
||||
migrations.RunPython(create_blue_state, create_blue_group),
|
||||
migrations.RunPython(populate_ownerships, migrations.RunPython.noop),
|
||||
migrations.RunPython(create_profiles, recreate_authservicesinfo),
|
||||
migrations.RemoveField(
|
||||
model_name='authservicesinfo',
|
||||
name='user',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AuthServicesInfo',
|
||||
),
|
||||
migrations.RunPython(disable_passwords, migrations.RunPython.noop),
|
||||
migrations.CreateModel(
|
||||
name='ProxyPermission',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'verbose_name': 'permission',
|
||||
'verbose_name_plural': 'permissions',
|
||||
},
|
||||
bases=('auth.permission',),
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.PermissionManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProxyUser',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
},
|
||||
bases=('auth.user',),
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
allianceauth/authentication/migrations/__init__.py
Normal file
0
allianceauth/authentication/migrations/__init__.py
Normal file
Reference in New Issue
Block a user