diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index c0dda34c7..a02d35121 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -2,7 +2,6 @@ # import django_filters from django.db.models import Q -from django.utils.translation import ugettext_lazy as _ from rest_framework.decorators import action from rest_framework.response import Response @@ -72,11 +71,13 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): ("platform", serializers.PlatformSerializer), ("suggestion", serializers.MiniAssetSerializer), ("gateways", serializers.GatewaySerializer), + ("spec_info", serializers.SpecSerializer), ) rbac_perms = ( ("match", "assets.match_asset"), ("platform", "assets.view_platform"), ("gateways", "assets.view_gateway"), + ("spec_info", "assets.view_asset"), ) extra_filter_backends = [LabelFilterBackend, IpInFilterBackend, NodeFilterBackend] @@ -94,6 +95,11 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): serializer = super().get_serializer(instance=asset.platform) return Response(serializer.data) + @action(methods=["GET"], detail=True, url_path="spec-info") + def spec_info(self, *args, **kwargs): + asset = super().get_object() + return Response(asset.spec_info) + @action(methods=["GET"], detail=True, url_path="gateways") def gateways(self, *args, **kwargs): asset = self.get_object() diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 60f2f3c14..dfbf2ecf0 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -10,8 +10,8 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from assets import const -from common.utils import lazyproperty from common.db.fields import EncryptMixin +from common.utils import lazyproperty from orgs.mixins.models import OrgManager, JMSOrgBaseModel from ..base import AbsConnectivity from ..platform import Platform @@ -113,45 +113,47 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel): verbose_name=_("Nodes")) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels")) - info = models.JSONField(verbose_name='Info', default=dict, blank=True) + info = models.JSONField(verbose_name='Info', default=dict, blank=True) # 资产的一些信息,如 硬件信息 objects = AssetManager.from_queryset(AssetQuerySet)() def __str__(self): return '{0.name}({0.address})'.format(self) - @property - def specific(self): - instance = getattr(self, self.category, None) - if not instance: - return {} - specific_fields = self.get_specific_fields(instance) + @staticmethod + def get_spec_values(instance, fields): info = {} - for i in specific_fields: + for i in fields: v = getattr(instance, i.name) if isinstance(i, models.JSONField) and not isinstance(v, (list, dict)): v = json.loads(v) info[i.name] = v return info - @property + @lazyproperty def spec_info(self): instance = getattr(self, self.category, None) if not instance: - return [] - specific_fields = self.get_specific_fields(instance) - info = [ - { - 'label': i.verbose_name, - 'name': i.name, - 'value': getattr(instance, i.name) - } - for i in specific_fields - ] - return info + return {} + spec_fields = self.get_spec_fields(instance) + return self.get_spec_values(instance, spec_fields) + + @staticmethod + def get_spec_fields(instance, secret=False): + spec_fields = [i for i in instance._meta.local_fields if i.name != 'asset_ptr'] + spec_fields = [i for i in spec_fields if isinstance(i, EncryptMixin) == secret] + return spec_fields @lazyproperty - def enabled_info(self): + def secret_info(self): + instance = getattr(self, self.category, None) + if not instance: + return {} + spec_fields = self.get_spec_fields(instance, secret=True) + return self.get_spec_values(instance, spec_fields) + + @lazyproperty + def auto_info(self): platform = self.platform automation = self.platform.automation return { @@ -165,12 +167,6 @@ class Asset(NodesRelationMixin, AbsConnectivity, JMSOrgBaseModel): 'gather_accounts_enabled': automation.gather_accounts_enabled, } - @staticmethod - def get_specific_fields(instance): - specific_fields = [i for i in instance._meta.local_fields if i.name != 'asset_ptr'] - specific_fields = [i for i in specific_fields if not isinstance(i, EncryptMixin)] - return specific_fields - def get_target_ip(self): return self.address diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 89d5a66eb..47c866603 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -18,7 +18,7 @@ __all__ = [ 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetTaskSerializer', 'AssetsTaskSerializer', 'AssetProtocolsSerializer', 'AssetDetailSerializer', 'DetailMixin', 'AssetAccountSerializer', - 'AccountSecretSerializer' + 'AccountSecretSerializer', 'SpecSerializer' ] @@ -113,13 +113,25 @@ class AccountSecretSerializer(SecretReadableMixin, CommonModelSerializer): } +class SpecSerializer(serializers.Serializer): + # 数据库 + db_name = serializers.CharField(label=_("Database"), max_length=128, required=False) + use_ssl = serializers.BooleanField(label=_("Use SSL"), required=False) + allow_invalid_cert = serializers.BooleanField(label=_("Allow invalid cert"), required=False) + # Web + autofill = serializers.CharField(label=_("Auto fill"), required=False) + username_selector = serializers.CharField(label=_("Username selector"), required=False) + password_selector = serializers.CharField(label=_("Password selector"), required=False) + submit_selector = serializers.CharField(label=_("Submit selector"), required=False) + script = serializers.JSONField(label=_("Script"), required=False) + + class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSerializer): category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) labels = AssetLabelSerializer(many=True, required=False, label=_('Label')) protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols')) accounts = AssetAccountSerializer(many=True, required=False, write_only=True, label=_('Account')) - enabled_info = serializers.DictField(read_only=True, label=_('Enabled info')) class Meta: model = Asset @@ -127,11 +139,11 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali fields_small = fields_mini + ['is_active', 'comment'] fields_fk = ['domain', 'platform'] fields_m2m = [ - 'nodes', 'labels', 'protocols', 'nodes_display', 'accounts' + 'nodes', 'labels', 'protocols', + 'nodes_display', 'accounts' ] read_only_fields = [ - 'category', 'type', 'info', 'enabled_info', - 'connectivity', 'date_verified', + 'category', 'type', 'connectivity', 'date_verified', 'created_by', 'date_created' ] fields = fields_small + fields_fk + fields_m2m + read_only_fields @@ -235,11 +247,13 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali class DetailMixin(serializers.Serializer): accounts = AssetAccountSerializer(many=True, required=False, label=_('Accounts')) + spec_info = serializers.DictField(label=_('Spec info'), read_only=True) + auto_info = serializers.DictField(read_only=True, label=_('Auto info')) def get_field_names(self, declared_fields, info): names = super().get_field_names(declared_fields, info) names.extend([ - 'accounts', 'info', 'specific', 'spec_info' + 'accounts', 'info', 'spec_info', 'auto_info' ]) return names diff --git a/apps/authentication/serializers/connect_token_secret.py b/apps/authentication/serializers/connect_token_secret.py index 4585111ea..41ee8d116 100644 --- a/apps/authentication/serializers/connect_token_secret.py +++ b/apps/authentication/serializers/connect_token_secret.py @@ -31,7 +31,8 @@ class _ConnectionTokenAssetSerializer(serializers.ModelSerializer): model = Asset fields = [ 'id', 'name', 'address', 'protocols', - 'category', 'type', 'org_id', 'specific' + 'category', 'type', 'org_id', 'spec_info', + 'secret_info', ] diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index e50a0801f..243f1b319 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -105,7 +105,7 @@ class JMSInventory: 'id': str(asset.id), 'name': asset.name, 'address': asset.address, 'type': asset.type, 'category': asset.category, 'protocol': asset.protocol, 'port': asset.port, - 'specific': asset.specific, + 'specific': asset.spec, 'protocols': [{'name': p.name, 'port': p.port} for p in protocols], }, 'jms_account': { diff --git a/apps/terminal/applets/navicat/app.py b/apps/terminal/applets/navicat/app.py index 33d89d2d2..37c80f144 100644 --- a/apps/terminal/applets/navicat/app.py +++ b/apps/terminal/applets/navicat/app.py @@ -11,7 +11,6 @@ if sys.platform == 'win32': ) from common import wait_pid, BaseApplication - _default_path = r'C:\Program Files\PremiumSoft\Navicat Premium 16\navicat.exe' @@ -24,7 +23,7 @@ class AppletApplication(BaseApplication): self.privileged = self.account.privileged self.host = self.asset.address self.port = self.asset.get_protocol_port(self.protocol) - self.db = self.asset.specific.db_name + self.db = self.asset.spec.db_name self.name = '%s-%s' % (self.host, self.db) self.pid = None self.app = None