Merge branch 'v3' of github.com:jumpserver/jumpserver into v3

pull/8873/head
Jiangjie.Bai 2022-09-15 10:47:05 +08:00
commit 572f03d844
31 changed files with 228 additions and 423 deletions

View File

@ -2,11 +2,8 @@ from .mixin import *
from .platform import *
from .asset import *
from .label import *
from .accounts import *
from .account import *
from .node import *
from .domain import *
from .gathered_user import *
from .favorite_asset import *
from .account_template import *
from .account_backup import *
from .account_history import *

View File

@ -0,0 +1,4 @@
from .account import *
from .backup import *
from .history import *
from .template import *

View File

@ -9,9 +9,9 @@ from common.drf.filters import BaseFilterSet, UUIDInFilter
from common.mixins import RecordViewLogMixin
from common.permissions import UserConfirmation
from authentication.const import ConfirmType
from ..tasks.account_connectivity import test_accounts_connectivity_manual
from ..models import Account, Node
from .. import serializers
from assets.tasks.account_connectivity import test_accounts_connectivity_manual
from assets.models import Account, Node
from assets import serializers
__all__ = ['AccountFilterSet', 'AccountViewSet', 'AccountSecretsViewSet', 'AccountTaskCreateAPI']

View File

@ -4,9 +4,9 @@ from rest_framework import status, viewsets
from rest_framework.response import Response
from orgs.mixins.api import OrgBulkModelViewSet
from .. import serializers
from ..tasks import execute_account_backup_plan
from ..models import (
from assets import serializers
from assets.tasks import execute_account_backup_plan
from assets.models import (
AccountBackupPlan, AccountBackupPlanExecution
)

View File

@ -1,9 +1,9 @@
from assets.api.accounts import (
from .account import (
AccountFilterSet, AccountViewSet, AccountSecretsViewSet
)
from common.mixins import RecordViewLogMixin
from .. import serializers
from ..models import Account
from assets import serializers
from assets.models import Account
__all__ = ['AccountHistoryViewSet', 'AccountHistorySecretsViewSet']

View File

@ -1,6 +1,6 @@
from orgs.mixins.api import OrgBulkModelViewSet
from ..models import AccountTemplate
from .. import serializers
from assets.models import AccountTemplate
from assets import serializers
class AccountTemplateViewSet(OrgBulkModelViewSet):

View File

@ -38,18 +38,19 @@ class Category(PlatformMixin, ChoicesMixin, models.TextChoices):
return {
cls.HOST: {
'domain_enabled': True,
'su_enabled': True,
'ping_enabled': True,
'gather_facts_enabled': True,
'verify_account_enabled': True,
'change_password_enabled': True,
'create_account_enabled': True,
'gather_accounts_enabled': True,
'_protocols': ['ssh', 'sftp']
'su_enabled': True, 'su_method': 'sudo',
'ping_enabled': True, 'ping_method': 'ping',
'gather_facts_enabled': True, 'gather_facts_method': 'gather_facts_posix',
'verify_account_enabled': True, 'verify_account_method': 'verify_account_posix',
'change_password_enabled': True, 'change_password_method': 'change_password_posix',
'create_account_enabled': True, 'create_account_method': 'create_account_posix',
'gather_accounts_enabled': True, 'gather_accounts_method': 'gather_accounts_posix',
'_protocols': ['ssh', 'telnet'],
},
cls.NETWORKING: {
'domain_enabled': True,
'su_enabled': False,
'ping_enabled': True, 'ping_method': 'ping',
'gather_facts_enabled': False,
'verify_account_enabled': False,
'change_password_enabled': False,
@ -65,15 +66,28 @@ class Category(PlatformMixin, ChoicesMixin, models.TextChoices):
'change_password_enabled': True,
'create_account_enabled': True,
'gather_accounts_enabled': True,
'_protocols': []
},
cls.WEB: {
'domain_enabled': False,
'su_enabled': False,
'ping_enabled': False,
'gather_facts_enabled': False,
'verify_account_enabled': False,
'change_password_enabled': False,
'create_account_enabled': False,
'gather_accounts_enabled': False,
'_protocols': ['http', 'https']
},
cls.CLOUD: {
'domain_enabled': False,
'su_enabled': False,
'ping_enabled': False,
'gather_facts_enabled': False,
'verify_account_enabled': False,
'change_password_enabled': False,
'create_account_enabled': False,
'gather_accounts_enabled': False,
'_protocols': []
}
}
@ -83,9 +97,6 @@ class HostTypes(PlatformMixin, ChoicesMixin, models.TextChoices):
LINUX = 'linux', 'Linux'
WINDOWS = 'windows', 'Windows'
UNIX = 'unix', 'Unix'
BSD = 'bsd', 'BSD'
MACOS = 'macos', 'MacOS'
MAINFRAME = 'mainframe', _("Mainframe")
OTHER_HOST = 'other_host', _("Other host")
@classmethod
@ -95,20 +106,26 @@ class HostTypes(PlatformMixin, ChoicesMixin, models.TextChoices):
'_protocols': ['ssh', 'rdp', 'vnc', 'telnet']
},
cls.WINDOWS: {
'_protocols': ['ssh', 'rdp', 'vnc'],
'gather_facts_method': 'gather_facts_windows',
'verify_account_method': 'verify_account_windows',
'change_password_method': 'change_password_windows',
'create_account_method': 'create_account_windows',
'gather_accounts_method': 'gather_accounts_windows',
'_protocols': ['rdp', 'ssh', 'vnc'],
'su_enabled': False
},
cls.MACOS: {
cls.UNIX: {
'_protocols': ['ssh', 'vnc']
}
}
class NetworkingTypes(PlatformMixin, ChoicesMixin, models.TextChoices):
GENERAL = 'general', _("General device")
SWITCH = 'switch', _("Switch")
ROUTER = 'router', _("Router")
FIREWALL = 'firewall', _("Firewall")
OTHER_NETWORK = 'other_network', _("Other device")
OTHER_NETWORK = 'other', _("Other device")
class DatabaseTypes(PlatformMixin, ChoicesMixin, models.TextChoices):
@ -125,7 +142,12 @@ class DatabaseTypes(PlatformMixin, ChoicesMixin, models.TextChoices):
meta = {}
for name, label in cls.choices:
meta[name] = {
'_protocols': [name]
'_protocols': [name],
'gather_facts_method': f'gather_facts_{name}',
'verify_account_method': f'verify_account_{name}',
'change_password_method': f'change_password_{name}',
'create_account_method': f'create_account_{name}',
'gather_accounts_method': f'gather_accounts_{name}',
}
return meta

View File

@ -12,38 +12,15 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name='DeviceInfo',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created 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')),
('vendor', models.CharField(blank=True, max_length=64, null=True, verbose_name='Vendor')),
('model', models.CharField(blank=True, max_length=54, null=True, verbose_name='Model')),
('sn', models.CharField(blank=True, max_length=128, null=True, verbose_name='Serial number')),
('cpu_model', models.CharField(blank=True, max_length=64, null=True, verbose_name='CPU model')),
('cpu_count', models.IntegerField(null=True, verbose_name='CPU count')),
('cpu_cores', models.IntegerField(null=True, verbose_name='CPU cores')),
('cpu_vcpus', models.IntegerField(null=True, verbose_name='CPU vcpus')),
('memory', models.CharField(blank=True, max_length=64, null=True, verbose_name='Memory')),
('disk_total', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Disk total')),
('disk_info', models.CharField(blank=True, max_length=1024, null=True, verbose_name='Disk info')),
('os', models.CharField(blank=True, max_length=128, null=True, verbose_name='OS')),
('os_version', models.CharField(blank=True, max_length=16, null=True, verbose_name='OS version')),
('os_arch', models.CharField(blank=True, max_length=16, null=True, verbose_name='OS arch')),
('hostname_raw', models.CharField(blank=True, max_length=128, null=True, verbose_name='Hostname raw')),
('number', models.CharField(blank=True, max_length=128, null=True, verbose_name='Asset number')),
],
options={
'verbose_name': 'DeviceInfo',
},
migrations.AddField(
model_name='asset',
name='info',
field=models.JSONField(blank=True, default=dict, verbose_name='Info'),
),
migrations.CreateModel(
name='Host',
fields=[
('asset_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.asset')),
('device_info', models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.deviceinfo', verbose_name='Host')),
],
),
]

