feat: 添加Endpoint (#8041)

* feat: add Endpoint EndpointRule EndpointProtocol model

* feat: add Endpoint EndpointRule EndpointProtocol API

* feat: modify protocols field

* feat: 修改序列类

* feat: 获取connect-url连接地址

* feat: 获取connect-url连接地址

* feat: 优化后台获取smart-endpoint逻辑

* feat: 优化后台获取smart-endpoint逻辑

* feat: 删除配置KOKO、XRDP、MAGNUS

* feat: 删除配置KOKO、XRDP、MAGNUS

* feat: 修改翻译

* feat: 修改smart endpoint

* feat: 修改翻译

* feat: smart API 添加token解析

* feat: 删除 smart serializer

* feat: 修改迁移逻辑

* feat: 解决冲突

* feat: 修改匹配 endpoint

Co-authored-by: Jiangjie.Bai <bugatti_it@163.com>
pull/8048/head
fit2bot 2022-04-12 17:45:10 +08:00 committed by GitHub
parent 4cf90df17c
commit f481463c64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 695 additions and 1027 deletions

View File

@ -247,6 +247,14 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
def category_remote_app(self):
return self.category == const.AppCategory.remote_app.value
@property
def category_cloud(self):
return self.category == const.AppCategory.cloud.value
@property
def category_db(self):
return self.category == const.AppCategory.db.value
def get_rdp_remote_app_setting(self):
from applications.serializers.attrs import get_serializer_class_by_application_type
if not self.category_remote_app:
@ -279,6 +287,18 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
if raise_exception:
raise ValueError("Remote App not has asset attr")
def get_target_ip(self):
if self.category_remote_app:
asset = self.get_remote_app_asset()
target_ip = asset.ip
elif self.category_cloud:
target_ip = self.attrs.get('cluster')
elif self.category_db:
target_ip = self.attrs.get('host')
else:
target_ip = ''
return target_ip
class ApplicationUser(SystemUser):
class Meta:

View File

@ -235,6 +235,9 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
def __str__(self):
return '{0.hostname}({0.ip})'.format(self)
def get_target_ip(self):
return self.ip
def set_admin_user_relation(self):
from .authbook import AuthBook
if not self.admin_user:

View File

@ -31,12 +31,13 @@ from perms.models.base import Action
from perms.utils.application.permission import get_application_actions
from perms.utils.asset.permission import get_asset_actions
from common.const.http import PATCH
from terminal.models import EndpointRule
from ..serializers import (
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
)
logger = get_logger(__name__)
__all__ = ['UserConnectionTokenViewSet']
__all__ = ['UserConnectionTokenViewSet', 'TokenCacheMixin']
class ClientProtocolMixin:
@ -51,6 +52,17 @@ class ClientProtocolMixin:
request: Request
get_serializer: Callable
create_token: Callable
get_serializer_context: Callable
def get_smart_endpoint(self, protocol, asset=None, application=None):
if asset:
target_ip = asset.get_target_ip()
elif application:
target_ip = application.get_target_ip()
else:
target_ip = ''
endpoint = EndpointRule.match_endpoint(target_ip, protocol, self.request)
return endpoint
def get_request_resource(self, serializer):
asset = serializer.validated_data.get('asset')
@ -122,10 +134,10 @@ class ClientProtocolMixin:
options['screen mode id:i'] = '2' if full_screen else '1'
# RDP Server 地址
address = settings.TERMINAL_RDP_ADDR
if not address or address == 'localhost:3389':
address = self.request.get_host().split(':')[0] + ':3389'
options['full address:s'] = address
endpoint = self.get_smart_endpoint(
protocol='rdp', asset=asset, application=application
)
options['full address:s'] = f'{endpoint.host}:{endpoint.rdp_port}'
# 用户名
options['username:s'] = '{}|{}'.format(user.username, token)
if system_user.ad_domain:
@ -169,9 +181,12 @@ class ClientProtocolMixin:
else:
name = '*'
endpoint = self.get_smart_endpoint(
protocol='ssh', asset=asset, application=application
)
content = {
'ip': settings.TERMINAL_KOKO_HOST,
'port': str(settings.TERMINAL_KOKO_SSH_PORT),
'ip': endpoint.host,
'port': endpoint.ssh_port,
'username': f'JMS-{token}',
'password': secret
}
@ -345,6 +360,7 @@ class SecretDetailMixin:
class TokenCacheMixin:
""" endpoint smart view 用到此类来解析token中的资产、应用 """
CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}'
def get_token_cache_key(self, token):

