From fe4df4b1796eb072d1fa1b8b72112fc25376c7cd Mon Sep 17 00:00:00 2001 From: feng626 <1304903146@qq.com> Date: Fri, 19 Aug 2022 18:49:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dswagger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/__init__.py | 1 + apps/assets/api/account_template.py | 12 ++++++ apps/assets/api/gathered_user.py | 4 +- .../migrations/0111_auto_20220819_1523.py | 40 +++++++++++++++++++ .../migrations/0112_auto_20220819_1526.py | 22 ++++++++++ apps/assets/models/account.py | 13 +++++- apps/assets/models/asset/common.py | 13 ++++++ apps/assets/serializers/__init__.py | 1 + apps/assets/serializers/account.py | 12 ------ apps/assets/serializers/account_template.py | 19 +++++++++ apps/assets/serializers/asset/category.py | 2 +- apps/assets/serializers/base.py | 14 ++++++- apps/assets/urls/api_urls.py | 1 + apps/audits/api.py | 10 ++--- apps/audits/serializers.py | 8 ++-- apps/authentication/api/connection_token.py | 9 +---- apps/ops/api/command.py | 1 - apps/ops/models/command.py | 4 -- apps/ops/serializers/adhoc.py | 3 +- .../api/asset/asset_permission_relation.py | 4 +- .../serializers/asset/user_permission.py | 2 +- 21 files changed, 150 insertions(+), 45 deletions(-) create mode 100644 apps/assets/api/account_template.py create mode 100644 apps/assets/migrations/0111_auto_20220819_1523.py create mode 100644 apps/assets/migrations/0112_auto_20220819_1526.py create mode 100644 apps/assets/serializers/account_template.py diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index 805202615..14fd38e9a 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -7,5 +7,6 @@ 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 * diff --git a/apps/assets/api/account_template.py b/apps/assets/api/account_template.py new file mode 100644 index 000000000..b88fcd0f6 --- /dev/null +++ b/apps/assets/api/account_template.py @@ -0,0 +1,12 @@ +from orgs.mixins.api import OrgBulkModelViewSet +from ..models import AccountTemplate +from .. import serializers + + +class AccountTemplateViewSet(OrgBulkModelViewSet): + model = AccountTemplate + filterset_fields = ("username", 'name') + search_fields = ('username', 'name') + serializer_classes = { + 'default': serializers.AccountTemplateSerializer + } diff --git a/apps/assets/api/gathered_user.py b/apps/assets/api/gathered_user.py index 22be7daf7..c5c9ece64 100644 --- a/apps/assets/api/gathered_user.py +++ b/apps/assets/api/gathered_user.py @@ -16,5 +16,5 @@ class GatheredUserViewSet(OrgModelViewSet): serializer_class = GatheredUserSerializer extra_filter_backends = [AssetRelatedByNodeFilterBackend] - filterset_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__hostname', 'asset_id'] - search_fields = ['username', 'asset__ip', 'asset__hostname'] + filterset_fields = ['asset', 'username', 'present', 'asset__ip', 'asset__name', 'asset_id'] + search_fields = ['username', 'asset__ip', 'asset__name'] diff --git a/apps/assets/migrations/0111_auto_20220819_1523.py b/apps/assets/migrations/0111_auto_20220819_1523.py new file mode 100644 index 000000000..1bed01959 --- /dev/null +++ b/apps/assets/migrations/0111_auto_20220819_1523.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.13 on 2022-08-19 07:23 + +import assets.models.base +import common.db.fields +from django.db import migrations, models +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0110_auto_20220817_1716'), + ] + + operations = [ + migrations.CreateModel( + name='AccountTemplate', + fields=[ + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('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')), + ('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')), + ('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')), + ('comment', models.TextField(blank=True, verbose_name='Comment')), + ('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')), + ('token', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Token')), + ('privileged', models.BooleanField(default=False, verbose_name='Privileged account')), + ], + options={ + 'verbose_name': 'Account template', + }, + bases=(models.Model, assets.models.base.AuthMixin), + ) + ] diff --git a/apps/assets/migrations/0112_auto_20220819_1526.py b/apps/assets/migrations/0112_auto_20220819_1526.py new file mode 100644 index 000000000..14e7d7765 --- /dev/null +++ b/apps/assets/migrations/0112_auto_20220819_1526.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.13 on 2022-08-19 07:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0111_auto_20220819_1523'), + ] + + operations = [ + migrations.RemoveField( + model_name='platform', + name='protocols_default', + ), + migrations.AddField( + model_name='platform', + name='protocols_enabled', + field=models.BooleanField(default=True, verbose_name='Protocols enabled'), + ), + ] diff --git a/apps/assets/models/account.py b/apps/assets/models/account.py index c48fd0256..585307324 100644 --- a/apps/assets/models/account.py +++ b/apps/assets/models/account.py @@ -5,7 +5,7 @@ from simple_history.models import HistoricalRecords from common.db import fields from .base import BaseAccount, AbsConnectivity -__all__ = ['Account'] +__all__ = ['Account', 'AccountTemplate'] class Account(BaseAccount, AbsConnectivity): @@ -27,3 +27,14 @@ class Account(BaseAccount, AbsConnectivity): def __str__(self): return '{}@{}'.format(self.username, self.asset.name) + + +class AccountTemplate(BaseAccount, AbsConnectivity): + token = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Token')) + privileged = models.BooleanField(verbose_name=_("Privileged account"), default=False) + + class Meta: + verbose_name = _('Account template') + + def __str__(self): + return '{}@{}'.format(self.username, self.name) diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index dd8d07dd1..1e4c3d71f 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -98,6 +98,11 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel): return False, warning return True, warning + def domain_display(self): + if self.domain: + return self.domain.name + return '' + def nodes_display(self): names = [] for n in self.nodes.all(): @@ -114,10 +119,18 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel): def type(self): return self.platform.type + @property + def type_display(self): + return self.platform.type + @property def category(self): return self.platform.category + @property + def category_display(self): + return self.platform.category + def as_node(self): from assets.models import Node fake_node = Node() diff --git a/apps/assets/serializers/__init__.py b/apps/assets/serializers/__init__.py index ded671746..a86110712 100644 --- a/apps/assets/serializers/__init__.py +++ b/apps/assets/serializers/__init__.py @@ -9,5 +9,6 @@ from .gathered_user import * from .favorite_asset import * from .account import * from .account_history import * +from .account_template import * from .backup import * from .platform import * diff --git a/apps/assets/serializers/account.py b/apps/assets/serializers/account.py index 3538676b0..6695aa348 100644 --- a/apps/assets/serializers/account.py +++ b/apps/assets/serializers/account.py @@ -6,7 +6,6 @@ from assets.models import Account from orgs.mixins.serializers import BulkOrgResourceModelSerializer from .base import AuthSerializerMixin -from common.utils.encode import ssh_pubkey_gen from common.drf.serializers import SecretReadableMixin @@ -33,17 +32,6 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): } ref_name = 'AssetAccountSerializer' - def _validate_gen_key(self, attrs): - private_key = attrs.get('private_key') - if not private_key: - return attrs - - password = attrs.get('passphrase') - username = attrs.get('username') - public_key = ssh_pubkey_gen(private_key, password=password, username=username) - attrs['public_key'] = public_key - return attrs - def validate(self, attrs): attrs = self._validate_gen_key(attrs) return attrs diff --git a/apps/assets/serializers/account_template.py b/apps/assets/serializers/account_template.py new file mode 100644 index 000000000..ba25ac8dd --- /dev/null +++ b/apps/assets/serializers/account_template.py @@ -0,0 +1,19 @@ +from assets.models import AccountTemplate +from orgs.mixins.serializers import BulkOrgResourceModelSerializer + +from .base import AuthSerializerMixin +from .account import AccountSerializer + + +class AccountTemplateSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): + class Meta: + model = AccountTemplate + fields_mini = ['id', 'privileged', 'username'] + fields_write_only = AccountSerializer.Meta.fields_write_only + fields_other = AccountSerializer.Meta.fields_other + fields = fields_mini + fields_write_only + fields_other + extra_kwargs = AccountSerializer.Meta.extra_kwargs + + def validate(self, attrs): + attrs = self._validate_gen_key(attrs) + return attrs diff --git a/apps/assets/serializers/asset/category.py b/apps/assets/serializers/asset/category.py index 78c3180cd..971a69471 100644 --- a/apps/assets/serializers/asset/category.py +++ b/apps/assets/serializers/asset/category.py @@ -31,7 +31,7 @@ class DatabaseSerializer(AssetSerializer): class Meta(AssetSerializer.Meta): model = Database fields_mini = [ - 'id', 'name', 'ip', 'port', 'db_name', + 'id', 'name', 'ip', 'db_name', ] fields_small = fields_mini + [ 'is_active', 'comment', diff --git a/apps/assets/serializers/base.py b/apps/assets/serializers/base.py index 92249705d..aba1ddfe8 100644 --- a/apps/assets/serializers/base.py +++ b/apps/assets/serializers/base.py @@ -13,7 +13,8 @@ from .utils import validate_password_for_ansible class AuthSerializer(serializers.ModelSerializer): password = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=1024, label=_('Password')) - private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=16384, label=_('Private key')) + private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=16384, + label=_('Private key')) def gen_keys(self, private_key=None, password=None): if private_key is None: @@ -74,6 +75,17 @@ class AuthSerializerMixin(serializers.ModelSerializer): validated_data.pop(field, None) validated_data.pop('passphrase', None) + def _validate_gen_key(self, attrs): + private_key = attrs.get('private_key') + if not private_key: + return attrs + + password = attrs.get('passphrase') + username = attrs.get('username') + public_key = ssh_pubkey_gen(private_key, password=password, username=username) + attrs['public_key'] = public_key + return attrs + def create(self, validated_data): self.clean_auth_fields(validated_data) return super().create(validated_data) diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 3e6b1309b..9d73acfa6 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -14,6 +14,7 @@ router.register(r'assets', api.AssetViewSet, 'asset') router.register(r'hosts', api.HostViewSet, 'host') router.register(r'databases', api.DatabaseViewSet, 'database') router.register(r'accounts', api.AccountViewSet, 'account') +router.register(r'account-templates', api.AccountTemplateViewSet, 'account-template') router.register(r'account-secrets', api.AccountSecretsViewSet, 'account-secret') router.register(r'accounts-history', api.AccountHistoryViewSet, 'account-history') router.register(r'account-history-secrets', api.AccountHistorySecretsViewSet, 'account-history-secret') diff --git a/apps/audits/api.py b/apps/audits/api.py index 2abc52a57..6df4ec861 100644 --- a/apps/audits/api.py +++ b/apps/audits/api.py @@ -107,11 +107,11 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet): ] filterset_fields = [ 'user__name', 'user__username', 'command', - 'run_as__name', 'run_as__username', 'is_finished' + 'account', 'is_finished' ] search_fields = [ 'command', 'user__name', 'user__username', - 'run_as__name', 'run_as__username', + 'account__username', ] ordering = ['-date_created'] @@ -121,7 +121,7 @@ class CommandExecutionViewSet(ListModelMixin, OrgGenericViewSet): return queryset.model.objects.none() if current_org.is_root(): return queryset - queryset = queryset.filter(run_as__org_id=current_org.org_id()) + # queryset = queryset.filter(run_as__org_id=current_org.org_id()) return queryset @@ -131,7 +131,7 @@ class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet) filterset_fields = [ 'id', 'asset', 'commandexecution' ] - search_fields = ('asset__hostname', ) + search_fields = ('asset__name', ) http_method_names = ['options', 'get'] rbac_perms = { 'GET': 'ops.view_commandexecution', @@ -142,7 +142,7 @@ class CommandExecutionHostRelationViewSet(OrgRelationMixin, OrgBulkModelViewSet) queryset = super().get_queryset() queryset = queryset.annotate( asset_display=Concat( - F('asset__hostname'), Value('('), + F('asset__name'), Value('('), F('asset__ip'), Value(')') ) ) diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index bc78ef238..9895176fd 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -88,24 +88,22 @@ class CommandExecutionSerializer(serializers.ModelSerializer): model = CommandExecution fields_mini = ['id'] fields_small = fields_mini + [ - 'run_as', 'command', 'is_finished', 'user', + 'command', 'is_finished', 'user', 'date_start', 'result', 'is_success', 'org_id' ] - fields = fields_small + ['hosts', 'hosts_display', 'run_as_display', 'user_display'] + fields = fields_small + ['hosts', 'hosts_display', 'user_display'] extra_kwargs = { 'result': {'label': _('Result')}, # model 上的方法,只能在这修改 'is_success': {'label': _('Is success')}, 'hosts': {'label': _('Hosts')}, # 外键,会生成 sql。不在 model 上修改 - 'run_as': {'label': _('Run as')}, 'user': {'label': _('User')}, - 'run_as_display': {'label': _('Run as display')}, 'user_display': {'label': _('User display')}, } @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.prefetch_related('user', 'run_as', 'hosts') + queryset = queryset.prefetch_related('user', 'hosts') return queryset diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index ce84cb74e..8ab5ecee9 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -45,18 +45,12 @@ class ConnectionTokenMixin: @staticmethod def check_user_has_resource_permission(user, asset, application, system_user): from perms.utils.asset import has_asset_system_permission - from perms.utils.application import has_application_system_permission if asset and not has_asset_system_permission(user, asset, system_user): error = f'User not has this asset and system user permission: ' \ f'user={user.id} system_user={system_user.id} asset={asset.id}' raise PermissionDenied(error) - if application and not has_application_system_permission(user, application, system_user): - error = f'User not has this application and system user permission: ' \ - f'user={user.id} system_user={system_user.id} application={application.id}' - raise PermissionDenied(error) - def get_smart_endpoint(self, protocol, asset=None, application=None): if asset: target_ip = asset.get_target_ip() @@ -204,8 +198,7 @@ class ConnectionTokenMixin: class ConnectionTokenViewSet(ConnectionTokenMixin, RootOrgViewMixin, JMSModelViewSet): filterset_fields = ( - 'type', - 'user_display', 'system_user_display', 'application_display', 'asset_display' + 'type', 'user_display', 'asset_display' ) search_fields = filterset_fields serializer_classes = { diff --git a/apps/ops/api/command.py b/apps/ops/api/command.py index 8c716fb03..1cf7950a6 100644 --- a/apps/ops/api/command.py +++ b/apps/ops/api/command.py @@ -25,7 +25,6 @@ class CommandExecutionViewSet(RootOrgViewMixin, viewsets.ModelViewSet): def check_hosts(self, serializer): data = serializer.validated_data assets = data["hosts"] - system_user = data["run_as"] user = self.request.user # TOdo: diff --git a/apps/ops/models/command.py b/apps/ops/models/command.py index 14cb99081..cb6023564 100644 --- a/apps/ops/models/command.py +++ b/apps/ops/models/command.py @@ -47,10 +47,6 @@ class CommandExecution(OrgModelMixin): inv = JMSInventory(self.allow_assets, run_as=username, system_user=self.run_as) return inv - @lazyproperty - def run_as_display(self): - return str(self.run_as) - @lazyproperty def user_display(self): return str(self.user) diff --git a/apps/ops/serializers/adhoc.py b/apps/ops/serializers/adhoc.py index 094abf958..50b1faeba 100644 --- a/apps/ops/serializers/adhoc.py +++ b/apps/ops/serializers/adhoc.py @@ -138,9 +138,8 @@ class CommandExecutionSerializer(serializers.ModelSerializer): 'command', 'result', 'log_url', 'is_finished', 'date_created', 'date_finished' ] - fields_fk = ['run_as'] fields_m2m = ['hosts'] - fields = fields_small + fields_fk + fields_m2m + fields = fields_small + fields_m2m read_only_fields = [ 'result', 'is_finished', 'log_url', 'date_created', 'date_finished' diff --git a/apps/perms/api/asset/asset_permission_relation.py b/apps/perms/api/asset/asset_permission_relation.py index ecf0d731b..902d45864 100644 --- a/apps/perms/api/asset/asset_permission_relation.py +++ b/apps/perms/api/asset/asset_permission_relation.py @@ -80,12 +80,12 @@ class AssetPermissionAssetRelationViewSet(RelationMixin): filterset_fields = [ 'id', 'asset', 'assetpermission', ] - search_fields = ["id", "asset__hostname", "asset__ip", "assetpermission__name"] + search_fields = ["id", "asset__name", "asset__ip", "assetpermission__name"] def get_queryset(self): queryset = super().get_queryset() queryset = queryset \ - .annotate(asset_display=F('asset__hostname')) + .annotate(asset_display=F('asset__name')) return queryset diff --git a/apps/perms/serializers/asset/user_permission.py b/apps/perms/serializers/asset/user_permission.py index f05834f1b..c0d9b4c74 100644 --- a/apps/perms/serializers/asset/user_permission.py +++ b/apps/perms/serializers/asset/user_permission.py @@ -25,7 +25,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer): class Meta: model = Asset only_fields = [ - "id", "name", "ip", "protocols", "os", 'domain', + "id", "name", "ip", "protocols", 'domain', "platform", "comment", "org_id", "is_active" ] fields = only_fields + ['org_name']