perf: 修改 base

pull/8605/head
ibuler 2022-04-07 18:51:35 +08:00
parent 1b9efff6c7
commit d418c28e98
24 changed files with 256 additions and 218 deletions

View File

@ -1,9 +1,11 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from assets.models import SystemUser
from acls import models
from orgs.models import Organization
from assets.models import SystemUser
from assets.const import Protocol
from acls import models
__all__ = ['LoginAssetACLSerializer']
@ -54,7 +56,7 @@ class LoginAssetACLSystemUsersSerializer(serializers.Serializer):
protocol_group = serializers.ListField(
default=['*'], child=serializers.CharField(max_length=16), label=_('Protocol'),
help_text=protocol_group_help_text.format(
', '.join([SystemUser.Protocol.ssh, SystemUser.Protocol.telnet])
', '.join([Protocol.ssh, Protocol.telnet])
)
)

View File

@ -10,6 +10,7 @@ from common.mixins.api import SuggestionMixin
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics
from orgs.utils import tmp_to_root_org
from assets.const import Protocol
from ..models import SystemUser, CommandFilterRule
from .. import serializers
from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer
@ -56,7 +57,7 @@ class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet):
""" API 获取可选的 su_from 系统用户"""
queryset = self.filter_queryset(self.get_queryset())
queryset = queryset.filter(
protocol=SystemUser.Protocol.ssh, login_mode=SystemUser.LOGIN_AUTO
protocol=Protocol.ssh, login_mode=SystemUser.LOGIN_AUTO
)
return self.get_paginate_response_if_need(queryset)

View File

@ -1,2 +1,92 @@
# -*- coding: utf-8 -*-
#
from django.db import models
from django.utils.translation import gettext_lazy as _
from common.db.models import IncludesTextChoicesMeta
__all__ = [
'Category', 'HostTypes', 'NetworkTypes', 'DatabaseTypes',
'RemoteAppTypes', 'CloudTypes', 'Protocol', 'AllTypes',
]
class Category(models.TextChoices):
HOST = 'host', _('Host')
NETWORK = 'network', _("Networking")
DATABASE = 'database', _("Database")
REMOTE_APP = 'remote_app', _("Remote app")
CLOUD = 'cloud', _("Clouding")
class HostTypes(models.TextChoices):
LINUX = 'linux', 'Linux'
WINDOWS = 'windows', 'Windows'
UNIX = 'unix', 'Unix'
BSD = 'bsd', 'BSD'
MACOS = 'macos', 'MacOS'
MAINFRAME = 'mainframe', _("Mainframe")
OTHER_HOST = 'other_host', _("Other host")
class NetworkTypes(models.TextChoices):
SWITCH = 'switch', _("Switch")
ROUTER = 'router', _("Router")
FIREWALL = 'firewall', _("Firewall")
OTHER_NETWORK = 'other_network', _("Other device")
class DatabaseTypes(models.TextChoices):
MYSQL = 'mysql', 'MySQL'
MARIADB = 'mariadb', 'MariaDB'
POSTGRESQL = 'postgresql', 'PostgreSQL'
ORACLE = 'oracle', 'Oracle'
SQLSERVER = 'sqlserver', 'SQLServer'
MONGODB = 'mongodb', 'MongoDB'
REDIS = 'redis', 'Redis'
class RemoteAppTypes(models.TextChoices):
CHROME = 'chrome', 'Chrome'
VSPHERE = 'vsphere', 'vSphere client'
MYSQL_WORKBENCH = 'mysql_workbench', 'MySQL workbench'
CUSTOM_REMOTE_APP = 'custom_remote_app', _("Custom")
class CloudTypes(models.TextChoices):
K8S = 'k8s', 'Kubernetes'
class AllTypes(metaclass=IncludesTextChoicesMeta):
choices: list
includes = [
HostTypes, NetworkTypes, DatabaseTypes,
RemoteAppTypes, CloudTypes
]
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
vnc = 'vnc', 'VNC'
mysql = 'mysql', 'MySQL'
mariadb = 'mariadb', 'MariaDB'
oracle = 'oracle', 'Oracle'
postgresql = 'postgresql', 'PostgreSQL'
sqlserver = 'sqlserver', 'SQLServer'
redis = 'redis', 'Redis'
mongodb = 'mongodb', 'MongoDB'
k8s = 'k8s', 'K8S'
@classmethod
def host_protocols(cls):
return [cls.ssh, cls.rdp, cls.telnet, cls.vnc]
@classmethod
def db_protocols(cls):
return [
cls.mysql, cls.mariadb, cls.postgresql, cls.oracle,
cls.sqlserver, cls.redis, cls.mongodb,
]