View File

@ -4,7 +4,7 @@ import json
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import force_text
from django.core.validators import MinValueValidator, MaxValueValidator
from ..utils import signer, crypto
@ -13,7 +13,7 @@ __all__ = [
'JsonCharField', 'JsonTextField', 'JsonListCharField', 'JsonListTextField',
'JsonDictCharField', 'JsonDictTextField', 'EncryptCharField',
'EncryptTextField', 'EncryptMixin', 'EncryptJsonDictTextField',
'EncryptJsonDictCharField',
'EncryptJsonDictCharField', 'PortField'
]
@ -180,3 +180,13 @@ class EncryptJsonDictTextField(EncryptMixin, JsonDictTextField):
class EncryptJsonDictCharField(EncryptMixin, JsonDictCharField):
pass
class PortField(models.IntegerField):
def __init__(self, *args, **kwargs):
kwargs.update({
'blank': False,
'null': False,
'validators': [MinValueValidator(0), MaxValueValidator(65535)]
})
super().__init__(*args, **kwargs)

View File

@ -310,16 +310,12 @@ class Config(dict):
'TERMINAL_HOST_KEY': '',
'TERMINAL_TELNET_REGEX': '',
'TERMINAL_COMMAND_STORAGE': {},
'TERMINAL_RDP_ADDR': lambda: urlparse(settings.SITE_URL).hostname + ':3389',
'XRDP_ENABLED': True,
'TERMINAL_KOKO_HOST': lambda: urlparse(settings.SITE_URL).hostname,
'TERMINAL_KOKO_SSH_PORT': 2222,
# 未来废弃(当下迁移会用)
'TERMINAL_RDP_ADDR': '',
# 保留(Luna还在用)
'TERMINAL_MAGNUS_ENABLED': True,
'TERMINAL_MAGNUS_HOST': lambda: urlparse(settings.SITE_URL).hostname,
'TERMINAL_MAGNUS_MYSQL_PORT': 33060,
'TERMINAL_MAGNUS_MARIADB_PORT': 33061,
'TERMINAL_MAGNUS_POSTGRE_PORT': 54320,
# 保留(Luna还在用)
'XRDP_ENABLED': True,
# 安全配置
'SECURITY_MFA_AUTH': 0, # 0 不开启 1 全局开启 2 管理员开启

View File