View File

@ -4,46 +4,6 @@ from django.utils import timezone
from django.db import migrations, models
def migrate_hardware(apps, *args):
host_model = apps.get_model('assets', 'Host')
asset_model = apps.get_model('assets', 'Asset')
hardware_model = apps.get_model('assets', 'DeviceInfo')
created = 0
batch_size = 1000
excludes = ['id', 'host', 'date_updated']
fields = [f.name for f in hardware_model._meta.fields]
fields = [name for name in fields if name not in excludes]
while True:
start = created
end = created + batch_size
hosts = host_model.objects.all()[start:end]
asset_ids = [h.asset_ptr_id for h in hosts]
assets = asset_model.objects.filter(id__in=asset_ids)
asset_mapper = {a.id: a for a in assets}
if not hosts:
break
hardware_infos = []
hosts_updated = []
for host in hosts:
hardware = hardware_model()
asset = asset_mapper[host.asset_ptr_id]
hardware.date_updated = timezone.now()
for name in fields:
setattr(hardware, name, getattr(asset, name))
hardware_infos.append(hardware)
host.device_info_id = hardware.id
hosts_updated.append(host)
hardware_model.objects.bulk_create(hardware_infos, ignore_conflicts=True)
host_model.objects.bulk_update(hosts_updated, ['device_info_id'])
created += len(hardware_infos)
def migrate_to_host(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
host_model = apps.get_model("assets", 'Host')
@ -64,6 +24,34 @@ def migrate_to_host(apps, schema_editor):
created += len(hosts)
def migrate_hardware_info(apps, *args):
asset_model = apps.get_model("assets", "Asset")
count = 0
batch_size = 1000
hardware_fields = [
'vendor', 'model', 'sn', 'cpu_model', 'cpu_count', 'cpu_cores',
'cpu_vcpus', 'memory', 'disk_total', 'disk_info', 'os', 'os_arch',
'os_version', 'hostname_raw', 'number'
]
while True:
start = count
end = count + batch_size
assets = asset_model.objects.all()[start:end]
if not assets:
break
updated = []
for asset in assets:
info = {getattr(asset, field) for field in hardware_fields if getattr(asset, field)}
if not info:
continue
asset.info = info
updated.append(asset)
asset_model.objects.bulk_update(updated, ['info'])
class Migration(migrations.Migration):
dependencies = [
@ -71,6 +59,6 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(migrate_hardware_info),
migrations.RunPython(migrate_to_host),
migrations.RunPython(migrate_hardware),
]

View File

@ -48,6 +48,10 @@ class Migration(migrations.Migration):
fields=[
('asset_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.asset')),
('url', models.CharField(max_length=1024, verbose_name='url')),
('autofill', models.CharField(default='basic', max_length=16)),
('password_selector', models.CharField(blank=True, default='', max_length=128)),
('submit_selector', models.CharField(blank=True, default='', max_length=128)),
('username_selector', models.CharField(blank=True, default='', max_length=128))
],
options={
'abstract': False,

View File

@ -1,6 +1,5 @@
# Generated by Django 3.1.14 on 2022-04-30 14:41
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
@ -19,11 +18,6 @@ class Migration(migrations.Migration):
('setting', models.JSONField(default=dict, verbose_name='Setting')),
],
),
migrations.AddField(
model_name='platform',
name='domain_default',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.domain', verbose_name='Domain default'),
),
migrations.AddField(
model_name='platform',
name='domain_enabled',
@ -62,12 +56,12 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='platform',
name='ping_enabled',
field=models.BooleanField(default=False),
field=models.BooleanField(default=False, verbose_name='Ping enabled'),
),
migrations.AddField(
model_name='platform',
name='ping_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Ping method'),
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Ping method'),
),
migrations.AddField(
model_name='platform',
@ -89,4 +83,24 @@ class Migration(migrations.Migration):
name='verify_account_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Verify account method'),
),
migrations.AddField(
model_name='platform',
name='gather_accounts_enabled',
field=models.BooleanField(default=False, verbose_name='Gather facts enabled'),
),
migrations.AddField(
model_name='platform',
name='gather_accounts_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'),
),
migrations.AddField(
model_name='platform',
name='gather_facts_enabled',
field=models.BooleanField(default=False, verbose_name='Gather facts enabled'),
),
migrations.AddField(
model_name='platform',
name='gather_facts_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'),
),
]