View File

@ -0,0 +1,34 @@
# Generated by Django 3.1.14 on 2022-04-07 09:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0096_auto_20220406_1546'),
]
operations = [
migrations.RenameField(
model_name='platform',
old_name='base',
new_name='type',
),
migrations.AddField(
model_name='platform',
name='category',
field=models.CharField(choices=[('host', 'Host'), ('network', 'Networking'), ('database', 'Database'), ('remote_app', 'Remote app'), ('cloud', 'Clouding')], default='host', max_length=16, verbose_name='Category'),
preserve_default=False,
),
migrations.AlterField(
model_name='asset',
name='type',
field=models.CharField(choices=[('linux', 'Linux'), ('unix', 'Unix'), ('windows', 'Windows'), ('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'), ('chrome', 'Chrome'), ('vsphere', 'vSphere client'), ('mysql_workbench', 'MySQL workbench'), ('custom_remote_app', 'Custom'), ('k8s', 'Kubernetes')], max_length=128, verbose_name='Type'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.1.14 on 2022-04-07 09:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0097_auto_20220407_1726'),
]
operations = [
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'), ('chrome', 'Chrome'), ('vsphere', 'vSphere client'), ('mysql_workbench', 'MySQL workbench'), ('custom_remote_app', 'Custom'), ('k8s', 'Kubernetes')], max_length=128, verbose_name='Type'),
),
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'), ('chrome', 'Chrome'), ('vsphere', 'vSphere client'), ('mysql_workbench', 'MySQL workbench'), ('custom_remote_app', 'Custom'), ('k8s', 'Kubernetes')], default='Linux', max_length=32, verbose_name='Base'),
),
]

View File

@ -1,73 +0,0 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class Category(models.TextChoices):
HOST = 'host', _('Host')
NETWORK = 'network', _("Networking")
DATABASE = 'database', _("Database")
REMOTE_APP = 'remote_app', _("Remote app")
CLOUD = 'cloud', _("Clouding")
class HostTypes(models.TextChoices):
LINUX = 'linux', 'Linux'
UNIX = 'unix', 'Unix'
WINDOWS = 'windows', 'Windows'
MACOS = 'macos', 'MacOS'
MAINFRAME = 'mainframe', _("Mainframe")
OTHER_HOST = 'other_host', _("Other host")
def __new__(cls, value):
"""
添加 Category
:param value:
"""
obj = str.__new__(cls)
obj.category = Category.HOST
return obj
class NetworkTypes(models.TextChoices):
SWITCH = 'switch', _("Switch")
ROUTER = 'router', _("Router")
FIREWALL = 'firewall', _("Firewall")
OTHER_NETWORK = 'other_network', _("Other device")
class DatabaseTypes(models.TextChoices):
MYSQL = 'mysql', 'MySQL'
MARIADB = 'mariadb', 'MariaDB'
POSTGRESQL = 'postgresql', 'PostgreSQL'
ORACLE = 'oracle', 'Oracle'
SQLSERVER = 'sqlserver', 'SQLServer'
MONGODB = 'mongodb', 'MongoDB'
REDIS = 'redis', 'Redis'
class RemoteAppTypes(models.TextChoices):
CHROME = 'chrome', 'Chrome'
VSPHERE = 'vsphere', 'vSphere client'
MYSQL_WORKBENCH = 'mysql_workbench', 'MySQL workbench'
CUSTOM_REMOTE_APP = 'custom_remote_app', _("Custom")
class CloudTypes(models.TextChoices):
K8S = 'k8s', 'Kubernetes'
class AllTypes:
includes = [
HostTypes, NetworkTypes, DatabaseTypes,
RemoteAppTypes, CloudTypes
]
@classmethod
def choices(cls):
choices = []
for tp in cls.includes:
choices.extend(tp.choices)
return choices

