mirror of https://github.com/jumpserver/jumpserver
perf: 修改 platform
parent
a9bf4eddea
commit
05f913ab18
|
@ -29,7 +29,6 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet)
|
||||||
filterset_fields = {
|
filterset_fields = {
|
||||||
'name': ['exact'],
|
'name': ['exact'],
|
||||||
'ip': ['exact'],
|
'ip': ['exact'],
|
||||||
'system_users__id': ['exact'],
|
|
||||||
'is_active': ['exact'],
|
'is_active': ['exact'],
|
||||||
'protocols': ['exact', 'icontains']
|
'protocols': ['exact', 'icontains']
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,6 @@ def create_app_platform(apps, *args):
|
||||||
{'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'},
|
{'name': 'MongoDB', 'category': 'database', 'type': 'mongodb'},
|
||||||
{'name': 'Redis', 'category': 'database', 'type': 'redis'},
|
{'name': 'Redis', 'category': 'database', 'type': 'redis'},
|
||||||
{'name': 'Chrome', 'category': 'remote_app', 'type': 'chrome'},
|
{'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'},
|
{'name': 'Kubernetes', 'category': 'cloud', 'type': 'k8s'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,15 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
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(
|
migrations.AddField(
|
||||||
model_name='platform',
|
model_name='platform',
|
||||||
name='domain_default',
|
name='domain_default',
|
||||||
|
@ -28,8 +37,8 @@ class Migration(migrations.Migration):
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='platform',
|
model_name='platform',
|
||||||
name='protocols_enabled',
|
name='protocols',
|
||||||
field=models.BooleanField(default=True, verbose_name='Protocols enabled'),
|
field=models.ManyToManyField(blank=True, to='assets.PlatformProtocol', verbose_name='Protocols'),
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='platform',
|
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)
|
tree_node = TreeNode(**data)
|
||||||
return tree_node
|
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:
|
class Meta:
|
||||||
unique_together = [('org_id', 'name')]
|
unique_together = [('org_id', 'name')]
|
||||||
verbose_name = _("Asset")
|
verbose_name = _("Asset")
|
||||||
|
|
|
@ -5,7 +5,13 @@ from assets.const import Category, AllTypes
|
||||||
from common.db.fields import JsonDictTextField
|
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):
|
class Platform(models.Model):
|
||||||
|
@ -30,9 +36,7 @@ class Platform(models.Model):
|
||||||
verbose_name=_("Domain default")
|
verbose_name=_("Domain default")
|
||||||
)
|
)
|
||||||
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
|
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
|
||||||
protocols_default = models.JSONField(
|
protocols = models.ManyToManyField(PlatformProtocol, blank=True, verbose_name=_("Protocols"))
|
||||||
max_length=128, default=list, blank=True, verbose_name=_("Protocols default")
|
|
||||||
)
|
|
||||||
# Accounts
|
# Accounts
|
||||||
# 这应该和账号有关
|
# 这应该和账号有关
|
||||||
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
|
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 django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.drf.serializers import JMSWritableNestedModelSerializer
|
from common.drf.serializers import JMSWritableNestedModelSerializer
|
||||||
|
from common.drf.fields import ChoiceDisplayField
|
||||||
from ..account import AccountSerializer
|
from ..account import AccountSerializer
|
||||||
from ...models import Asset, Node, Platform, Protocol, Label, Domain, Account
|
from ...models import Asset, Node, Platform, Protocol, Label, Domain, Account
|
||||||
|
from ...const import Category, AllTypes
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
|
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
|
||||||
|
@ -57,6 +59,8 @@ class AssetNodesSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class AssetSerializer(JMSWritableNestedModelSerializer):
|
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)
|
domain = AssetDomainSerializer(required=False)
|
||||||
platform = AssetPlatformSerializer(required=False)
|
platform = AssetPlatformSerializer(required=False)
|
||||||
labels = AssetLabelSerializer(many=True, required=False)
|
labels = AssetLabelSerializer(many=True, required=False)
|
||||||
|
|
|
@ -1,42 +1,43 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.core.validators import RegexValidator
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from assets.models import Platform
|
from common.drf.fields import ChoiceDisplayField
|
||||||
from .mixin import CategoryDisplayMixin
|
from common.drf.serializers import JMSWritableNestedModelSerializer
|
||||||
|
from ..models import Platform, PlatformProtocol
|
||||||
|
from ..const import Category, AllTypes
|
||||||
|
|
||||||
__all__ = ['PlatformSerializer']
|
__all__ = ['PlatformSerializer']
|
||||||
|
|
||||||
|
|
||||||
class PlatformProtocolsSerializer(serializers.Serializer):
|
class PlatformProtocolsSerializer(serializers.ModelSerializer):
|
||||||
name = serializers.CharField(max_length=255, required=True)
|
class Meta:
|
||||||
port = serializers.IntegerField(max_value=65535, min_value=1, required=True)
|
model = PlatformProtocol
|
||||||
|
fields = ['id', 'name', 'port', 'setting']
|
||||||
|
|
||||||
|
|
||||||
class PlatformSerializer(CategoryDisplayMixin, serializers.ModelSerializer):
|
class PlatformSerializer(JMSWritableNestedModelSerializer):
|
||||||
meta = serializers.DictField(required=False, allow_null=True, label=_('Meta'))
|
type = ChoiceDisplayField(choices=AllTypes.choices, label=_("Type"))
|
||||||
protocols_default = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
|
category = ChoiceDisplayField(choices=Category.choices, label=_("Category"))
|
||||||
|
protocols = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
|
||||||
type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
|
type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Platform
|
model = Platform
|
||||||
fields_mini = ['id', 'name', 'internal']
|
fields_mini = ['id', 'name', 'internal']
|
||||||
fields_small = fields_mini + [
|
fields_small = fields_mini + [
|
||||||
'meta', 'comment', 'charset',
|
'category', 'type',
|
||||||
'category', 'category_display',
|
]
|
||||||
'type', 'type_display',
|
fields = fields_small + [
|
||||||
|
'domain_enabled', 'domain_default',
|
||||||
'su_enabled', 'su_method',
|
'su_enabled', 'su_method',
|
||||||
|
'protocols_enabled', 'protocols',
|
||||||
'ping_enabled', 'ping_method',
|
'ping_enabled', 'ping_method',
|
||||||
'verify_account_enabled', 'verify_account_method',
|
'verify_account_enabled', 'verify_account_method',
|
||||||
'create_account_enabled', 'create_account_method',
|
'create_account_enabled', 'create_account_method',
|
||||||
'change_password_enabled', 'change_password_method',
|
'change_password_enabled', 'change_password_method',
|
||||||
'type_constraints',
|
'type_constraints',
|
||||||
|
'comment', 'charset',
|
||||||
]
|
]
|
||||||
fields_fk = [
|
|
||||||
'domain_enabled', 'domain_default',
|
|
||||||
'protocols_enabled', 'protocols_default',
|
|
||||||
]
|
|
||||||
fields = fields_small + fields_fk
|
|
||||||
read_only_fields = [
|
read_only_fields = [
|
||||||
'category_display', 'type_display',
|
'category_display', 'type_display',
|
||||||
]
|
]
|
||||||
|
|
|
@ -51,6 +51,6 @@ class ChoiceDisplayField(ChoiceField):
|
||||||
if value in ('', None):
|
if value in ('', None):
|
||||||
return value
|
return value
|
||||||
return {
|
return {
|
||||||
'name': value,
|
'value': value,
|
||||||
'label': self.choice_mapper.get(six.text_type(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"]
|
system_user = data["run_as"]
|
||||||
user = self.request.user
|
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__users=user) |
|
||||||
Q(granted_by_permissions__user_groups__users=user)
|
Q(granted_by_permissions__user_groups__users=user)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue