mirror of https://github.com/jumpserver/jumpserver
				
				
				
			perf: 修改 platform
							parent
							
								
									a9bf4eddea
								
							
						
					
					
						commit
						05f913ab18
					
				| 
						 | 
				
			
			@ -29,7 +29,6 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
 | 
			
		|||
    filterset_fields = {
 | 
			
		||||
        'name': ['exact'],
 | 
			
		||||
        'ip': ['exact'],
 | 
			
		||||
        'system_users__id': ['exact'],
 | 
			
		||||
        'is_active': ['exact'],
 | 
			
		||||
        'protocols': ['exact', 'icontains']
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,9 +18,6 @@ def create_app_platform(apps, *args):
 | 
			
		|||
        {'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'},
 | 
			
		||||
        {'name': 'Redis', 'category': 'database', 'type': 'redis'},
 | 
			
		||||
        {'name': 'Chrome', 'category': 'remote_app', 'type': 'chrome'},
 | 
			
		||||
        {'name': 'vSphereClient', 'category': 'remote_app', 'type': 'vmware_client'},
 | 
			
		||||
        {'name': 'MySQLWorkbench', 'category': 'remote_app', 'type': 'mysql_workbench'},
 | 
			
		||||
        {'name': 'GeneralRemoteApp', 'category': 'remote_app', 'type': 'general_remote_app'},
 | 
			
		||||
        {'name': 'Kubernetes', 'category': 'cloud', 'type': 'k8s'},
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,15 @@ class Migration(migrations.Migration):
 | 
			
		|||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name='PlatformProtocol',
 | 
			
		||||
            fields=[
 | 
			
		||||
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
 | 
			
		||||
                ('name', models.CharField(max_length=32, verbose_name='Name')),
 | 
			
		||||
                ('port', models.IntegerField(verbose_name='Port')),
 | 
			
		||||
                ('setting', models.JSONField(default=dict, verbose_name='Setting')),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='platform',
 | 
			
		||||
            name='domain_default',
 | 
			
		||||
| 
						 | 
				
			
			@ -28,8 +37,8 @@ class Migration(migrations.Migration):
 | 
			
		|||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='platform',
 | 
			
		||||
            name='protocols_enabled',
 | 
			
		||||
            field=models.BooleanField(default=True, verbose_name='Protocols enabled'),
 | 
			
		||||
            name='protocols',
 | 
			
		||||
            field=models.ManyToManyField(blank=True, to='assets.PlatformProtocol', verbose_name='Protocols'),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name='platform',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,137 +0,0 @@
 | 
			
		|||
# -*- coding: utf-8 -*-
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models import F
 | 
			
		||||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
from simple_history.models import HistoricalRecords
 | 
			
		||||
 | 
			
		||||
from common.utils import lazyproperty, get_logger
 | 
			
		||||
from .base import BaseAccount, AbsConnectivity
 | 
			
		||||
 | 
			
		||||
logger = get_logger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['AuthBook']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AuthBook(BaseAccount, AbsConnectivity):
 | 
			
		||||
    asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset'))
 | 
			
		||||
    systemuser = models.ForeignKey('assets.SystemUser', on_delete=models.CASCADE, null=True, verbose_name=_("System user"))
 | 
			
		||||
    version = models.IntegerField(default=1, verbose_name=_('Version'))
 | 
			
		||||
    history = HistoricalRecords()
 | 
			
		||||
 | 
			
		||||
    auth_attrs = ['username', 'password', 'private_key', 'public_key']
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('AuthBook')
 | 
			
		||||
        unique_together = [('username', 'asset', 'systemuser')]
 | 
			
		||||
        permissions = [
 | 
			
		||||
            ('test_authbook', _('Can test asset account connectivity')),
 | 
			
		||||
            ('view_assetaccountsecret', _('Can view asset account secret')),
 | 
			
		||||
            ('change_assetaccountsecret', _('Can change asset account secret')),
 | 
			
		||||
            ('view_assethistoryaccount', _('Can view asset history account')),
 | 
			
		||||
            ('view_assethistoryaccountsecret', _('Can view asset history account secret')),
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.auth_snapshot = {}
 | 
			
		||||
 | 
			
		||||
    def get_or_systemuser_attr(self, attr):
 | 
			
		||||
        val = getattr(self, attr, None)
 | 
			
		||||
        if val:
 | 
			
		||||
            return val
 | 
			
		||||
        if self.systemuser:
 | 
			
		||||
            return getattr(self.systemuser, attr, '')
 | 
			
		||||
        return ''
 | 
			
		||||
 | 
			
		||||
    def load_auth(self):
 | 
			
		||||
        for attr in self.auth_attrs:
 | 
			
		||||
            value = self.get_or_systemuser_attr(attr)
 | 
			
		||||
            self.auth_snapshot[attr] = [getattr(self, attr), value]
 | 
			
		||||
            setattr(self, attr, value)
 | 
			
		||||
 | 
			
		||||
    def unload_auth(self):
 | 
			
		||||
        if not self.systemuser:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for attr, values in self.auth_snapshot.items():
 | 
			
		||||
            origin_value, loaded_value = values
 | 
			
		||||
            current_value = getattr(self, attr, '')
 | 
			
		||||
            if current_value == loaded_value:
 | 
			
		||||
                setattr(self, attr, origin_value)
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        self.unload_auth()
 | 
			
		||||
        instance = super().save(*args, **kwargs)
 | 
			
		||||
        self.load_auth()
 | 
			
		||||
        return instance
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def username_display(self):
 | 
			
		||||
        return self.get_or_systemuser_attr('username') or '*'
 | 
			
		||||
 | 
			
		||||
    @lazyproperty
 | 
			
		||||
    def systemuser_display(self):
 | 
			
		||||
        if not self.systemuser:
 | 
			
		||||
            return ''
 | 
			
		||||
        return str(self.systemuser)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def smart_name(self):
 | 
			
		||||
        username = self.username_display
 | 
			
		||||
 | 
			
		||||
        if self.asset:
 | 
			
		||||
            asset = str(self.asset)
 | 
			
		||||
        else:
 | 
			
		||||
            asset = '*'
 | 
			
		||||
        return '{}@{}'.format(username, asset)
 | 
			
		||||
 | 
			
		||||
    def sync_to_system_user_account(self):
 | 
			
		||||
        if self.systemuser:
 | 
			
		||||
            return
 | 
			
		||||
        matched = AuthBook.objects.filter(
 | 
			
		||||
            asset=self.asset, systemuser__username=self.username
 | 
			
		||||
        )
 | 
			
		||||
        if not matched:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        for i in matched:
 | 
			
		||||
            i.password = self.password
 | 
			
		||||
            i.private_key = self.private_key
 | 
			
		||||
            i.public_key = self.public_key
 | 
			
		||||
            i.comment = 'Update triggered by account {}'.format(self.id)
 | 
			
		||||
 | 
			
		||||
        # 不触发post_save信号
 | 
			
		||||
        self.__class__.objects.bulk_update(matched, fields=['password', 'private_key', 'public_key'])
 | 
			
		||||
 | 
			
		||||
    def remove_asset_admin_user_if_need(self):
 | 
			
		||||
        if not self.asset or not self.systemuser:
 | 
			
		||||
            return
 | 
			
		||||
        if not self.systemuser.is_admin_user or self.asset.admin_user != self.systemuser:
 | 
			
		||||
            return
 | 
			
		||||
        self.asset.admin_user = None
 | 
			
		||||
        self.asset.save()
 | 
			
		||||
        logger.debug('Remove asset admin user: {} {}'.format(self.asset, self.systemuser))
 | 
			
		||||
 | 
			
		||||
    def update_asset_admin_user_if_need(self):
 | 
			
		||||
        if not self.asset or not self.systemuser:
 | 
			
		||||
            return
 | 
			
		||||
        if not self.systemuser.is_admin_user or self.asset.admin_user == self.systemuser:
 | 
			
		||||
            return
 | 
			
		||||
        self.asset.admin_user = self.systemuser
 | 
			
		||||
        self.asset.save()
 | 
			
		||||
        logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser))
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_queryset(cls):
 | 
			
		||||
        queryset = cls.objects.all() \
 | 
			
		||||
            .annotate(ip=F('asset__ip')) \
 | 
			
		||||
            .annotate(hostname=F('asset__hostname')) \
 | 
			
		||||
            .annotate(platform=F('asset__platform__name')) \
 | 
			
		||||
            .annotate(protocols=F('asset__protocols'))
 | 
			
		||||
        return queryset
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.smart_name
 | 
			
		||||
| 
						 | 
				
			
			@ -157,13 +157,6 @@ class Asset(AbsConnectivity, NodesRelationMixin, JMSOrgBaseModel):
 | 
			
		|||
        tree_node = TreeNode(**data)
 | 
			
		||||
        return tree_node
 | 
			
		||||
 | 
			
		||||
    def get_all_system_users(self):
 | 
			
		||||
        from assets.models import SystemUser
 | 
			
		||||
        system_user_ids = SystemUser.assets.through.objects.filter(asset=self) \
 | 
			
		||||
            .values_list('systemuser_id', flat=True)
 | 
			
		||||
        system_users = SystemUser.objects.filter(id__in=system_user_ids)
 | 
			
		||||
        return system_users
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        unique_together = [('org_id', 'name')]
 | 
			
		||||
        verbose_name = _("Asset")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,13 @@ from assets.const import Category, AllTypes
 | 
			
		|||
from common.db.fields import JsonDictTextField
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ['Platform']
 | 
			
		||||
__all__ = ['Platform', 'PlatformProtocol']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlatformProtocol(models.Model):
 | 
			
		||||
    name = models.CharField(max_length=32, verbose_name=_('Name'))
 | 
			
		||||
    port = models.IntegerField(verbose_name=_('Port'))
 | 
			
		||||
    setting = models.JSONField(verbose_name=_('Setting'), default=dict)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Platform(models.Model):
 | 
			
		||||
| 
						 | 
				
			
			@ -30,9 +36,7 @@ class Platform(models.Model):
 | 
			
		|||
        verbose_name=_("Domain default")
 | 
			
		||||
    )
 | 
			
		||||
    protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
 | 
			
		||||
    protocols_default = models.JSONField(
 | 
			
		||||
        max_length=128, default=list, blank=True, verbose_name=_("Protocols default")
 | 
			
		||||
    )
 | 
			
		||||
    protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols"))
 | 
			
		||||
    # Accounts
 | 
			
		||||
    # 这应该和账号有关
 | 
			
		||||
    su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,10 @@ from rest_framework import serializers
 | 
			
		|||