View File

@ -13,9 +13,9 @@ from rest_framework.exceptions import ValidationError
from common.utils import lazyproperty
from orgs.mixins.models import OrgModelMixin, OrgManager
from assets.const import Category, AllTypes
from ..platform import Platform
from ..base import AbsConnectivity
from ._category import Category, AllTypes
__all__ = ['Asset', 'ProtocolsMixin', 'AssetQuerySet', 'default_node', 'default_cluster']
logger = logging.getLogger(__name__)
@ -123,11 +123,12 @@ class NodesRelationMixin:
class Asset(AbsConnectivity, ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
Category = Category
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
hostname = models.CharField(max_length=128, verbose_name=_('Hostname'))
ip = models.CharField(max_length=128, verbose_name=_('IP'), db_index=True)
category = models.CharField(max_length=16, choices=Category.choices, verbose_name=_("Category"))
type = models.CharField(max_length=128, choices=AllTypes.choices(), verbose_name=_("Type"))
type = models.CharField(max_length=128, choices=AllTypes.choices, verbose_name=_("Type"))
protocol = models.CharField(max_length=128, default=ProtocolsMixin.Protocol.ssh,
choices=ProtocolsMixin.Protocol.choices, verbose_name=_('Protocol'))
port = models.IntegerField(default=22, verbose_name=_('Port'))
@ -183,14 +184,6 @@ class Asset(AbsConnectivity, ProtocolsMixin, NodesRelationMixin, OrgModelMixin):
return False, warning
return True, warning
@property
def category_display(self):
return self.get_category_display()
@property
def type_display(self):
pass
@lazyproperty
def platform_base(self):
return self.platform.base

View File

@ -6,4 +6,3 @@ from .common import Asset
class Database(Asset):
database = models.CharField(max_length=1024, verbose_name=_("Database"), blank=True)

View File

@ -1,12 +1,15 @@
from django.utils.translation import gettext_lazy as _
from django.db import models
from assets.const import Category
from common.mixins.models import CommonModelMixin
from .common import Asset
class Host(Asset):
pass
def save(self, *args, **kwargs):
self.category = Category.HOST
return super().save(*args, **kwargs)
class DeviceInfo(CommonModelMixin):

View File

@ -0,0 +1,5 @@
from .common import Asset
class Network(Asset):
pass

View File

@ -1,19 +1,12 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from assets.const import Category, AllTypes
from common.fields.model import JsonDictTextField
__all__ = ['Platform']
class Category(models.TextChoices):
Host = 'host', _('Host')
Network = 'network', _('Network device')
Database = 'database', _('Database')
RemoteApp = 'remote_app', _('Microsoft remote app')
Cloud = 'cloud', _("Cloud")
class Platform(models.Model):
CHARSET_CHOICES = (
('utf8', 'UTF-8'),
@ -28,7 +21,8 @@ class Platform(models.Model):
('Other', 'Other'),
)
name = models.SlugField(verbose_name=_("Name"), unique=True, allow_unicode=True)
base = models.CharField(choices=BASE_CHOICES, max_length=16, default='Linux', verbose_name=_("Base"))
category = models.CharField(max_length=16, choices=Category.choices, verbose_name=_("Category"))
type = models.CharField(choices=AllTypes.choices, max_length=32, default='Linux', verbose_name=_("Base"))
charset = models.CharField(default='utf8', choices=CHARSET_CHOICES, max_length=8, verbose_name=_("Charset"))
meta = JsonDictTextField(blank=True, null=True, verbose_name=_("Meta"))
internal = models.BooleanField(default=False, verbose_name=_("Internal"))

View File

@ -10,6 +10,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.cache import cache
from common.utils import signer, get_object_or_none
from assets.const import Protocol
from .base import BaseUser
from .asset import Asset
from .authbook import AuthBook
@ -21,20 +22,7 @@ logger = logging.getLogger(__name__)
class ProtocolMixin:
protocol: str
class Protocol(models.TextChoices):
ssh = 'ssh', 'SSH'
rdp = 'rdp', 'RDP'
telnet = 'telnet', 'Telnet'
vnc = 'vnc', 'VNC'
mysql = 'mysql', 'MySQL'
oracle = 'oracle', 'Oracle'
mariadb = 'mariadb', 'MariaDB'
postgresql = 'postgresql', 'PostgreSQL'
sqlserver = 'sqlserver', 'SQLServer'
redis = 'redis', 'Redis'
mongodb = 'mongodb', 'MongoDB'
k8s = 'k8s', 'K8S'
Protocol = Protocol
SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp]
@ -245,7 +233,7 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups"))
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_('Type'))
priority = models.IntegerField(default=81, verbose_name=_("Priority"), help_text=_("1-100, the lower the value will be match first"), validators=[MinValueValidator(1), MaxValueValidator(100)])
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol'))
protocol = models.CharField(max_length=16, choices=Protocol.choices, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))