View File

@ -21,6 +21,7 @@ class Migration(migrations.Migration):
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
@ -38,6 +39,7 @@ class Migration(migrations.Migration):
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('asset', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='assets.asset', verbose_name='Asset')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('su_from', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='assets.account', verbose_name='Su from')),
],
options={
'verbose_name': 'historical Account',
@ -52,6 +54,7 @@ class Migration(migrations.Migration):
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('username', models.CharField(blank=True, db_index=True, max_length=128, verbose_name='Username')),
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
@ -63,12 +66,13 @@ class Migration(migrations.Migration):
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('privileged', models.BooleanField(default=False, verbose_name='Privileged account')),
('version', models.IntegerField(default=0, verbose_name='Version')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.asset', verbose_name='Asset')),
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='assets.asset', verbose_name='Asset')),
('su_from', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='assets.account', verbose_name='Su from')),
],
options={
'verbose_name': 'Account',
'permissions': [('view_accountsecret', 'Can view asset account secret'), ('change_accountsecret', 'Can change asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')],
'unique_together': {('username', 'asset')},
'unique_together': {('username', 'asset'), ('name', 'asset')},
},
),
]

View File

@ -40,7 +40,7 @@ def migrate_accounts(apps, schema_editor):
values['version'] = 1
system_user = auth_book.systemuser
if auth_book.systemuser:
if system_user:
values.update({attr: getattr(system_user, attr) for attr in auth_attrs})
values['created_by'] = str(system_user.id)
values['privileged'] = system_user.type == 'admin'
@ -48,6 +48,7 @@ def migrate_accounts(apps, schema_editor):
auth_book_auth = {attr: getattr(auth_book, attr) for attr in auth_attrs}
auth_book_auth = {attr: value for attr, value in auth_book_auth.items() if value}
values.update(auth_book_auth)
values['name'] = values['username']
account = account_model(**values)
accounts.append(account)