from django.utils.translation import ugettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from common.drf.serializers import JMSWritableNestedModelSerializer
 | 
			
		||||
from common.drf.fields import ChoiceDisplayField
 | 
			
		||||
from ..account import AccountSerializer
 | 
			
		||||
from ...models import Asset, Node, Platform, Protocol, Label, Domain, Account
 | 
			
		||||
from ...const import Category, AllTypes
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
 | 
			
		||||
| 
						 | 
				
			
			@ -57,6 +59,8 @@ class AssetNodesSerializer(serializers.ModelSerializer):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class AssetSerializer(JMSWritableNestedModelSerializer):
 | 
			
		||||
    category = ChoiceDisplayField(choices=Category.choices, read_only=True, label=_('Category'))
 | 
			
		||||
    type = ChoiceDisplayField(choices=AllTypes.choices, read_only=True, label=_('Type'))
 | 
			
		||||
    domain = AssetDomainSerializer(required=False)
 | 
			
		||||
    platform = AssetPlatformSerializer(required=False)
 | 
			
		||||
    labels = AssetLabelSerializer(many=True, required=False)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,42 +1,43 @@
 | 
			
		|||
from rest_framework import serializers
 | 
			
		||||
from django.core.validators import RegexValidator
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from assets.models import Platform
 | 
			
		||||
