perf: 修改平台

pull/8873/head
ibuler 2022-08-10 19:27:08 +08:00
parent 1ca0bdf843
commit 9d4a828c53
6 changed files with 176 additions and 86 deletions

View File

@ -21,7 +21,7 @@ class AssetPlatformViewSet(JMSModelViewSet):
search_fields = ['name']
rbac_perms = {
'categories': 'assets.view_platform',
'type_limits': 'assets-view_platform'
'type_constraints': 'assets-view_platform'
}
@action(methods=['GET'], detail=False)
@ -30,14 +30,13 @@ class AssetPlatformViewSet(JMSModelViewSet):
serializer = self.get_serializer(data, many=True)
return Response(serializer.data)
@action(methods=['GET'], detail=False, url_path='type-limits')
def type_limits(self, request, *args, **kwargs):
@action(methods=['GET'], detail=False, url_path='type-constraints')
def type_constraints(self, request, *args, **kwargs):
category = request.query_params.get('category')
tp = request.query_params.get('type')
limits = AllTypes.get_type_limits(category, tp)
limits = AllTypes.get_constraints(category, tp)
return Response(limits)
def check_object_permissions(self, request, obj):
if request.method.lower() in ['delete', 'put', 'patch'] and obj.internal:
self.permission_denied(

View File

@ -12,8 +12,16 @@ __all__ = [
class PlatformMixin:
@classmethod
def platform_limits(cls):
return {}
def platform_constraints(cls):
return {
'has_domain': False,
'has_su': False,
'has_ping': False,
'has_change_password': False,
'has_verify_account': False,
'has_create_account': False,
'_protocols': []
}
class Category(PlatformMixin, models.TextChoices):
@ -24,25 +32,30 @@ class Category(PlatformMixin, models.TextChoices):
WEB = 'web', _("Web")
@classmethod
def platform_limits(cls):
def platform_constraints(cls) -> dict:
return {
cls.HOST: {
'has_domain': True,
'protocols_limit': ['ssh', 'rdp', 'vnc', 'telnet']
'has_ping': True,
'has_verify_account': True,
'has_change_password': True,
'has_create_account': True,
'_protocols': ['ssh', 'telnet']
},
cls.NETWORK: {
'has_domain': True,
'protocols_limit': ['ssh', 'telnet']
'_protocols': ['ssh', 'telnet']
},
cls.DATABASE: {
'has_domain': True
'has_domain': True,
},
cls.WEB: {
'has_domain': False,
'_protocols': []
},
cls.CLOUD: {
'has_domain': False,
'protocol_limit': []
'_protocols': []
}
}
@ -57,18 +70,17 @@ class HostTypes(PlatformMixin, models.TextChoices):
OTHER_HOST = 'other_host', _("Other host")
@classmethod
def platform_limits(cls):
return {}
@classmethod
def get_default_port(cls):
defaults = {
cls.LINUX: 22,
cls.WINDOWS: 3389,
cls.UNIX: 22,
cls.BSD: 22,
cls.MACOS: 22,
cls.MAINFRAME: 22,
def platform_constraints(cls):
return {
cls.LINUX: {
'_protocols': ['ssh', 'rdp', 'vnc', 'telnet']
},
cls.WINDOWS: {
'_protocols': ['ssh', 'rdp', 'vnc']
},
cls.MACOS: {
'_protocols': ['ssh', 'vnc']
}
}
@ -89,11 +101,11 @@ class DatabaseTypes(PlatformMixin, models.TextChoices):
REDIS = 'redis', 'Redis'
@classmethod
def platform_limits(cls):
def platform_constraints(cls):
meta = {}
for name, label in cls.choices:
meta[name] = {
'protocols_limit': [name]
'protocols': [name]
}
return meta
@ -114,23 +126,25 @@ class AllTypes(metaclass=IncludesTextChoicesMeta):
]
@classmethod
def get_type_limits(cls, category, tp):
limits = Category.platform_limits().get(category, {})
def get_constraints(cls, category, tp):
constraints = PlatformMixin.platform_constraints()
category_constraints = Category.platform_constraints().get(category) or {}
constraints.update(category_constraints)
types_cls = dict(cls.category_types()).get(category)
if not types_cls:
return {}
types_limits = types_cls.platform_limits() or {}
type_limits = types_limits.get(tp, {})
limits.update(type_limits)
return constraints
type_constraints = types_cls.platform_constraints().get(tp) or {}
constraints.update(type_constraints)
_protocols_limit = limits.get('protocols_limit', [])
_protocols = constraints.pop('_protocols', [])
default_ports = Protocol.default_ports()
protocols_limit = []
for p in _protocols_limit:
protocols = []
for p in _protocols:
port = default_ports.get(p, 0)
protocols_limit.append(f'{p}/{port}')
limits['protocols_limit'] = protocols_limit
return limits
protocols.append({'name': p, 'port': port})
constraints['protocols'] = protocols
return constraints
@classmethod
def category_types(cls):

View File

@ -11,16 +11,6 @@ class Migration(migrations.Migration):
]
operations = [
migrations.AddField(
model_name='platform',
name='admin_user_default',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='assets.systemuser', verbose_name='Admin user default'),
),
migrations.AddField(
model_name='platform',
name='admin_user_enabled',
field=models.BooleanField(default=True, verbose_name='Admin user enabled'),
),
migrations.AddField(
model_name='platform',
name='domain_default',
@ -34,13 +24,63 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='platform',
name='protocols_default',
field=models.CharField(blank=True, default='', max_length=128, verbose_name='Protocols default'),
field=models.JSONField(blank=True, default=list, max_length=128, verbose_name='Protocols default'),
),
migrations.AddField(
model_name='platform',
name='protocols_enabled',
field=models.BooleanField(default=True, verbose_name='Protocols enabled'),
),
migrations.AddField(
model_name='platform',
name='change_password_enabled',
field=models.BooleanField(default=False, verbose_name='Change password enabled'),
),
migrations.AddField(
model_name='platform',
name='change_password_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Change password method'),
),
migrations.AddField(
model_name='platform',
name='create_account_enabled',
field=models.BooleanField(default=False, verbose_name='Create account enabled'),
),
migrations.AddField(
model_name='platform',
name='create_account_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Create account method'),
),
migrations.AddField(
model_name='platform',
name='ping_enabled',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='platform',
name='ping_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Ping method'),
),
migrations.AddField(
model_name='platform',
name='su_enabled',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='platform',
name='su_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='SU method'),
),
migrations.AddField(
model_name='platform',
name='verify_account_enabled',
field=models.BooleanField(default=False, verbose_name='Verify account enabled'),
),
migrations.AddField(
model_name='platform',
name='verify_account_method',
field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Verify account method'),
),
migrations.AlterField(
model_name='asset',
name='category',

View File

@ -0,0 +1,41 @@
# Generated by Django 3.2.14 on 2022-08-10 06:49
import assets.models.platform
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0104_auto_20220803_1859'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='category',
field=models.CharField(choices=[('host', 'Host'), ('network', 'NetworkDevice'), ('database', 'Database'), ('cloud', 'Clouding'), ('web', 'Web')], max_length=16, verbose_name='Category'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.ForeignKey(default=assets.models.platform.Platform.default, on_delete=django.db.models.deletion.PROTECT, related_name='assets', to='assets.platform', verbose_name='Platform'),
),
migrations.AlterField(
model_name='asset',
name='type',
field=models.CharField(choices=[('linux', 'Linux'), ('windows', 'Windows'), ('unix', 'Unix'), ('bsd', 'BSD'), ('macos', 'MacOS'), ('mainframe', 'Mainframe'), ('other_host', 'Other host'), ('switch', 'Switch'), ('router', 'Router'), ('firewall', 'Firewall'), ('other_network', 'Other device'), ('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('oracle', 'Oracle'), ('sqlserver', 'SQLServer'), ('mongodb', 'MongoDB'), ('redis', 'Redis'), ('general', 'General'), ('k8s', 'Kubernetes')], max_length=128, verbose_name='Type'),
),
migrations.AlterField(
model_name='platform',
name='category',
field=models.CharField(choices=[('host', 'Host'), ('network', 'NetworkDevice'), ('database', 'Database'), ('cloud', 'Clouding'), ('web', 'Web')], default='host', max_length=16, verbose_name='Category'),
),
migrations.AlterField(
model_name='platform',
name='type',
field=models.CharField(choices=[('linux', 'Linux'), ('windows', 'Windows'), ('unix', 'Unix'), ('bsd', 'BSD'), ('macos', 'MacOS'), ('mainframe', 'Mainframe'), ('other_host', 'Other host'), ('switch', 'Switch'), ('router', 'Router'), ('firewall', 'Firewall'), ('other_network', 'Other device'), ('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('oracle', 'Oracle'), ('sqlserver', 'SQLServer'), ('mongodb', 'MongoDB'), ('redis', 'Redis'), ('general', 'General'), ('k8s', 'Kubernetes')], default='Linux', max_length=32, verbose_name='Type'),
),
]

View File

@ -9,6 +9,10 @@ __all__ = ['Platform']
class Platform(models.Model):
"""
对资产提供 约束和默认值
对资产进行抽象
"""
CHARSET_CHOICES = (
('utf8', 'UTF-8'),
('gbk', 'GBK'),
@ -26,27 +30,25 @@ class Platform(models.Model):
verbose_name=_("Domain default")
)
protocols_enabled = models.BooleanField(default=True, verbose_name=_("Protocols enabled"))
protocols_default = models.CharField(
max_length=128, default='', blank=True, verbose_name=_("Protocols default")
protocols_default = models.JSONField(
max_length=128, default=list, blank=True, verbose_name=_("Protocols default")
)
admin_user_enabled = models.BooleanField(default=True, verbose_name=_("Admin user enabled"))
admin_user_default = models.ForeignKey(
'assets.SystemUser', null=True, on_delete=models.SET_NULL,
verbose_name=_("Admin user default")
)
@classmethod
def get_type_meta(cls, category, tp):
meta = Category.platform_meta().get(category, {})
types = dict(AllTypes.category_types()).get(category)
types_meta = types.platform_meta() or {}
type_meta = types_meta.get(tp, {})
meta.update(type_meta)
return meta
# Accounts
# 这应该和账号有关
su_enabled = models.BooleanField(default=False)
su_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("SU method"))
ping_enabled = models.BooleanField(default=False)
ping_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Ping method"))
verify_account_enabled = models.BooleanField(default=False, verbose_name=_("Verify account enabled"))
verify_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Verify account method"))
create_account_enabled = models.BooleanField(default=False, verbose_name=_("Create account enabled"))
create_account_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Create account method"))
change_password_enabled = models.BooleanField(default=False, verbose_name=_("Change password enabled"))
change_password_method = models.TextField(max_length=32, blank=True, null=True, verbose_name=_("Change password method"))
@property
def type_limits(self):
return AllTypes.get_type_limits(self.category, self.type)
def type_constraints(self):
return AllTypes.get_constraints(self.category, self.type)
@classmethod
def default(cls):
@ -55,12 +57,6 @@ class Platform(models.Model):
)
return linux.id
def is_windows(self):
return self.type.lower() in ('windows',)
def is_unixlike(self):
return self.type.lower() in ("linux", "unix", "macos", "bsd")
def __str__(self):
return self.name

View File

@ -8,19 +8,15 @@ from .mixin import CategoryDisplayMixin
__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 PlatformSerializer(CategoryDisplayMixin, serializers.ModelSerializer):
meta = serializers.DictField(required=False, allow_null=True, label=_('Meta'))
protocols_default = serializers.ListField(label=_('Protocols'), required=False)
type_limits = serializers.ReadOnlyField(required=False, read_only=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# TODO 修复 drf SlugField RegexValidator bug之后记得删除
validators = self.fields['name'].validators
if isinstance(validators[-1], RegexValidator):
validators.pop()
# self.set_platform_meta()
protocols_default = PlatformProtocolsSerializer(label=_('Protocols'), many=True, required=False)
type_constraints = serializers.ReadOnlyField(required=False, read_only=True)
class Meta:
model = Platform
@ -29,12 +25,16 @@ class PlatformSerializer(CategoryDisplayMixin, serializers.ModelSerializer):
'meta', 'comment', 'charset',
'category', 'category_display',
'type', 'type_display',
'type_limits',
'su_enabled', 'su_method',
'ping_enabled', 'ping_method',
'verify_account_enabled', 'verify_account_method',
'create_account_enabled', 'create_account_method',
'change_password_enabled', 'change_password_method',
'type_constraints',
]
fields_fk = [
'domain_enabled', 'domain_default',
'protocols_enabled', 'protocols_default',
'admin_user_enabled', 'admin_user_default',
]
fields = fields_small + fields_fk
read_only_fields = [