View File

@ -41,6 +41,11 @@ class Migration(migrations.Migration):
old_name='hostname',
new_name='name',
),
migrations.AlterField(
model_name='asset',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AddField(
model_name='asset',
name='date_updated',

View File

@ -1,44 +1,10 @@
# Generated by Django 3.2.13 on 2022-08-29 11:46
# Generated by Django 3.2.14 on 2022-09-14 12:45
from django.db import migrations, models
from assets.const import Category
from assets.models import Type
def update_account_backup_type(apps, schema_editor):
backup_model = apps.get_model('assets', 'AccountBackupPlan')
all_number = 4294967295
asset_number = Type.choices_to_value([Category.HOST])
app_number = Type.choices_to_value([
Category.NETWORKING, Category.DATABASE, Category.CLOUD, Category.WEB]
)
backup_model.objects.filter(types=255).update(types=all_number)
backup_model.objects.filter(types=1).update(types=asset_number)
backup_model.objects.filter(types=2).update(types=app_number)
backup_execution_model = apps.get_model('assets', 'AccountBackupPlanExecution')
choices_dict = {
'all': Type.get_types(value=all_number),
'asset': Type.get_types(value=asset_number),
'app': Type.get_types(value=app_number)
}
qs_dict = {
'all': backup_execution_model.objects.filter(plan__types=255),
'asset': backup_execution_model.objects.filter(plan__types=1),
'app': backup_execution_model.objects.filter(plan__types=2)
}
backup_executions = []
for k, qs in qs_dict.items():
type_choices = choices_dict[k]
for i in qs:
i.plan_snapshot['types'] = type_choices
backup_executions.append(i)
backup_execution_model.objects.bulk_update(backup_executions, fields=['plan_snapshot'])
class Migration(migrations.Migration):
dependencies = [
('assets', '0106_auto_20220819_1523'),
]
@ -47,13 +13,6 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='accountbackupplan',
name='types',
field=models.BigIntegerField(
choices=[(4294967295, 'All'), (1, 'Linux'), (2, 'Windows'), (4, 'Unix'), (8, 'BSD'), (16, 'MacOS'),
(32, 'Mainframe'), (64, 'Other host'), (127, 'Host'), (128, 'Switch'), (256, 'Router'),
(512, 'Firewall'), (1024, 'Other device'), (1920, 'NetworkDevice'), (2048, 'MySQL'),
(4096, 'MariaDB'), (8192, 'PostgreSQL'), (16384, 'Oracle'), (32768, 'SQLServer'),
(65536, 'MongoDB'), (131072, 'Redis'), (260096, 'Database'), (262144, 'Clouding'),
(524288, 'Web')], default=4294967295, verbose_name='Type'),
field=models.BigIntegerField(),
),
migrations.RunPython(update_account_backup_type),
]