View File

@ -70,6 +70,8 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
labels_display = serializers.ListField(
child=serializers.CharField(), label=_('Labels name'), required=False, read_only=True
)
category_display = serializers.ReadOnlyField(source='get_category_display', label=_("Category display"))
type_display = serializers.ReadOnlyField(source='get_type_display', label=_("Type display"))
"""
资产的数据结构
@ -78,7 +80,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
class Meta:
model = Asset
fields_mini = [
'id', 'category', 'category_display', 'type',
'id', 'category', 'category_display', 'type', 'type_display',
'hostname', 'ip', 'platform', 'protocols'
]
fields_small = fields_mini + [

View File

@ -6,6 +6,7 @@ from common.mixins.serializers import BulkSerializerMixin
from common.utils import ssh_pubkey_gen
from common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from assets.const import Protocol
from ..models import SystemUser, Asset
from .utils import validate_password_contains_left_double_curly_bracket
from .base import AuthSerializerMixin
@ -107,9 +108,9 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
def validate_username(self, username):
protocol = self.get_initial_value("protocol")
if username:
if protocol == SystemUser.Protocol.telnet:
if protocol == Protocol.telnet:
regx = alphanumeric_cn_re
elif protocol == SystemUser.Protocol.rdp:
elif protocol == Protocol.rdp:
regx = alphanumeric_win_re
else:
regx = alphanumeric_re
@ -122,8 +123,8 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
return ''
login_mode = self.get_initial_value("login_mode")
if login_mode == SystemUser.LOGIN_AUTO and protocol != SystemUser.Protocol.vnc \
and protocol != SystemUser.Protocol.redis:
if login_mode == SystemUser.LOGIN_AUTO and protocol != Protocol.vnc \
and protocol != Protocol.redis:
msg = _('* Automatic login mode must fill in the username.')
raise serializers.ValidationError(msg)
return username
@ -163,8 +164,8 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
error = _('This field is required.')
raise serializers.ValidationError(error)
# self: protocol ssh
protocol = self.get_initial_value('protocol', default=SystemUser.Protocol.ssh.value)
if protocol not in [SystemUser.Protocol.ssh.value]:
protocol = self.get_initial_value('protocol', default=Protocol.ssh.value)
if protocol not in [Protocol.ssh.value]:
error = _('Only ssh protocol system users are allowed')
raise serializers.ValidationError(error)
# su_from: protocol same
@ -184,7 +185,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
tp = attrs.get('type')
if tp != SystemUser.Type.admin:
return attrs
attrs['protocol'] = SystemUser.Protocol.ssh
attrs['protocol'] = Protocol.ssh
attrs['login_mode'] = SystemUser.LOGIN_AUTO
attrs['username_same_with_user'] = False
attrs['auto_push'] = False
@ -202,7 +203,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
if auto_gen_key and not self.instance:
password = SystemUser.gen_password()
attrs['password'] = password
if protocol == SystemUser.Protocol.ssh:
if protocol == Protocol.ssh:
private_key, public_key = SystemUser.gen_key(username)
attrs['private_key'] = private_key
attrs['public_key'] = public_key

View File

@ -3,8 +3,9 @@ import uuid
from django.utils.translation import ugettext_lazy as _
from rest_framework.authtoken.models import Token
from django.conf import settings
from django.db import models
from common.db import models
from common.db.models import BaseCreateUpdateModel
class AccessKey(models.Model):
@ -40,7 +41,7 @@ class PrivateToken(Token):
verbose_name = _('Private Token')
class SSOToken(models.JMSBaseModel):
class SSOToken(BaseCreateUpdateModel):
"""
类似腾讯企业邮的 [单点登录](https://exmail.qq.com/qy_mng_logic/doc#10036)
出于安全考虑这里的 `token` 使用一次随即过期但我们保留每一个生成过的 `token`
@ -53,7 +54,7 @@ class SSOToken(models.JMSBaseModel):
verbose_name = _('SSO token')
class ConnectionToken(models.JMSBaseModel):
class ConnectionToken(BaseCreateUpdateModel):
# Todo: 未来可能放到这里,不记录到 redis 了,虽然方便,但是不易于审计
# Todo: add connection token 可能要授权给 普通用户, 或者放开就行

View File

@ -13,57 +13,34 @@ import uuid
from functools import reduce, partial
import inspect
from django.db.models import *
from django.db import models
from django.db.models import F, Value, ExpressionWrapper
from enum import _EnumDict
from django.db.models import QuerySet
from django.db.models.functions import Concat
from django.utils.translation import ugettext_lazy as _
class Choice(str):
def __new__(cls, value, label=''): # `deepcopy` 的时候不会传 `label`
self = super().__new__(cls, value)
self.label = label
return self
class IncludesTextChoicesMeta(type):
def __new__(metacls, classname, bases, classdict):
includes = classdict.pop('includes', None)
assert includes
attrs = _EnumDict()
for k, v in classdict.items():
attrs[k] = v
class ChoiceSetType(type):
def __new__(cls, name, bases, attrs):
_choices = []
collected = set()
new_attrs = {}
for k, v in attrs.items():
if isinstance(v, tuple):
v = Choice(*v)
assert v not in collected, 'Cannot be defined repeatedly'
_choices.append(v)
collected.add(v)
new_attrs[k] = v
for base in bases:
if hasattr(base, '_choices'):
for c in base._choices:
if c not in collected:
_choices.append(c)
collected.add(c)
new_attrs['_choices'] = _choices
new_attrs['_choices_dict'] = {c: c.label for c in _choices}
return type.__new__(cls, name, bases, new_attrs)
for cls in includes:
_member_names_ = cls._member_names_
_member_map_ = cls._member_map_
_value2label_map_ = cls._value2label_map_
def __contains__(self, item):
return self._choices_dict.__contains__(item)
def __getitem__(self, item):
return self._choices_dict.__getitem__(item)
def get(self, item, default=None):
return self._choices_dict.get(item, default)
@property
def choices(self):
return [(c, c.label) for c in self._choices]
class ChoiceSet(metaclass=ChoiceSetType):
choices = None # 用于 Django Model 中的 choices 配置, 为了代码提示在此声明
for name in _member_names_:
value = str(_member_map_[name])
label = _value2label_map_[value]
attrs[name] = value, label
bases = (models.TextChoices,)
return type(classname, bases, attrs)
class BitOperationChoice:
@ -107,18 +84,18 @@ class BitOperationChoice:
return [(cls.NAME_MAP[i], j) for i, j in cls.DB_CHOICES]
class JMSBaseModel(Model):
created_by = CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
updated_by = CharField(max_length=32, null=True, blank=True, verbose_name=_('Updated by'))
date_created = DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
date_updated = DateTimeField(auto_now=True, verbose_name=_('Date updated'))
class BaseCreateUpdateModel(models.Model):
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
updated_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Updated by'))
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
date_updated = models.DateTimeField(auto_now=True, verbose_name=_('Date updated'))
class Meta:
abstract = True
class JMSModel(JMSBaseModel):
id = UUIDField(default=uuid.uuid4, primary_key=True)
class JMSBaseModel(BaseCreateUpdateModel):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
class Meta:
abstract = True
@ -129,7 +106,7 @@ def concated_display(name1, name2):
def output_as_string(field_name):
return ExpressionWrapper(F(field_name), output_field=CharField())
return ExpressionWrapper(F(field_name), output_field=models.CharField())
class UnionQuerySet(QuerySet):

View File

@ -1,11 +1,11 @@
from django.db import models
from common.db.models import JMSModel
from common.db.models import JMSBaseModel
__all__ = ('SystemMsgSubscription', 'UserMsgSubscription')
class UserMsgSubscription(JMSModel):
class UserMsgSubscription(JMSBaseModel):
user = models.OneToOneField('users.User', related_name='user_msg_subscription', on_delete=models.CASCADE)
receive_backends = models.JSONField(default=list)
@ -13,7 +13,7 @@ class UserMsgSubscription(JMSModel):
return f'{self.user} subscription: {self.receive_backends}'
class SystemMsgSubscription(JMSModel):
class SystemMsgSubscription(JMSBaseModel):
message_type = models.CharField(max_length=128, unique=True)
users = models.ManyToManyField('users.User', related_name='system_msg_subscriptions')
groups = models.ManyToManyField('users.UserGroup', related_name='system_msg_subscriptions')

View File

@ -1,18 +1,18 @@
from django.db import models
from common.db.models import JMSModel
from common.db.models import JMSBaseModel
__all__ = ('SiteMessageUsers', 'SiteMessage')
class SiteMessageUsers(JMSModel):
class SiteMessageUsers(JMSBaseModel):
sitemessage = models.ForeignKey('notifications.SiteMessage', on_delete=models.CASCADE, db_constraint=False, related_name='m2m_sitemessageusers')
user = models.ForeignKey('users.User', on_delete=models.CASCADE, db_constraint=False, related_name='m2m_sitemessageusers')
has_read = models.BooleanField(default=False)
read_at = models.DateTimeField(default=None, null=True)
class SiteMessage(JMSModel):
class SiteMessage(JMSBaseModel):
subject = models.CharField(max_length=1024)
message = models.TextField()
users = models.ManyToManyField(

View File

@ -2,10 +2,11 @@ import logging
from django.utils.translation import ugettext_lazy as _
from django.db.models import F, TextChoices
from django.db import models
from orgs.mixins.models import OrgModelMixin
from common.db import models
from common.utils import lazyproperty
from common.db.models import BaseCreateUpdateModel
from assets.models import Asset, SystemUser, Node, FamilyMixin
from .base import BasePermission
@ -89,7 +90,7 @@ class AssetPermission(BasePermission):
return names
class UserAssetGrantedTreeNodeRelation(OrgModelMixin, FamilyMixin, models.JMSBaseModel):
class UserAssetGrantedTreeNodeRelation(OrgModelMixin, FamilyMixin, BaseCreateUpdateModel):
class NodeFrom(TextChoices):
granted = 'granted', 'Direct node granted'
child = 'child', 'Have children node'

View File

@ -1,7 +1,7 @@
from django.utils.translation import ugettext_lazy as _, gettext
from django.db import models
from common.db.models import JMSModel
from common.db.models import JMSBaseModel
from common.utils import lazyproperty
from .permission import Permission
from ..builtin import BuiltinRole
@ -22,7 +22,7 @@ class OrgRoleManager(models.Manager):
return queryset.filter(scope=const.Scope.org)
class Role(JMSModel):
class Role(JMSBaseModel):
""" 定义 角色 角色-权限 关系 """
Scope = const.Scope

View File

@ -4,7 +4,7 @@ from django.db.models import Q
from django.core.exceptions import ValidationError
from rest_framework.serializers import ValidationError
from common.db.models import JMSModel
from common.db.models import JMSBaseModel
from common.utils import lazyproperty
from orgs.utils import current_org
from .role import Role
@ -29,7 +29,7 @@ class RoleBindingManager(models.Manager):
return self.get_queryset()
class RoleBinding(JMSModel):
class RoleBinding(JMSBaseModel):
Scope = Scope
""" 定义 用户-角色 关系 """
scope = models.CharField(

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.14 on 2022-04-07 09:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('terminal', '0047_auto_20220302_1951'),
]
operations = [
migrations.AlterField(
model_name='session',
name='protocol',
field=models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('mariadb', 'MariaDB'), ('oracle', 'Oracle'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], db_index=True, default='ssh', max_length=16),
),
]

View File

@ -11,6 +11,7 @@ from django.core.files.storage import default_storage
from django.core.cache import cache
from assets.models import Asset
from assets.const import Protocol
from users.models import User
from orgs.mixins.models import OrgModelMixin
from django.db.models import TextChoices
@ -24,20 +25,6 @@ class Session(OrgModelMixin):
WT = 'WT', 'Web Terminal'
DT = 'DT', 'DB Terminal'
class PROTOCOL(TextChoices):
SSH = 'ssh', 'ssh'
RDP = 'rdp', 'rdp'
VNC = 'vnc', 'vnc'
TELNET = 'telnet', 'telnet'
MYSQL = 'mysql', 'mysql'
ORACLE = 'oracle', 'oracle'
MARIADB = 'mariadb', 'mariadb'
SQLSERVER = 'sqlserver', 'sqlserver'
POSTGRESQL = 'postgresql', 'postgresql'
REDIS = 'redis', 'redis'
MONGODB = 'mongodb', 'MongoDB'
K8S = 'k8s', 'kubernetes'
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
user = models.CharField(max_length=128, verbose_name=_("User"), db_index=True)
user_id = models.CharField(blank=True, default='', max_length=36, db_index=True)
@ -52,7 +39,7 @@ class Session(OrgModelMixin):
has_replay = models.BooleanField(default=False, verbose_name=_("Replay"))
has_command = models.BooleanField(default=False, verbose_name=_("Command"))
terminal = models.ForeignKey('terminal.Terminal', null=True, on_delete=models.DO_NOTHING, db_constraint=False)
protocol = models.CharField(choices=PROTOCOL.choices, default='ssh', max_length=16, db_index=True)
protocol = models.CharField(choices=Protocol.choices, default='ssh', max_length=16, db_index=True)
date_start = models.DateTimeField(verbose_name=_("Date start"), db_index=True, default=timezone.now)
date_end = models.DateTimeField(verbose_name=_("Date end"), null=True)
@ -131,29 +118,20 @@ class Session(OrgModelMixin):
@property
def can_join(self):
_PROTOCOL = self.PROTOCOL
if self.is_finished:
return False
if self.login_from == self.LOGIN_FROM.RT:
return False
if self.protocol in [
_PROTOCOL.SSH, _PROTOCOL.VNC, _PROTOCOL.RDP,
_PROTOCOL.TELNET, _PROTOCOL.K8S
if Protocol in [
Protocol.SSH, Protocol.VNC, Protocol.RDP,
Protocol.TELNET, Protocol.K8S
]:
return True
else:
return False
@property
def db_protocols(self):
_PROTOCOL = self.PROTOCOL
return [_PROTOCOL.MYSQL, _PROTOCOL.MARIADB, _PROTOCOL.ORACLE,
_PROTOCOL.POSTGRESQL, _PROTOCOL.SQLSERVER,
_PROTOCOL.REDIS, _PROTOCOL.MONGODB]
@property
def can_terminate(self):
_PROTOCOL = self.PROTOCOL
if self.is_finished:
return False
else:

View File

@ -5,6 +5,7 @@ import forgery_py
from .base import FakeDataGenerator
from assets.models import *
from assets.const import Protocol
class AdminUsersGenerator(FakeDataGenerator):
@ -28,7 +29,7 @@ class AdminUsersGenerator(FakeDataGenerator):
class SystemUsersGenerator(FakeDataGenerator):
def do_generate(self, batch, batch_size):
system_users = []
protocols = list(dict(SystemUser.Protocol.choices).keys())
protocols = list(dict(Protocol.choices).keys())
for i in batch:
username = forgery_py.internet.user_name(True)
protocol = random.choice(protocols)