@ -140,9 +140,6 @@ CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS = CONFIG.CLOUD_SYNC_TASK_EXECUTION_KEEP_DAYS
XRDP_ENABLED = CONFIG.XRDP_ENABLED
TERMINAL_KOKO_HOST = CONFIG.TERMINAL_KOKO_HOST
TERMINAL_KOKO_SSH_PORT = CONFIG.TERMINAL_KOKO_SSH_PORT
# SMS enabled
SMS_ENABLED = CONFIG.SMS_ENABLED
SMS_BACKEND = CONFIG.SMS_BACKEND
@ -170,11 +167,3 @@ ANNOUNCEMENT = CONFIG.ANNOUNCEMENT
# help
HELP_DOCUMENT_URL = CONFIG.HELP_DOCUMENT_URL
HELP_SUPPORT_URL = CONFIG.HELP_SUPPORT_URL
# Magnus
MAGNUS_ENABLED = CONFIG.MAGNUS_ENABLED
TERMINAL_MAGNUS_HOST = CONFIG.TERMINAL_MAGNUS_HOST
TERMINAL_MAGNUS_ENABLED = CONFIG.TERMINAL_MAGNUS_ENABLED
TERMINAL_MAGNUS_MYSQL_PORT = CONFIG.TERMINAL_MAGNUS_MYSQL_PORT
TERMINAL_MAGNUS_MARIADB_PORT = CONFIG.TERMINAL_MAGNUS_MARIADB_PORT
TERMINAL_MAGNUS_POSTGRE_PORT = CONFIG.TERMINAL_MAGNUS_POSTGRE_PORT

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:097c6d06ed8dcf2e1807560b6eb52d98cba31f25fe8d67ce4315668c150ca6b8
size 129989
oid sha256:70685e92cbf84f4178224a44fd84eb884a0acfb9749200541ea6655a9a397a72
size 125019

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3d6a0d40534209f3ffab0b0ecfab7ec82f137156fc8e7b19ce1711036d14aeca
size 107709
oid sha256:9c13775875a335e3c8dbc7f666af622c5aa12050100b15e616c210e8e3043e38
size 103490

File diff suppressed because it is too large Load Diff

View File

@ -64,13 +64,6 @@ class PublicSettingApi(generics.RetrieveAPIView):
"AUTH_FEISHU": settings.AUTH_FEISHU,
# Terminal
"XRDP_ENABLED": settings.XRDP_ENABLED,
"TERMINAL_KOKO_HOST": settings.TERMINAL_KOKO_HOST,
"TERMINAL_KOKO_SSH_PORT": settings.TERMINAL_KOKO_SSH_PORT,
"TERMINAL_MAGNUS_ENABLED": settings.TERMINAL_MAGNUS_ENABLED,
"TERMINAL_MAGNUS_HOST": settings.TERMINAL_MAGNUS_HOST,
"TERMINAL_MAGNUS_MYSQL_PORT": settings.TERMINAL_MAGNUS_MYSQL_PORT,
"TERMINAL_MAGNUS_MARIADB_PORT": settings.TERMINAL_MAGNUS_MARIADB_PORT,
"TERMINAL_MAGNUS_POSTGRE_PORT": settings.TERMINAL_MAGNUS_POSTGRE_PORT,
# Announcement
"ANNOUNCEMENT_ENABLED": settings.ANNOUNCEMENT_ENABLED,
"ANNOUNCEMENT": settings.ANNOUNCEMENT,

View File

@ -33,33 +33,5 @@ class TerminalSettingSerializer(serializers.Serializer):
help_text=_("The login success message varies with devices. "
"if you cannot log in to the device through Telnet, set this parameter")
)
TERMINAL_RDP_ADDR = serializers.CharField(
required=False, label=_("RDP address"), max_length=1024, allow_blank=True,
help_text=_('RDP visit address, eg: dev.jumpserver.org:3389')
)
XRDP_ENABLED = serializers.BooleanField(label=_("Enable XRDP"))
TERMINAL_KOKO_HOST = serializers.CharField(
required=False, label=_("Koko host"), max_length=1024
)
TERMINAL_KOKO_SSH_PORT = serializers.CharField(
required=False, label=_("Koko ssh port"), max_length=1024
)
TERMINAL_MAGNUS_ENABLED = serializers.BooleanField(label=_("Enable database proxy"))
TERMINAL_MAGNUS_HOST = serializers.CharField(
required=False, label=_("Database proxy host"), max_length=1024, allow_blank=True,
help_text=_('Database proxy host, eg: dev.jumpserver.org')
)
TERMINAL_MAGNUS_MYSQL_PORT = serializers.IntegerField(
required=False, label=_("MySQL port"), default=33060,
help_text=_('MySQL protocol listen port')
)
TERMINAL_MAGNUS_MARIADB_PORT = serializers.IntegerField(
required=False, label=_("MariaDB port"), default=33061,
help_text=_('MariaDB protocol listen port')
)
TERMINAL_MAGNUS_POSTGRE_PORT = serializers.IntegerField(
required=False, label=_("PostgreSQL port"), default=54320,
help_text=_('PostgreSQL protocol listen port')
)
XRDP_ENABLED = serializers.BooleanField(label=_("Enable XRDP"))