View File

@ -1,37 +0,0 @@
# Generated by Django 3.2.14 on 2022-09-01 02:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0107_alter_accountbackupplan_types'),
]
operations = [
migrations.RemoveField(
model_name='platform',
name='create_account_enabled',
),
migrations.RemoveField(
model_name='platform',
name='create_account_method',
),
migrations.RemoveField(
model_name='platform',
name='domain_default',
),
migrations.RemoveField(
model_name='platform',
name='domain_enabled',
),
migrations.RemoveField(
model_name='platform',
name='ping_enabled',
),
migrations.RemoveField(
model_name='platform',
name='ping_method',
),
]

View File

@ -1,30 +0,0 @@
# Generated by Django 3.2.14 on 2022-09-01 06:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0108_auto_20220901_1034'),
]
operations = [
migrations.RemoveField(
model_name='host',
name='device_info',
),
migrations.AddField(
model_name='asset',
name='info',
field=models.JSONField(blank=True, default=dict, verbose_name='Info'),
),
migrations.AddField(
model_name='database',
name='version',
field=models.CharField(blank=True, max_length=16, verbose_name='Version'),
),
migrations.DeleteModel(
name='DeviceInfo',
),
]

View File

@ -1,43 +0,0 @@
# Generated by Django 3.2.14 on 2022-09-01 07:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0109_auto_20220901_1431'),
]
operations = [
migrations.AddField(
model_name='platform',
name='create_account_enabled',
field=models.BooleanField(default=False, verbose_name='Create account enabled'),
),
migrations.AddField(
model_name='platform',
name='create_account_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Create account method'),
),
migrations.AddField(
model_name='platform',
name='gather_accounts_enabled',
field=models.BooleanField(default=False, verbose_name='Gather facts enabled'),
),
migrations.AddField(
model_name='platform',
name='gather_accounts_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'),
),
migrations.AddField(
model_name='platform',
name='gather_facts_enabled',
field=models.BooleanField(default=False, verbose_name='Gather facts enabled'),
),
migrations.AddField(
model_name='platform',
name='gather_facts_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Gather facts method'),
),
]

View File

@ -1,29 +0,0 @@
# Generated by Django 3.2.14 on 2022-09-08 11:58
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0110_auto_20220901_1542'),
]
operations = [
migrations.AddField(
model_name='platform',
name='domain_enabled',
field=models.BooleanField(default=True, verbose_name='Domain enalbed'),
),
migrations.AlterField(
model_name='account',
name='asset',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='accounts', to='assets.asset', verbose_name='Asset'),
),
migrations.AlterField(
model_name='asset',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
]