from .mixin import CategoryDisplayMixin
 | 
			
		||||
from common.drf.fields import ChoiceDisplayField
 | 
			
		||||
from common.drf.serializers import JMSWritableNestedModelSerializer
 | 
			
		||||
from ..models import Platform, PlatformProtocol
 | 
			
		||||
from ..const import Category, AllTypes
 | 
			
		||||
 | 
			
		||||
__all__ = ['PlatformSerializer']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlatformProtocolsSerializer(serializers.Serializer):
 | 
			
		||||
    name = serializers.CharField(max_length=255, required=True)
 | 
			
		||||
    port = serializers.IntegerField(max_value=65535, min_value=1, required=True)
 | 
			
		||||
class PlatformProtocolsSerializer(serializers.ModelSerializer):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = PlatformProtocol
 | 
			
		||||
        fields = ['id', 'name', 'port', 'setting']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PlatformSerializer(CategoryDisplayMixin, serializers.ModelSerializer):
 | 
			
		||||
    meta = serializers.DictField(required=False, allow_null=True, label=_('Meta'))
 | 
			
		||||
    protocols_default = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
 | 
			
		||||
class PlatformSerializer(JMSWritableNestedModelSerializer):
 | 
			
		||||
    type = ChoiceDisplayField(choices=AllTypes.choices, label=_("Type"))
 | 
			
		||||
    category = ChoiceDisplayField(choices=Category.choices, label=_("Category"))
 | 
			
		||||
    protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
 | 
			
		||||
    type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Platform
 | 
			
		||||
        fields_mini = ['id', 'name', 'internal']
 | 
			
		||||
        fields_small = fields_mini + [
 | 
			
		||||
            'meta', 'comment', 'charset',
 | 
			
		||||
            'category', 'category_display',
 | 
			
		||||
            'type', 'type_display',
 | 
			
		||||
            'category', 'type',
 | 
			
		||||
        ]
 | 
			
		||||
        fields = fields_small + [
 | 
			
		||||
            'domain_enabled', 'domain_default',
 | 
			
		||||
            'su_enabled', 'su_method',
 | 
			
		||||
            'protocols_enabled', 'protocols',
 | 
			
		||||
            'ping_enabled', 'ping_method',
 | 
			
		||||
            'verify_account_enabled', 'verify_account_method',
 | 
			
		||||
            'create_account_enabled', 'create_account_method',
 | 
			
		||||
            'change_password_enabled', 'change_password_method',
 | 
			
		||||
            'type_constraints',
 | 
			
		||||
            'comment', 'charset',
 | 
			
		||||
        ]
 | 
			
		||||
        fields_fk = [
 | 
			
		||||
            'domain_enabled', 'domain_default',
 | 
			
		||||
            'protocols_enabled', 'protocols_default',
 | 
			
		||||
        ]
 | 
			
		||||
        fields = fields_small + fields_fk
 | 
			
		||||
        read_only_fields = [
 | 
			
		||||
            'category_display', 'type_display',
 | 
			
		||||
        ]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,6 @@ class ChoiceDisplayField(ChoiceField):
 | 
			
		|||
        if value in ('', None):
 | 
			
		||||
            return value
 | 
			
		||||
        return {
 | 
			
		||||
            'name': value,
 | 
			
		||||
            'value': value,
 | 
			
		||||
            'label': self.choice_mapper.get(six.text_type(value), value),
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,7 +28,9 @@ class CommandExecutionViewSet(RootOrgViewMixin, viewsets.ModelViewSet):
 | 
			
		|||
        system_user = data["run_as"]
 | 
			
		||||
        user = self.request.user
 | 
			
		||||
 | 
			
		||||
        q = Q(granted_by_permissions__system_users__id=system_user.id) & (
 | 
			
		||||
        # TOdo:
 | 
			
		||||
        # Q(granted_by_permissions__system_users__id=system_user.id) &
 | 
			
		||||
        q = (
 | 
			
		||||
                Q(granted_by_permissions__users=user) |
 | 
			
		||||
                Q(granted_by_permissions__user_groups__users=user)
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue