mirror of https://github.com/jumpserver/jumpserver
767 lines
28 KiB
Python
767 lines
28 KiB
Python
# Generated by Django 4.1.13 on 2025-02-24 03:45
|
|
|
|
import uuid
|
|
from datetime import timedelta as dt_timedelta
|
|
|
|
import django.db.models.deletion
|
|
import private_storage.fields
|
|
import private_storage.storage.files
|
|
from django.conf import settings
|
|
from django.db import migrations, models
|
|
|
|
import common.db.fields
|
|
import common.db.utils
|
|
|
|
|
|
def migrate_account_backup(apps, schema_editor):
|
|
backup_id_old_new_map = migrate_backup_account_automation(apps)
|
|
migrate_automation_execution(apps, backup_id_old_new_map)
|
|
|
|
|
|
def migrate_automation_execution(apps, backup_id_old_new_map):
|
|
try:
|
|
old_execution_model = apps.get_model('accounts', 'AccountBackupExecution')
|
|
except LookupError:
|
|
return
|
|
backup_execution_model = apps.get_model('accounts', 'AutomationExecution')
|
|
for execution in old_execution_model.objects.all():
|
|
automation_id = backup_id_old_new_map.get(str(execution.plan_id))
|
|
if not automation_id:
|
|
continue
|
|
data = {
|
|
'automation_id': automation_id,
|
|
'date_start': execution.date_start,
|
|
'duration': int(execution.timedelta),
|
|
'date_finished': execution.date_start + dt_timedelta(seconds=int(execution.timedelta)),
|
|
'snapshot': execution.snapshot,
|
|
'trigger': execution.trigger,
|
|
'status': 'error' if execution.reason == '-' else 'success',
|
|
'org_id': execution.org_id
|
|
}
|
|
backup_execution_model.objects.create(**data)
|
|
|
|
|
|
def migrate_backup_account_automation(apps):
|
|
try:
|
|
old_backup_model = apps.get_model('accounts', 'AccountBackupAutomation')
|
|
except LookupError:
|
|
return
|
|
account_backup_model = apps.get_model('accounts', 'BackupAccountAutomation')
|
|
backup_id_old_new_map = {}
|
|
for backup in old_backup_model.objects.all():
|
|
data = {
|
|
'comment': backup.comment,
|
|
'created_by': backup.created_by,
|
|
'updated_by': backup.updated_by,
|
|
'date_created': backup.date_created,
|
|
'date_updated': backup.date_updated,
|
|
'name': backup.name,
|
|
'interval': backup.interval,
|
|
'crontab': backup.crontab,
|
|
'is_periodic': backup.is_periodic,
|
|
'start_time': backup.start_time,
|
|
'date_last_run': backup.date_last_run,
|
|
'org_id': backup.org_id,
|
|
'type': 'backup_account',
|
|
'types': backup.types,
|
|
'backup_type': backup.backup_type,
|
|
'is_password_divided_by_email': backup.is_password_divided_by_email,
|
|
'is_password_divided_by_obj_storage': backup.is_password_divided_by_obj_storage,
|
|
'zip_encrypt_password': backup.zip_encrypt_password
|
|
}
|
|
obj = account_backup_model.objects.create(**data)
|
|
backup_id_old_new_map[str(backup.id)] = str(obj.id)
|
|
obj.recipients_part_one.set(backup.recipients_part_one.all())
|
|
obj.recipients_part_two.set(backup.recipients_part_two.all())
|
|
obj.obj_recipients_part_one.set(backup.obj_recipients_part_one.all())
|
|
obj.obj_recipients_part_two.set(backup.obj_recipients_part_two.all())
|
|
return backup_id_old_new_map
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
dependencies = [
|
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
("assets", "0012_auto_20241204_1516"),
|
|
("terminal", "0005_endpoint_vnc_port"),
|
|
("accounts", "0004_alter_changesecretrecord_account_and_more"),
|
|
]
|
|
|
|
operations = [
|
|
migrations.CreateModel(
|
|
name="AccountRisk",
|
|
fields=[
|
|
(
|
|
"created_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Created by"
|
|
),
|
|
),
|
|
(
|
|
"updated_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Updated by"
|
|
),
|
|
),
|
|
(
|
|
"date_created",
|
|
models.DateTimeField(
|
|
auto_now_add=True, null=True, verbose_name="Date created"
|
|
),
|
|
),
|
|
(
|
|
"date_updated",
|
|
models.DateTimeField(auto_now=True, verbose_name="Date updated"),
|
|
),
|
|
(
|
|
"comment",
|
|
models.TextField(blank=True, default="", verbose_name="Comment"),
|
|
),
|
|
(
|
|
"id",
|
|
models.UUIDField(
|
|
default=uuid.uuid4, primary_key=True, serialize=False
|
|
),
|
|
),
|
|
(
|
|
"org_id",
|
|
models.CharField(
|
|
blank=True,
|
|
db_index=True,
|
|
default="",
|
|
max_length=36,
|
|
verbose_name="Organization",
|
|
),
|
|
),
|
|
("username", models.CharField(max_length=32, verbose_name="Username")),
|
|
(
|
|
"risk",
|
|
models.CharField(
|
|
choices=[
|
|
("long_time_no_login", "Long time no login"),
|
|
("new_found", "New found"),
|
|
("groups_changed", "Groups change"),
|
|
("sudoers_changed", "Sudo changed"),
|
|
("authorized_keys_changed", "Authorized keys changed"),
|
|
("account_deleted", "Account delete"),
|
|
("password_expired", "Password expired"),
|
|
("long_time_password", "Long time no change"),
|
|
("weak_password", "Weak password"),
|
|
("leaked_password", "Leaked password"),
|
|
("repeated_password", "Repeated password"),
|
|
("password_error", "Password error"),
|
|
("no_admin_account", "No admin account"),
|
|
("others", "Others"),
|
|
],
|
|
max_length=128,
|
|
verbose_name="Risk",
|
|
),
|
|
),
|
|
(
|
|
"status",
|
|
models.CharField(
|
|
blank=True,
|
|
choices=[
|
|
("0", "Pending"),
|
|
("1", "Confirmed"),
|
|
("2", "Ignored"),
|
|
],
|
|
default="0",
|
|
max_length=32,
|
|
verbose_name="Status",
|
|
),
|
|
),
|
|
("details", models.JSONField(default=list, verbose_name="Details")),
|
|
],
|
|
options={
|
|
"verbose_name": "Account risk",
|
|
},
|
|
),
|
|
migrations.CreateModel(
|
|
name="BackupAccountAutomation",
|
|
fields=[
|
|
(
|
|
"baseautomation_ptr",
|
|
models.OneToOneField(
|
|
auto_created=True,
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
parent_link=True,
|
|
primary_key=True,
|
|
serialize=False,
|
|
to="assets.baseautomation",
|
|
),
|
|
),
|
|
("types", models.JSONField(default=list)),
|
|
(
|
|
"backup_type",
|
|
models.CharField(
|
|
choices=[("email", "Email"), ("object_storage", "SFTP")],
|
|
default="email",
|
|
max_length=128,
|
|
verbose_name="Backup type",
|
|
),
|
|
),
|
|
(
|
|
"is_password_divided_by_email",
|
|
models.BooleanField(default=True, verbose_name="Password divided"),
|
|
),
|
|
(
|
|
"is_password_divided_by_obj_storage",
|
|
models.BooleanField(default=True, verbose_name="Password divided"),
|
|
),
|
|
(
|
|
"zip_encrypt_password",
|
|
common.db.fields.EncryptCharField(
|
|
blank=True,
|
|
max_length=4096,
|
|
null=True,
|
|
verbose_name="Zip encrypt password",
|
|
),
|
|
),
|
|
(
|
|
"obj_recipients_part_one",
|
|
models.ManyToManyField(
|
|
blank=True,
|
|
related_name="obj_recipient_part_one_plans",
|
|
to="terminal.replaystorage",
|
|
verbose_name="Object storage recipient part one",
|
|
),
|
|
),
|
|
(
|
|
"obj_recipients_part_two",
|
|
models.ManyToManyField(
|
|
blank=True,
|
|
related_name="obj_recipient_part_two_plans",
|
|
to="terminal.replaystorage",
|
|
verbose_name="Object storage recipient part two",
|
|
),
|
|
),
|
|
(
|
|
"recipients_part_one",
|
|
models.ManyToManyField(
|
|
blank=True,
|
|
related_name="recipient_part_one_plans",
|
|
to=settings.AUTH_USER_MODEL,
|
|
verbose_name="Recipient part one",
|
|
),
|
|
),
|
|
(
|
|
"recipients_part_two",
|
|
models.ManyToManyField(
|
|
blank=True,
|
|
related_name="recipient_part_two_plans",
|
|
to=settings.AUTH_USER_MODEL,
|
|
verbose_name="Recipient part two",
|
|
),
|
|
),
|
|
],
|
|
options={
|
|
"verbose_name": "Account backup plan",
|
|
},
|
|
bases=("accounts.accountbaseautomation",),
|
|
),
|
|
migrations.CreateModel(
|
|
name="CheckAccountAutomation",
|
|
fields=[
|
|
(
|
|
"baseautomation_ptr",
|
|
models.OneToOneField(
|
|
auto_created=True,
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
parent_link=True,
|
|
primary_key=True,
|
|
serialize=False,
|
|
to="assets.baseautomation",
|
|
),
|
|
),
|
|
("engines", models.JSONField(default=list, verbose_name="Engines")),
|
|
(
|
|
"recipients",
|
|
models.ManyToManyField(
|
|
blank=True,
|
|
to=settings.AUTH_USER_MODEL,
|
|
verbose_name="Recipient",
|
|
),
|
|
),
|
|
],
|
|
options={
|
|
"verbose_name": "account check automation",
|
|
"permissions": [
|
|
("view_checkaccountexecution", "Can view check account execution"),
|
|
("add_checkaccountexecution", "Can add check account execution"),
|
|
],
|
|
},
|
|
bases=("accounts.accountbaseautomation",),
|
|
),
|
|
migrations.CreateModel(
|
|
name="CheckAccountEngine",
|
|
fields=[
|
|
(
|
|
"created_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Created by"
|
|
),
|
|
),
|
|
(
|
|
"updated_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Updated by"
|
|
),
|
|
),
|
|
(
|
|
"date_created",
|
|
models.DateTimeField(
|
|
auto_now_add=True, null=True, verbose_name="Date created"
|
|
),
|
|
),
|
|
(
|
|
"date_updated",
|
|
models.DateTimeField(auto_now=True, verbose_name="Date updated"),
|
|
),
|
|
(
|
|
"comment",
|
|
models.TextField(blank=True, default="", verbose_name="Comment"),
|
|
),
|
|
(
|
|
"id",
|
|
models.UUIDField(
|
|
default=uuid.uuid4, primary_key=True, serialize=False
|
|
),
|
|
),
|
|
(
|
|
"name",
|
|
models.CharField(max_length=128, unique=True, verbose_name="Name"),
|
|
),
|
|
(
|
|
"slug",
|
|
models.SlugField(max_length=128, unique=True, verbose_name="Slug"),
|
|
),
|
|
],
|
|
options={
|
|
"abstract": False,
|
|
},
|
|
),
|
|
migrations.CreateModel(
|
|
name="IntegrationApplication",
|
|
fields=[
|
|
(
|
|
"created_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Created by"
|
|
),
|
|
),
|
|
(
|
|
"updated_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Updated by"
|
|
),
|
|
),
|
|
(
|
|
"date_created",
|
|
models.DateTimeField(
|
|
auto_now_add=True, null=True, verbose_name="Date created"
|
|
),
|
|
),
|
|
(
|
|
"date_updated",
|
|
models.DateTimeField(auto_now=True, verbose_name="Date updated"),
|
|
),
|
|
(
|
|
"comment",
|
|
models.TextField(blank=True, default="", verbose_name="Comment"),
|
|
),
|
|
(
|
|
"id",
|
|
models.UUIDField(
|
|
default=uuid.uuid4, primary_key=True, serialize=False
|
|
),
|
|
),
|
|
(
|
|
"org_id",
|
|
models.CharField(
|
|
blank=True,
|
|
db_index=True,
|
|
default="",
|
|
max_length=36,
|
|
verbose_name="Organization",
|
|
),
|
|
),
|
|
("name", models.CharField(max_length=128, verbose_name="Name")),
|
|
(
|
|
"logo",
|
|
private_storage.fields.PrivateImageField(
|
|
max_length=128,
|
|
storage=private_storage.storage.files.PrivateFileSystemStorage(),
|
|
upload_to="images",
|
|
verbose_name="Logo",
|
|
),
|
|
),
|
|
(
|
|
"secret",
|
|
common.db.fields.EncryptTextField(
|
|
default="", verbose_name="Secret"
|
|
),
|
|
),
|
|
(
|
|
"accounts",
|
|
common.db.fields.JSONManyToManyField(
|
|
default=dict, to="accounts.Account", verbose_name="Accounts"
|
|
),
|
|
),
|
|
(
|
|
"ip_group",
|
|
models.JSONField(
|
|
default=common.db.utils.default_ip_group,
|
|
verbose_name="IP group",
|
|
),
|
|
),
|
|
(
|
|
"date_last_used",
|
|
models.DateTimeField(
|
|
blank=True, null=True, verbose_name="Date last used"
|
|
),
|
|
),
|
|
("is_active", models.BooleanField(default=True, verbose_name="Active")),
|
|
],
|
|
options={
|
|
"verbose_name": "Integration App",
|
|
"unique_together": {("name", "org_id")},
|
|
},
|
|
),
|
|
migrations.CreateModel(
|
|
name="PushSecretRecord",
|
|
fields=[
|
|
(
|
|
"created_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Created by"
|
|
),
|
|
),
|
|
(
|
|
"updated_by",
|
|
models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Updated by"
|
|
),
|
|
),
|
|
(
|
|
"date_created",
|
|
models.DateTimeField(
|
|
auto_now_add=True, null=True, verbose_name="Date created"
|
|
),
|
|
),
|
|
(
|
|
"date_updated",
|
|
models.DateTimeField(auto_now=True, verbose_name="Date updated"),
|
|
),
|
|
(
|
|
"comment",
|
|
models.TextField(blank=True, default="", verbose_name="Comment"),
|
|
),
|
|
(
|
|
"id",
|
|
models.UUIDField(
|
|
default=uuid.uuid4, primary_key=True, serialize=False
|
|
),
|
|
),
|
|
(
|
|
"date_finished",
|
|
models.DateTimeField(
|
|
blank=True,
|
|
db_index=True,
|
|
null=True,
|
|
verbose_name="Date finished",
|
|
),
|
|
),
|
|
(
|
|
"status",
|
|
models.CharField(
|
|
default="pending", max_length=16, verbose_name="Status"
|
|
),
|
|
),
|
|
(
|
|
"error",
|
|
models.TextField(blank=True, null=True, verbose_name="Error"),
|
|
),
|
|
],
|
|
options={
|
|
"verbose_name": "Push secret record",
|
|
},
|
|
),
|
|
migrations.RemoveField(
|
|
model_name="accountbackupexecution",
|
|
name="plan",
|
|
),
|
|
migrations.AlterModelOptions(
|
|
name="automationexecution",
|
|
options={
|
|
"permissions": [
|
|
("view_changesecretexecution", "Can view change secret execution"),
|
|
("add_changesecretexecution", "Can add change secret execution"),
|
|
(
|
|
"view_gatheraccountsexecution",
|
|
"Can view gather accounts execution",
|
|
),
|
|
(
|
|
"add_gatheraccountsexecution",
|
|
"Can add gather accounts execution",
|
|
),
|
|
("view_pushaccountexecution", "Can view push account execution"),
|
|
("add_pushaccountexecution", "Can add push account execution"),
|
|
(
|
|
"view_backupaccountexecution",
|
|
"Can view backup account execution",
|
|
),
|
|
("add_backupaccountexecution", "Can add backup account execution"),
|
|
],
|
|
"verbose_name": "Automation execution",
|
|
"verbose_name_plural": "Automation executions",
|
|
},
|
|
),
|
|
migrations.AlterModelOptions(
|
|
name="changesecretrecord",
|
|
options={"verbose_name": "Change secret record"},
|
|
),
|
|
migrations.RemoveField(
|
|
model_name="changesecretrecord",
|
|
name="date_started",
|
|
),
|
|
migrations.RemoveField(
|
|
model_name="pushaccountautomation",
|
|
name="action",
|
|
),
|
|
migrations.RemoveField(
|
|
model_name="pushaccountautomation",
|
|
name="triggers",
|
|
),
|
|
migrations.RemoveField(
|
|
model_name="pushaccountautomation",
|
|
name="username",
|
|
),
|
|
migrations.AddField(
|
|
model_name="account",
|
|
name="change_secret_status",
|
|
field=models.CharField(
|
|
blank=True,
|
|
max_length=16,
|
|
null=True,
|
|
verbose_name="Change secret status",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="account",
|
|
name="date_change_secret",
|
|
field=models.DateTimeField(
|
|
blank=True, null=True, verbose_name="Date change secret"
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="account",
|
|
name="date_last_login",
|
|
field=models.DateTimeField(
|
|
blank=True, null=True, verbose_name="Date last access"
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="account",
|
|
name="login_by",
|
|
field=models.CharField(
|
|
blank=True, max_length=128, null=True, verbose_name="Access by"
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="account",
|
|
name="secret_reset",
|
|
field=models.BooleanField(default=True, verbose_name="Secret reset"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="changesecretautomation",
|
|
name="check_conn_after_change",
|
|
field=models.BooleanField(
|
|
default=True, verbose_name="Check connection after change"
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="changesecretrecord",
|
|
name="ignore_fail",
|
|
field=models.BooleanField(default=False, verbose_name="Ignore fail"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="gatheraccountsautomation",
|
|
name="check_risk",
|
|
field=models.BooleanField(default=True, verbose_name="Check risk"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="gatheredaccount",
|
|
name="date_password_change",
|
|
field=models.DateTimeField(null=True, verbose_name="Date change password"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="gatheredaccount",
|
|
name="date_password_expired",
|
|
field=models.DateTimeField(null=True, verbose_name="Date password expired"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="gatheredaccount",
|
|
name="detail",
|
|
field=models.JSONField(blank=True, default=dict, verbose_name="Detail"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="gatheredaccount",
|
|
name="remote_present",
|
|
field=models.BooleanField(default=True, verbose_name="Remote present"),
|
|
),
|
|
migrations.AddField(
|
|
model_name="gatheredaccount",
|
|
name="status",
|
|
field=models.CharField(
|
|
blank=True,
|
|
choices=[("0", "Pending"), ("1", "Confirmed"), ("2", "Ignored")],
|
|
default="0",
|
|
max_length=32,
|
|
verbose_name="Status",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="pushaccountautomation",
|
|
name="check_conn_after_change",
|
|
field=models.BooleanField(
|
|
default=True, verbose_name="Check connection after change"
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="account",
|
|
name="connectivity",
|
|
field=models.CharField(
|
|
choices=[
|
|
("-", "Unknown"),
|
|
("na", "N/A"),
|
|
("ok", "OK"),
|
|
("err", "Error"),
|
|
],
|
|
default="-",
|
|
max_length=16,
|
|
verbose_name="Connectivity",
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="changesecretrecord",
|
|
name="account",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="%(class)ss",
|
|
to="accounts.account",
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="changesecretrecord",
|
|
name="asset",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="asset_%(class)ss",
|
|
to="assets.asset",
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="changesecretrecord",
|
|
name="date_finished",
|
|
field=models.DateTimeField(
|
|
blank=True, db_index=True, null=True, verbose_name="Date finished"
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="changesecretrecord",
|
|
name="execution",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="execution_%(class)ss",
|
|
to="accounts.automationexecution",
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="gatheredaccount",
|
|
name="address_last_login",
|
|
field=models.CharField(
|
|
default="", max_length=39, null=True, verbose_name="Address login"
|
|
),
|
|
),
|
|
migrations.AlterField(
|
|
model_name="gatheredaccount",
|
|
name="present",
|
|
field=models.BooleanField(default=False, verbose_name="Present"),
|
|
),
|
|
migrations.DeleteModel(
|
|
name="AccountBackupAutomation",
|
|
),
|
|
migrations.DeleteModel(
|
|
name="AccountBackupExecution",
|
|
),
|
|
migrations.AddField(
|
|
model_name="pushsecretrecord",
|
|
name="account",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="%(class)ss",
|
|
to="accounts.account",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="pushsecretrecord",
|
|
name="asset",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="asset_%(class)ss",
|
|
to="assets.asset",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="pushsecretrecord",
|
|
name="execution",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.SET_NULL,
|
|
related_name="execution_%(class)ss",
|
|
to="accounts.automationexecution",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="accountrisk",
|
|
name="account",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="risks",
|
|
to="accounts.account",
|
|
verbose_name="Account",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="accountrisk",
|
|
name="asset",
|
|
field=models.ForeignKey(
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="risks",
|
|
to="assets.asset",
|
|
verbose_name="Asset",
|
|
),
|
|
),
|
|
migrations.AddField(
|
|
model_name="accountrisk",
|
|
name="gathered_account",
|
|
field=models.ForeignKey(
|
|
null=True,
|
|
on_delete=django.db.models.deletion.CASCADE,
|
|
related_name="risks",
|
|
to="accounts.gatheredaccount",
|
|
),
|
|
),
|
|
migrations.AlterUniqueTogether(
|
|
name="accountrisk",
|
|
unique_together={("asset", "username", "risk")},
|
|
),
|
|
migrations.RunPython(migrate_account_backup),
|
|
]
|