View File

@ -1,38 +0,0 @@
# Generated by Django 3.2.14 on 2022-09-09 11:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0111_auto_20220908_1958'),
]
operations = [
migrations.AddField(
model_name='web',
name='autofill',
field=models.CharField(default='basic', max_length=16),
),
migrations.AddField(
model_name='web',
name='password_selector',
field=models.CharField(blank=True, default='', max_length=128),
),
migrations.AddField(
model_name='web',
name='submit_selector',
field=models.CharField(blank=True, default='', max_length=128),
),
migrations.AddField(
model_name='web',
name='username_selector',
field=models.CharField(blank=True, default='', max_length=128),
),
migrations.AlterField(
model_name='platform',
name='domain_enabled',
field=models.BooleanField(default=True, verbose_name='Domain enabled'),
),
]

View File

@ -3,20 +3,29 @@ from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords
from common.utils import lazyproperty
from .base import BaseAccount
__all__ = ['Account', 'AccountTemplate']
class Account(BaseAccount):
asset = models.ForeignKey('assets.Asset', related_name='accounts', on_delete=models.CASCADE, verbose_name=_('Asset'))
asset = models.ForeignKey(
'assets.Asset', related_name='accounts',
on_delete=models.CASCADE, verbose_name=_('Asset')
)
su_from = models.ForeignKey(
'assets.Account', related_name='su_to', null=True,
on_delete=models.SET_NULL, verbose_name=_("Su from")
)
version = models.IntegerField(default=0, verbose_name=_('Version'))
history = HistoricalRecords()
class Meta:
verbose_name = _('Account')
unique_together = [('username', 'asset')]
unique_together = [
('username', 'asset'),
('name', 'asset'),
]
permissions = [
('view_accountsecret', _('Can view asset account secret')),
('change_accountsecret', _('Can change asset account secret')),
@ -24,10 +33,6 @@ class Account(BaseAccount):
('view_historyaccountsecret', _('Can view asset history account secret')),
]
@property
def name(self):
return "{}({})_{}".format(self.asset_name, self.ip, self.username)
@lazyproperty
def ip(self):
return self.asset.ip

View File

@ -6,7 +6,6 @@ from .common import Asset
class Database(Asset):
db_name = models.CharField(max_length=1024, verbose_name=_("Database"), blank=True)
version = models.CharField(max_length=16, verbose_name=_("Version"), blank=True)
def __str__(self):
return '{}({}://{}/{})'.format(self.name, self.type, self.ip, self.db_name)

View File

@ -3,4 +3,5 @@ from .common import Asset
class Networking(Asset):
pass

View File