View File

@ -7,3 +7,4 @@ from .task import *
from .storage import *
from .status import *
from .sharing import *
from .endpoint import *

View File

@ -0,0 +1,78 @@
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from common.drf.api import JMSBulkModelViewSet
from common.utils import get_object_or_none
from django.shortcuts import get_object_or_404
from assets.models import Asset
from orgs.utils import tmp_to_root_org
from applications.models import Application
from terminal.models import Session
from ..models import Endpoint, EndpointRule
from .. import serializers
__all__ = ['EndpointViewSet', 'EndpointRuleViewSet']
class EndpointViewSet(JMSBulkModelViewSet):
filterset_fields = ('name', 'host')
search_fields = filterset_fields
serializer_class = serializers.EndpointSerializer
queryset = Endpoint.objects.all()
rbac_perms = {
'smart': 'terminal.view_endpoint'
}
@staticmethod
def get_target_ip(request):
# 用来方便测试
target_ip = request.GET.get('target_ip')
if target_ip:
return target_ip
asset_id = request.GET.get('asset_id')
app_id = request.GET.get('app_id')
session_id = request.GET.get('session_id')
token = request.GET.get('token')
if token:
from authentication.api.connection_token import TokenCacheMixin as TokenUtil
value = TokenUtil().get_token_from_cache(token)
if value:
if value.get('type') == 'asset':
asset_id = value.get('asset')
else:
app_id = value.get('application')
if asset_id:
pk, model = asset_id, Asset
elif app_id:
pk, model = app_id, Application
elif session_id:
pk, model = session_id, Session
else:
return ''
with tmp_to_root_org():
instance = get_object_or_404(model, pk=pk)
target_ip = instance.get_target_ip()
return target_ip
@action(methods=['get'], detail=False, url_path='smart')
def smart(self, request, *args, **kwargs):
protocol = request.GET.get('protocol')
if not protocol:
return Response(
data={'error': _('Not found protocol query params')},
status=status.HTTP_404_NOT_FOUND
)
target_ip = self.get_target_ip(request)
endpoint = EndpointRule.match_endpoint(target_ip, protocol, request)
serializer = self.get_serializer(endpoint)
return Response(serializer.data)
class EndpointRuleViewSet(JMSBulkModelViewSet):
filterset_fields = ('name',)
search_fields = filterset_fields
serializer_class = serializers.EndpointRuleSerializer
queryset = EndpointRule.objects.all()

View File

