diff --git a/apps/assets/migrations/0092_add_host.py b/apps/assets/migrations/0092_add_host.py index 72ad9f7d8..83a63056f 100644 --- a/apps/assets/migrations/0092_add_host.py +++ b/apps/assets/migrations/0092_add_host.py @@ -96,6 +96,7 @@ class Migration(migrations.Migration): ('password_selector', models.CharField(blank=True, default='', max_length=128, verbose_name='Password selector')), ('submit_selector', models.CharField(blank=True, default='', max_length=128, verbose_name='Submit selector')), ('username_selector', models.CharField(blank=True, default='', max_length=128, verbose_name='Username selector')), + ('script', models.JSONField(blank=True, default=list, verbose_name='Script')), ], options={ 'abstract': False, diff --git a/apps/assets/migrations/0099_auto_20220711_1409.py b/apps/assets/migrations/0099_auto_20220711_1409.py index cf04f8b66..01ad1cf1f 100644 --- a/apps/assets/migrations/0099_auto_20220711_1409.py +++ b/apps/assets/migrations/0099_auto_20220711_1409.py @@ -22,6 +22,7 @@ class Migration(migrations.Migration): ('id', models.UUIDField(db_index=True, default=uuid.uuid4)), ('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token')], default='password', max_length=16, verbose_name='Secret type')), ('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')), + ('version', models.IntegerField(default=0, verbose_name='Version'),), ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField(db_index=True)), ('history_change_reason', models.CharField(max_length=100, null=True)), @@ -46,6 +47,8 @@ class Migration(migrations.Migration): ('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token')], default='password', max_length=16, verbose_name='Secret type')), ('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')), ('comment', models.TextField(blank=True, verbose_name='Comment')), + ('connectivity', models.CharField(choices=[('unknown', 'Unknown'), ('ok', 'Ok'), ('failed', 'Failed')], default='unknown', max_length=16, verbose_name='Connectivity')), + ('date_verified', models.DateTimeField(null=True, verbose_name='Date verified')), ('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')), ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), ('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')), diff --git a/apps/assets/migrations/0107_auto_20221019_1115.py b/apps/assets/migrations/0107_auto_20221019_1115.py index 6c812b708..c720655a8 100644 --- a/apps/assets/migrations/0107_auto_20221019_1115.py +++ b/apps/assets/migrations/0107_auto_20221019_1115.py @@ -35,7 +35,6 @@ class Migration(migrations.Migration): name='BaseAutomation', fields=[ ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), - ('updated_by', models.CharField(blank=True, max_length=32, 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')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), @@ -144,7 +143,7 @@ class Migration(migrations.Migration): ('execution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.automationexecution')), ], options={ - 'verbose_name': 'Change secret', + 'verbose_name': 'Change secret record', }, ), migrations.AddField( @@ -156,13 +155,11 @@ class Migration(migrations.Migration): name='ChangeSecretAutomation', 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')), - ('secret_types', models.JSONField(default=list, verbose_name='Secret types')), - ('password_strategy', models.CharField(choices=[('specific', 'Specific'), ('random_one', 'All assets use the same random password'), ('random_all', 'All assets use different random password')], default='random_one', max_length=16, verbose_name='Password strategy')), - ('password', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')), + ('secret_type', models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token')], default='password', max_length=16, verbose_name='Secret type')), + ('secret_strategy', models.CharField(choices=[('specific', 'Specific'), ('random_one', 'All assets use the same random password'), ('random_all', 'All assets use different random password')], default='specific', max_length=16, verbose_name='Secret strategy')), + ('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')), ('password_rules', models.JSONField(default=dict, verbose_name='Password rules')), - ('ssh_key_strategy', models.CharField(choices=[('specific', 'Specific'), ('random_one', 'All assets use the same random password'), ('random_all', 'All assets use different random password')], default='random_one', max_length=16)), - ('ssh_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH key')), - ('ssh_key_change_strategy', models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (The key generated by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key strategy')), + ('ssh_key_change_strategy', models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (The key generated by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy')), ('recipients', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Recipient')), ], options={ diff --git a/apps/assets/migrations/0108_auto_20221019_1706.py b/apps/assets/migrations/0108_auto_20221019_1706.py deleted file mode 100644 index 57279fffc..000000000 --- a/apps/assets/migrations/0108_auto_20221019_1706.py +++ /dev/null @@ -1,75 +0,0 @@ -# Generated by Django 3.2.14 on 2022-10-19 09:06 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0107_auto_20221019_1115'), - ] - - operations = [ - migrations.AlterModelOptions( - name='automationexecution', - options={'verbose_name': 'Automation task execution'}, - ), - migrations.AlterModelOptions( - name='baseautomation', - options={'verbose_name': 'Automation task'}, - ), - migrations.AlterModelOptions( - name='changesecretrecord', - options={'verbose_name': 'Change secret record'}, - ), - migrations.AlterModelOptions( - name='verifyaccountautomation', - options={'verbose_name': 'Verify account automation'}, - ), - migrations.RenameField( - model_name='changesecretautomation', - old_name='password', - new_name='secret', - ), - migrations.RemoveField( - model_name='baseautomation', - name='updated_by', - ), - migrations.RemoveField( - model_name='changesecretautomation', - name='password_strategy', - ), - migrations.RemoveField( - model_name='changesecretautomation', - name='secret_types', - ), - migrations.RemoveField( - model_name='changesecretautomation', - name='ssh_key', - ), - migrations.RemoveField( - model_name='changesecretautomation', - name='ssh_key_strategy', - ), - migrations.AddField( - model_name='changesecretautomation', - name='secret_strategy', - field=models.CharField(choices=[('specific', 'Specific'), ('random_one', 'All assets use the same random password'), ('random_all', 'All assets use different random password')], default='specific', max_length=16, verbose_name='Secret strategy'), - ), - migrations.AddField( - model_name='changesecretautomation', - name='secret_type', - field=models.CharField(choices=[('password', 'Password'), ('ssh_key', 'SSH key'), ('access_key', 'Access key'), ('token', 'Token')], default='password', max_length=16, verbose_name='Secret type'), - ), - migrations.AlterField( - model_name='automationexecution', - name='automation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='executions', to='assets.baseautomation', verbose_name='Automation task'), - ), - migrations.AlterField( - model_name='changesecretautomation', - name='ssh_key_change_strategy', - field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (The key generated by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'), - ), - ] diff --git a/apps/assets/migrations/0109_auto_20221019_2040.py b/apps/assets/migrations/0109_auto_20221019_2040.py deleted file mode 100644 index 958ca4bea..000000000 --- a/apps/assets/migrations/0109_auto_20221019_2040.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.14 on 2022-10-19 12:40 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0108_auto_20221019_1706'), - ] - - operations = [ - migrations.AddField( - model_name='web', - name='script', - field=models.JSONField(blank=True, default=list, verbose_name='Script'), - ), - migrations.AddField( - model_name='historicalaccount', - name='version', - field=models.IntegerField(default=0, verbose_name='Version'), - ), - ] diff --git a/apps/assets/migrations/0110_auto_20221021_1506.py b/apps/assets/migrations/0110_auto_20221021_1506.py deleted file mode 100644 index 039f7d179..000000000 --- a/apps/assets/migrations/0110_auto_20221021_1506.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.14 on 2022-10-21 07:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('assets', '0109_auto_20221019_2040'), - ] - - operations = [ - migrations.AddField( - model_name='account', - name='connectivity', - field=models.CharField(choices=[('unknown', 'Unknown'), ('ok', 'Ok'), ('failed', 'Failed')], default='unknown', max_length=16, verbose_name='Connectivity'), - ), - migrations.AddField( - model_name='account', - name='date_verified', - field=models.DateTimeField(null=True, verbose_name='Date verified'), - ), - ] diff --git a/apps/orgs/mixins/serializers.py b/apps/orgs/mixins/serializers.py index 4ec936c2f..8f70814bc 100644 --- a/apps/orgs/mixins/serializers.py +++ b/apps/orgs/mixins/serializers.py @@ -31,8 +31,7 @@ class OrgResourceSerializerMixin(CommonSerializerMixin, serializers.Serializer): validators = [] for v in _validators: - if isinstance(v, UniqueTogetherValidator) \ - and "org_id" in v.fields: + if isinstance(v, UniqueTogetherValidator) and "org_id" in v.fields: v = ProjectUniqueValidator(v.queryset, v.fields) validators.append(v) return validators diff --git a/apps/terminal/api/applet/host.py b/apps/terminal/api/applet/host.py index 09de7dbff..d4166ea98 100644 --- a/apps/terminal/api/applet/host.py +++ b/apps/terminal/api/applet/host.py @@ -1,26 +1,20 @@ from rest_framework import viewsets -from rest_framework.decorators import action -from orgs.utils import tmp_to_root_org -from orgs.models import Organization -from assets.models import Host +from orgs.utils import tmp_to_builtin_org from terminal import serializers, models __all__ = ['AppletHostViewSet', 'AppletHostDeploymentViewSet'] class AppletHostViewSet(viewsets.ModelViewSet): - queryset = models.AppletHost.objects.all() serializer_class = serializers.AppletHostSerializer - @action(methods=['get'], detail=False) - def hosts(self, request): - with tmp_to_root_org(): - kwargs = { - 'platform__name': 'RemoteAppHost', - 'org_id': Organization.SYSTEM_ID - } - return Host.objects.filter(**kwargs) + def get_queryset(self): + return models.AppletHost.objects.all() + + def dispatch(self, request, *args, **kwargs): + with tmp_to_builtin_org(system=1): + return super().dispatch(request, *args, **kwargs) class AppletHostDeploymentViewSet(viewsets.ModelViewSet): diff --git a/apps/terminal/migrations/0054_auto_20221024_1452.py b/apps/terminal/migrations/0054_auto_20221027_1125.py similarity index 80% rename from apps/terminal/migrations/0054_auto_20221024_1452.py rename to apps/terminal/migrations/0054_auto_20221027_1125.py index c2d67ca65..418cbe993 100644 --- a/apps/terminal/migrations/0054_auto_20221024_1452.py +++ b/apps/terminal/migrations/0054_auto_20221027_1125.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.14 on 2022-10-24 06:52 +# Generated by Django 3.2.14 on 2022-10-27 03:25 from django.db import migrations, models import django.db.models.deletion @@ -8,7 +8,7 @@ import uuid class Migration(migrations.Migration): dependencies = [ - ('assets', '0110_auto_20221021_1506'), + ('assets', '0107_auto_20221019_1115'), ('terminal', '0053_auto_20220830_1244'), ] @@ -26,8 +26,7 @@ class Migration(migrations.Migration): ('version', models.CharField(max_length=16, verbose_name='Version')), ('author', models.CharField(max_length=128, verbose_name='Author')), ('type', models.CharField(choices=[('general', 'General'), ('web', 'Web')], default='general', max_length=16, verbose_name='Type')), - ('vcs_type', models.CharField(max_length=16, null=True, verbose_name='VCS type')), - ('vcs_url', models.CharField(max_length=256, null=True, verbose_name='URL')), + ('is_active', models.BooleanField(default=True, verbose_name='Is active')), ('protocols', models.JSONField(default=list, verbose_name='Protocol')), ('tags', models.JSONField(default=list, verbose_name='Tags')), ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), @@ -39,12 +38,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name='AppletHost', fields=[ - ('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')), - ('updated_by', models.CharField(blank=True, max_length=32, 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')), - ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), - ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('host_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='assets.host')), ('account_automation', models.BooleanField(default=False, verbose_name='Account automation')), ('date_synced', models.DateTimeField(blank=True, null=True, verbose_name='Date synced')), ('status', models.CharField(max_length=16, verbose_name='Status')), @@ -52,6 +46,7 @@ class Migration(migrations.Migration): options={ 'abstract': False, }, + bases=('assets.host',), ), migrations.CreateModel( name='AppletPublication', @@ -91,9 +86,4 @@ class Migration(migrations.Migration): name='applets', field=models.ManyToManyField(through='terminal.AppletPublication', to='terminal.Applet', verbose_name='Applet'), ), - migrations.AddField( - model_name='applethost', - name='host', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='assets.host', verbose_name='Host'), - ), ] diff --git a/apps/terminal/migrations/0055_auto_20221026_1631.py b/apps/terminal/migrations/0055_auto_20221026_1631.py deleted file mode 100644 index cbfe73256..000000000 --- a/apps/terminal/migrations/0055_auto_20221026_1631.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.2.14 on 2022-10-26 08:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('terminal', '0054_auto_20221024_1452'), - ] - - operations = [ - migrations.RemoveField( - model_name='applet', - name='vcs_type', - ), - migrations.RemoveField( - model_name='applet', - name='vcs_url', - ), - migrations.AddField( - model_name='applet', - name='is_active', - field=models.BooleanField(default=True, verbose_name='Is active'), - ), - ] diff --git a/apps/terminal/models/applet/host.py b/apps/terminal/models/applet/host.py index 89b0bd576..2fee8d15d 100644 --- a/apps/terminal/models/applet/host.py +++ b/apps/terminal/models/applet/host.py @@ -2,14 +2,13 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from common.db.models import JMSBaseModel +from assets.models import Host __all__ = ['AppletHost', 'AppletHostDeployment'] -class AppletHost(JMSBaseModel): - host = models.ForeignKey('assets.Host', on_delete=models.PROTECT, verbose_name=_('Host')) - comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) +class AppletHost(Host): account_automation = models.BooleanField(default=False, verbose_name=_('Account automation')) date_synced = models.DateTimeField(null=True, blank=True, verbose_name=_('Date synced')) status = models.CharField(max_length=16, verbose_name=_('Status')) @@ -19,7 +18,7 @@ class AppletHost(JMSBaseModel): ) def __str__(self): - return self.host.name + return self.name class AppletHostDeployment(JMSBaseModel): diff --git a/apps/terminal/serializers/applet.py b/apps/terminal/serializers/applet.py index 25259b402..5ad2416b8 100644 --- a/apps/terminal/serializers/applet.py +++ b/apps/terminal/serializers/applet.py @@ -2,9 +2,9 @@ from rest_framework import serializers from django.utils.translation import gettext_lazy as _ from common.drf.fields import ObjectRelatedField, LabeledChoiceField -from assets.models import Host, Platform +from common.validators import ProjectUniqueValidator +from assets.models import Platform from assets.serializers import HostSerializer -from orgs.utils import tmp_to_builtin_org from ..models import Applet, AppletPublication, AppletHost, AppletHostDeployment @@ -48,41 +48,42 @@ class AppletPublicationSerializer(serializers.ModelSerializer): ] + read_only_fields -class AppletHostSerializer(serializers.ModelSerializer): - host = HostSerializer(allow_null=True, required=False) - - class Meta: +class AppletHostSerializer(HostSerializer): + class Meta(HostSerializer.Meta): model = AppletHost - fields_mini = ['id', 'host'] - read_only_fields = ['date_synced', 'status', 'date_created', 'date_updated'] - fields = fields_mini + ['comment', 'account_automation'] + read_only_fields + fields = HostSerializer.Meta.fields + [ + 'account_automation', 'status', 'date_synced' + ] + extra_kwargs = { + 'status': {'read_only': True}, + 'date_synced': {'read_only': True} + } - def __init__(self, *args, **kwargs): - self.host_data = kwargs.get('data', {}).pop('host', {}) - super().__init__(*args, **kwargs) + def __init__(self, *args, data=None, **kwargs): + self.set_initial_data(data) + super().__init__(*args, data=data, **kwargs) - def _create_host(self): + @staticmethod + def set_initial_data(data): + if not data: + return platform = Platform.objects.get(name='RemoteAppHost') - data = { - **self.host_data, + data.update({ 'platform': platform.id, 'nodes_display': [ 'RemoteAppHosts' ] - } - serializer = HostSerializer(data=data) - try: - serializer.is_valid(raise_exception=True) - except serializers.ValidationError: - raise serializers.ValidationError({'host': serializer.errors}) - host = serializer.save() - return host + }) - def create(self, validated_data): - with tmp_to_builtin_org(system=1): - host = self._create_host() - instance = super().create({**validated_data, 'host': host}) - return instance + def get_validators(self): + validators = super().get_validators() + # 不知道为啥没有继承过来 + uniq_validator = ProjectUniqueValidator( + queryset=AppletHost.objects.all(), + fields=('org_id', 'name') + ) + validators.append(uniq_validator) + return validators class AppletHostDeploymentSerializer(serializers.ModelSerializer):