@ -71,7 +71,7 @@ class Type(BitOperationChoice):
class AccountBackupPlan(CommonModelMixin, PeriodTaskModelMixin, OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
types = models.BigIntegerField(choices=Type.DB_CHOICES, default=Type.ALL, verbose_name=_('Type'))
types = models.BigIntegerField()
recipients = models.ManyToManyField(
'users.User', related_name='recipient_escape_route_plans', blank=True,
verbose_name=_("Recipient")

View File

@ -9,6 +9,12 @@ __all__ = ['Platform', 'PlatformProtocol']
class PlatformProtocol(models.Model):
SETTING_ATTRS = {
'console': True,
'security': 'any,tls,rdp',
'sftp_enabled': True,
'sftp_home': '/tmp'
}
name = models.CharField(max_length=32, verbose_name=_('Name'))
port = models.IntegerField(verbose_name=_('Port'))
setting = models.JSONField(verbose_name=_('Setting'), default=dict)
@ -34,6 +40,8 @@ class Platform(models.Model):
domain_enabled = models.BooleanField(default=True, verbose_name=_("Domain enabled"))
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols"))
ping_enabled = models.BooleanField(default=False, verbose_name=_("Ping enabled"))
ping_method = models.CharField(max_length=32, blank=True, null=True, verbose_name=_("Ping method"))
gather_facts_enabled = models.BooleanField(default=False, verbose_name=_("Gather facts enabled"))
gather_facts_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Gather facts method"))
# 账号有关的
@ -61,71 +69,7 @@ class Platform(models.Model):
@staticmethod
def set_default_platforms_ops(platform_model):
default_ok = {
'su_enabled': True,
'su_method': 'sudo',
'domain_enabled': True,
'change_password_enabled': True,
'change_password_method': 'change_password_linux',
'verify_account_enabled': True,
'verify_account_method': 'ansible_posix_ping',
}
db_default = {
'su_enabled': False,
'domain_enabled': True,
'change_password_enabled': True,
'verify_account_enabled': True,
}
platform_ops_map = {
('host', 'linux'): {
**default_ok,
'change_password_method': 'change_password_linux',
'verify_account_method': 'ansible_posix_ping'
},
('host', 'windows'): {
**default_ok,
'su_enabled': False,
'change_password_method': 'change_password_windows',
'verify_account_method': 'ansible_win_ping'
},
('host', 'unix'): {
**default_ok,
'verify_account_method': 'ansible_posix_ping',
'change_password_method': 'change_password_aix'
},
('database', 'mysql'): {
**db_default,
'verify_account_method': 'mysql_ping',
'change_password_method': 'change_password_mysql'
},
('database', 'postgresql'): {
**db_default,
'verify_account_method': 'postgresql_ping',
'change_password_method': 'change_password_postgresql'
},
('database', 'oracle'): {
**db_default,
'verify_account_method': 'oracle_ping',
'change_password_method': 'change_password_oracle'
},
('database', 'sqlserver'): {
**db_default,
'verify_account_method': 'mysql_ping',
'change_password_method': 'change_password_sqlserver'
},
}
platforms = platform_model.objects.all()
updated = []
for p in platforms:
attrs = platform_ops_map.get((p.category, p.type), {})
if not attrs:
continue
for k, v in attrs.items():
setattr(p, k, v)
updated.append(p)
platform_model.objects.bulk_update(updated, list(default_ok.keys()))
pass
def __str__(self):
return self.name

View File

@ -2,8 +2,6 @@
# -*- coding: utf-8 -*-
#
from django.utils import timezone
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
@ -21,3 +19,53 @@ def private_key_validator(value):
_('%(value)s is not an even number'),
params={'value': value},
)
def update_internal_platforms(platform_model):
from assets.const import AllTypes
platforms = [
{'name': 'Linux', 'category': 'host', 'type': 'linux'},
{'name': 'BSD', 'category': 'host', 'type': 'unix'},
{'name': 'Unix', 'category': 'host', 'type': 'unix'},
{'name': 'MacOS', 'category': 'host', 'type': 'unix'},
{'name': 'Windows', 'category': 'host', 'type': 'unix'},
{
'name': 'AIX', 'category': 'host', 'type': 'unix',
'create_account_method': 'create_account_aix',
'change_password_method': 'change_password_aix',
},
{'name': 'Windows', 'category': 'host', 'type': 'windows'},
{'name': 'Windows-TLS', 'category': 'host', 'type': 'windows'},
{'name': 'Windows-RDP', 'category': 'host', 'type': 'windows'},
# 数据库
{'name': 'MySQL', 'category': 'database', 'type': 'mysql'},
{'name': 'PostgreSQL', 'category': 'database', 'type': 'postgresql'},
{'name': 'Oracle', 'category': 'database', 'type': 'oracle'},
{'name': 'SQLServer', 'category': 'database', 'type': 'sqlserver'},
{'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'},
{'name': 'Redis', 'category': 'database', 'type': 'redis'},
# 网络设备
{'name': 'Generic', 'category': 'networking', 'type': 'general'},
{'name': 'Huawei', 'category': 'networking', 'type': 'general'},
{'name': 'Cisco', 'category': 'networking', 'type': 'general'},
{'name': 'H3C', 'category': 'networking', 'type': 'general'},
# Web
# Cloud
]
platforms = platform_model.objects.all()
updated = []
for p in platforms:
attrs = platform_ops_map.get((p.category, p.type), {})
if not attrs:
continue
for k, v in attrs.items():
setattr(p, k, v)
updated.append(p)
platform_model.objects.bulk_update(updated, list(default_ok.keys()))

View File

@ -1,4 +1,4 @@
- hosts: {{ account.asset.name }}
- hosts: all
vars:
account:
username: {{ account.username }}

View File

@ -4,7 +4,8 @@ from rest_framework import serializers
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from common.drf.serializers import SecretReadableMixin
from assets.models import Account, AccountTemplate
from common.drf.fields import ObjectRelatedField
from assets.models import Account, AccountTemplate, Asset
from assets.serializers.base import AuthValidateMixin
from .common import AccountFieldsSerializerMixin
@ -14,7 +15,9 @@ class AccountSerializerCreateMixin(serializers.ModelSerializer):
required=False, allow_null=True, write_only=True,
label=_('Account template')
)
push_to_asset = serializers.BooleanField(default=False, label=_("Push to asset"), write_only=True)
push_now = serializers.BooleanField(
default=False, label=_("Push now"), write_only=True
)
@staticmethod
def validate_template(value):
@ -39,30 +42,34 @@ class AccountSerializerCreateMixin(serializers.ModelSerializer):
account_template = attrs.pop('template', None)
if account_template:
self.replace_attrs(account_template, attrs)
push_to_asset = attrs.pop('push_to_asset', False)
self.push_now = attrs.pop('push_now', False)
return super().validate(attrs)
def create(self, validated_data):
instance = super().create(validated_data)
if self.push_now:
print("Start push account to asset")
# Todo: push it
pass
return instance
class AccountSerializer(AuthValidateMixin,
AccountSerializerCreateMixin,
AccountFieldsSerializerMixin,
BulkOrgResourceModelSerializer):
name = serializers.CharField(max_length=128, read_only=True, label=_("Name"))
ip = serializers.ReadOnlyField(label=_("IP"))
asset_name = serializers.ReadOnlyField(label=_("Asset"))
asset = ObjectRelatedField(required=False, queryset=Asset.objects, label=_('Asset'), attrs=('id', 'name', 'ip'))
platform = serializers.ReadOnlyField(label=_("Platform"))
class Meta(AccountFieldsSerializerMixin.Meta):
model = Account
fields = AccountFieldsSerializerMixin.Meta.fields \
+ ['template', 'push_to_asset']
+ ['template', 'push_now']
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('asset') \
.annotate(ip=F('asset__ip')) \
.annotate(asset_name=F('asset__name'))
queryset = queryset.prefetch_related('asset')
return queryset

View File

@ -8,8 +8,8 @@ __all__ = ['AccountFieldsSerializerMixin']
class AccountFieldsSerializerMixin(serializers.ModelSerializer):
class Meta:
fields_mini = [
'id', 'name', 'username', 'privileged', 'ip',
'asset_name', 'platform', 'version'
'id', 'name', 'username', 'privileged',
'platform', 'version'
]
fields_write_only = ['password', 'private_key', 'public_key', 'passphrase']
fields_other = ['date_created', 'date_updated', 'comment']

View File

@ -54,9 +54,12 @@ class AssetAccountSerializer(AccountSerializer):
class Meta(AccountSerializer.Meta):
fields_mini = [
'id', 'name', 'username', 'privileged', 'version'
'id', 'name', 'username', 'privileged', 'version',
]
fields_write_only = [
'password', 'private_key', 'public_key',
'passphrase', 'token', 'push_now'
]
fields_write_only = ['password', 'private_key', 'public_key', 'passphrase', 'token']
fields = fields_mini + fields_write_only

View File

@ -11,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('assets', '0111_auto_20220908_1958'),
('assets', '0106_auto_20220819_1523'),
('ops', '0022_auto_20220817_1346'),
]