@ -0,0 +1,86 @@
# Generated by Django 3.1.14 on 2022-04-12 07:39
import common.fields.model
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import uuid
from django.conf import settings
def migrate_endpoints(apps, schema_editor):
endpoint_data = {
'id': '00000000-0000-0000-0000-000000000001',
'name': 'Default',
'host': '',
'https_port': 0,
'http_port': 0,
'created_by': 'System'
}
if settings.XRDP_ENABLED:
xrdp_addr = settings.TERMINAL_RDP_ADDR
if ':' in xrdp_addr:
hostname, port = xrdp_addr.strip().split(':')
else:
hostname, port = xrdp_addr, 3389
endpoint_data.update({
'host': '' if hostname.strip() in ['localhost', '127.0.0.1'] else hostname.strip(),
'rdp_port': int(port)
})
Endpoint = apps.get_model("terminal", "Endpoint")
Endpoint.objects.create(**endpoint_data)
class Migration(migrations.Migration):
dependencies = [
('terminal', '0047_auto_20220302_1951'),
]
operations = [
migrations.CreateModel(
name='Endpoint',
fields=[
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, blank=True, verbose_name='Name')),
('host', models.CharField(max_length=256, verbose_name='Host')),
('https_port', common.fields.model.PortField(default=443, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTPS Port')),
('http_port', common.fields.model.PortField(default=80, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTP Port')),
('ssh_port', common.fields.model.PortField(default=2222, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='SSH Port')),
('rdp_port', common.fields.model.PortField(default=3389, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='RDP Port')),
('mysql_port', common.fields.model.PortField(default=33060, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='MySQL Port')),
('mariadb_port', common.fields.model.PortField(default=33061, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='MariaDB Port')),
('postgresql_port', common.fields.model.PortField(default=54320, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='PostgreSQL Port')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
],
options={
'verbose_name': 'Endpoint',
'ordering': ('name',),
},
),
migrations.CreateModel(
name='EndpointRule',
fields=[
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('ip_group', models.JSONField(default=list, verbose_name='IP group')),
('priority', models.IntegerField(help_text='1-100, the lower the value will be match first', unique=True, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(100)], verbose_name='Priority')),
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
('endpoint', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rules', to='terminal.endpoint', verbose_name='Endpoint')),
],
options={
'verbose_name': 'Endpoint rule',
'ordering': ('priority', 'name'),
},
),
migrations.RunPython(migrate_endpoints),
]

View File

@ -6,3 +6,4 @@ from .task import *
from .terminal import *
from .sharing import *
from .replay import *
from .endpoint import *

View File

@ -0,0 +1,94 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
from common.db.models import JMSModel
from common.fields.model import PortField
from common.utils.ip import contains_ip
class Endpoint(JMSModel):
name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True)
host = models.CharField(max_length=256, blank=True, verbose_name=_('Host'))
# disabled value=0
https_port = PortField(default=443, verbose_name=_('HTTPS Port'))
http_port = PortField(default=80, verbose_name=_('HTTP Port'))
ssh_port = PortField(default=2222, verbose_name=_('SSH Port'))
rdp_port = PortField(default=3389, verbose_name=_('RDP Port'))
mysql_port = PortField(default=33060, verbose_name=_('MySQL Port'))
mariadb_port = PortField(default=33061, verbose_name=_('MariaDB Port'))
postgresql_port = PortField(default=54320, verbose_name=_('PostgreSQL Port'))
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
default_id = '00000000-0000-0000-0000-000000000001'
class Meta:
verbose_name = _('Endpoint')
ordering = ('name',)
def __str__(self):
return self.name
def get_port(self, protocol):
return getattr(self, f'{protocol}_port', 0)
def delete(self, using=None, keep_parents=False):
if self.id == self.default_id:
return
return super().delete(using, keep_parents)
@classmethod
def get_or_create_default(cls, request=None):
data = {
'id': cls.default_id,
'name': 'Default',
'host': '',
'https_port': 0,
'http_port': 0,
}
endpoint, created = cls.objects.get_or_create(id=cls.default_id, defaults=data)
if not endpoint.host and request:
endpoint.host = request.get_host().split(':')[0]
return endpoint
class EndpointRule(JMSModel):
name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True)
ip_group = models.JSONField(default=list, verbose_name=_('IP group'))
priority = models.IntegerField(
verbose_name=_("Priority"), validators=[MinValueValidator(1), MaxValueValidator(100)],
unique=True, help_text=_("1-100, the lower the value will be match first"),
)
endpoint = models.ForeignKey(
'terminal.Endpoint', null=True, blank=True, related_name='rules',
on_delete=models.SET_NULL, verbose_name=_("Endpoint"),
)
comment = models.TextField(default='', blank=True, verbose_name=_('Comment'))
class Meta:
verbose_name = _('Endpoint rule')
ordering = ('priority', 'name')
def __str__(self):
return f'{self.name}({self.priority})'
@classmethod
def match(cls, target_ip, protocol):
for endpoint_rule in cls.objects.all().prefetch_related('endpoint'):
if not contains_ip(target_ip, endpoint_rule.ip_group):
continue
if not endpoint_rule.endpoint:
continue
if not endpoint_rule.endpoint.host:
continue
if endpoint_rule.endpoint.get_port(protocol) == 0:
continue
return endpoint_rule
@classmethod
def match_endpoint(cls, target_ip, protocol, request=None):
endpoint_rule = cls.match(target_ip, protocol)
if endpoint_rule:
endpoint = endpoint_rule.endpoint
else:
endpoint = Endpoint.get_or_create_default(request)
return endpoint

View File

@ -11,9 +11,11 @@ from django.core.files.storage import default_storage
from django.core.cache import cache
from assets.models import Asset
from applications.models import Application
from users.models import User
from orgs.mixins.models import OrgModelMixin
from django.db.models import TextChoices
from common.utils import get_object_or_none
from ..backends import get_multi_command_storage
@ -194,6 +196,13 @@ class Session(OrgModelMixin):
def login_from_display(self):
return self.get_login_from_display()
def get_target_ip(self):
instance = get_object_or_none(Asset, pk=self.asset_id)
if not instance:
instance = get_object_or_none(Application, pk=self.asset_id)
target_ip = instance.get_target_ip() if instance else ''
return target_ip
@classmethod
def generate_fake(cls, count=100, is_finished=True):
import random

View File

@ -4,3 +4,4 @@ from .terminal import *
from .session import *
from .storage import *
from .sharing import *
from .endpoint import *

View File

@ -0,0 +1,51 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from common.drf.serializers import BulkModelSerializer
from acls.serializers.rules import ip_group_help_text, ip_group_child_validator
from ..models import Endpoint, EndpointRule
__all__ = ['EndpointSerializer', 'EndpointRuleSerializer']
class EndpointSerializer(BulkModelSerializer):
class Meta:
model = Endpoint
fields_mini = ['id', 'name']
fields_small = [
'host',
'https_port', 'http_port', 'ssh_port',
'rdp_port', 'mysql_port', 'mariadb_port',
'postgresql_port',
]
fields = fields_mini + fields_small + [
'comment', 'date_created', 'date_updated', 'created_by'
]
extra_kwargs = {
'https_port': {'default': 443},
'http_port': {'default': 80},
'ssh_port': {'default': 2222},
'rdp_port': {'default': 3389},
'mysql_port': {'default': 33060},
'mariadb_port': {'default': 33061},
'postgresql_port': {'default': 54320},
}
class EndpointRuleSerializer(BulkModelSerializer):
ip_group = serializers.ListField(
default=['*'], label=_('IP'), help_text=ip_group_help_text,
child=serializers.CharField(max_length=1024, validators=[ip_group_child_validator])
)
endpoint_display = serializers.ReadOnlyField(source='endpoint.name', label=_('Endpoint'))
class Meta:
model = EndpointRule
fields_mini = ['id', 'name']
fields_small = fields_mini + ['ip_group', 'priority']
fields_fk = ['endpoint', 'endpoint_display']
fields = fields_mini + fields_small + fields_fk + [
'comment', 'date_created', 'date_updated', 'created_by'
]
extra_kwargs = {
}

View File

@ -22,6 +22,8 @@ router.register(r'replay-storages', api.ReplayStorageViewSet, 'replay-storage')
router.register(r'command-storages', api.CommandStorageViewSet, 'command-storage')
router.register(r'session-sharings', api.SessionSharingViewSet, 'session-sharing')
router.register(r'session-join-records', api.SessionJoinRecordsViewSet, 'session-sharing-record')
router.register(r'endpoints', api.EndpointViewSet, 'endpoint')
router.register(r'endpoint-rules', api.EndpointRuleViewSet, 'endpoint-rule')
urlpatterns = [
path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'),