pref: 后端返回 connect types

pull/9115/head
ibuler 2022-11-17 20:48:50 +08:00
parent 4591b03e17
commit 04ee7ee0e7
6 changed files with 180 additions and 83 deletions

View File

@ -2,13 +2,11 @@ from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import status from rest_framework import status
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.generics import ListAPIView
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from assets.models import Asset from assets.models import Asset
from common.drf.api import JMSBulkModelViewSet from common.drf.api import JMSBulkModelViewSet
from common.permissions import IsValidUser
from common.permissions import IsValidUserOrConnectionToken from common.permissions import IsValidUserOrConnectionToken
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org
from terminal import serializers from terminal import serializers
@ -98,15 +96,3 @@ class EndpointRuleViewSet(JMSBulkModelViewSet):
search_fields = filterset_fields search_fields = filterset_fields
serializer_class = serializers.EndpointRuleSerializer serializer_class = serializers.EndpointRuleSerializer
queryset = EndpointRule.objects.all() queryset = EndpointRule.objects.all()
class ConnectMethodListApi(ListAPIView):
permission_classes = (IsValidUser,)
# serializer_class = serializers.ProtocolConnectMethodsSerializer
def get_queryset(self):
protocol = self.request.query_params.get('protocol')
if not protocol:
return []
return Protocol.objects.filter(name=protocol)

View File

@ -1,26 +1,24 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
import logging import logging
import uuid
from django.core.cache import cache
from rest_framework import generics
from rest_framework.views import APIView, Response
from rest_framework import status
from django.conf import settings from django.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import generics
from rest_framework import status
from rest_framework.views import APIView, Response
from common.exceptions import JMSException
from common.drf.api import JMSBulkModelViewSet from common.drf.api import JMSBulkModelViewSet
from common.utils import get_object_or_none, get_request_ip from common.exceptions import JMSException
from common.permissions import IsValidUser
from common.permissions import WithBootstrapToken from common.permissions import WithBootstrapToken
from terminal.models import Terminal
from terminal import serializers from terminal import serializers
from terminal import exceptions from terminal.const import TerminalType
from terminal.models import Terminal
__all__ = [ __all__ = [
'TerminalViewSet', 'TerminalConfig', 'TerminalViewSet', 'TerminalConfig',
'TerminalRegistrationApi', 'TerminalRegistrationApi', 'ConnectMethodListApi'
] ]
logger = logging.getLogger(__file__) logger = logging.getLogger(__file__)
@ -72,3 +70,22 @@ class TerminalRegistrationApi(generics.CreateAPIView):
data = {"error": "service account registration disabled"} data = {"error": "service account registration disabled"}
return Response(data=data, status=status.HTTP_400_BAD_REQUEST) return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
return super().create(request, *args, **kwargs) return super().create(request, *args, **kwargs)
class ConnectMethodListApi(generics.ListAPIView):
serializer_class = serializers.ConnectMethodSerializer
permission_classes = [IsValidUser]
def get_queryset(self):
user_agent = self.request.META['HTTP_USER_AGENT'].lower()
if 'macintosh' in user_agent:
os = 'macos'
elif 'windows' in user_agent:
os = 'windows'
else:
os = 'linux'
return TerminalType.get_protocols_connect_methods(os)
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
return Response(queryset)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from collections import defaultdict
from django.db.models import TextChoices from django.db.models import TextChoices
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -43,24 +44,12 @@ class ComponentLoad(TextChoices):
return set(dict(cls.choices).keys()) return set(dict(cls.choices).keys())
class TerminalType(TextChoices): class HttpMethod(TextChoices):
koko = 'koko', 'KoKo' web_gui = 'web_gui', 'Web GUI'
guacamole = 'guacamole', 'Guacamole' web_cli = 'web_cli', 'Web CLI'
omnidb = 'omnidb', 'OmniDB'
xrdp = 'xrdp', 'Xrdp'
lion = 'lion', 'Lion'
core = 'core', 'Core'
celery = 'celery', 'Celery'
magnus = 'magnus', 'Magnus'
razor = 'razor', 'Razor'
tinker = 'tinker', 'Tinker'
@classmethod
def types(cls):
return set(dict(cls.choices).keys())
class NativeClient: class NativeClient(TextChoices):
# Koko # Koko
ssh = 'ssh', 'ssh' ssh = 'ssh', 'ssh'
putty = 'putty', 'PuTTY' putty = 'putty', 'PuTTY'
@ -71,15 +60,42 @@ class NativeClient:
psql = 'psql', 'psql' psql = 'psql', 'psql'
sqlplus = 'sqlplus', 'sqlplus' sqlplus = 'sqlplus', 'sqlplus'
redis = 'redis-cli', 'redis-cli' redis = 'redis-cli', 'redis-cli'
mongodb = 'mongo', 'mongo'
# Razor # Razor
mstsc = 'mstsc', 'Remote Desktop' mstsc = 'mstsc', 'Remote Desktop'
@classmethod @classmethod
def get_native_clients(cls, p, os='windows'): def get_native_clients(cls):
clients = { clients = {
'' Protocol.ssh: {
'default': [cls.ssh],
'windows': [cls.putty],
},
Protocol.rdp: [cls.mstsc],
Protocol.mysql: [cls.mysql],
Protocol.oracle: [cls.sqlplus],
Protocol.postgresql: [cls.psql],
Protocol.redis: [cls.redis],
Protocol.mongodb: [cls.mongodb],
} }
return clients
@classmethod
def get_native_methods(cls, os='windows'):
clients_map = cls.get_native_clients()
methods = defaultdict(list)
for protocol, _clients in clients_map.items():
if isinstance(_clients, dict):
_clients = _clients.get(os, _clients['default'])
for client in _clients:
methods[protocol].append({
'value': client.value,
'label': client.label,
'type': 'native',
})
return methods
@classmethod @classmethod
def get_launch_command(cls, name, os='windows'): def get_launch_command(cls, name, os='windows'):
@ -104,33 +120,113 @@ class NativeClient:
class RemoteAppMethod: class RemoteAppMethod:
@classmethod @classmethod
def get_remote_app_methods(cls, protocol): def get_remote_app_methods(cls):
from .models import Applet from .models import Applet
applets = Applet.objects.filter(protocol=protocol) applets = Applet.objects.all()
return applets methods = defaultdict(list)
for applet in applets:
for protocol in applet.protocols:
methods[protocol].append({
'value': applet.name,
'label': applet.display_name,
'icon': applet.icon,
'type': 'remote_app',
})
return methods
class ConnectMethod(TextChoices): class TerminalType(TextChoices):
web_cli = 'web_cli', _('Web CLI') koko = 'koko', 'KoKo'
web_gui = 'web_gui', _('Web GUI') guacamole = 'guacamole', 'Guacamole'
native_client = 'native_client', _('Native Client') omnidb = 'omnidb', 'OmniDB'
remote_app = 'remote_app', _('Remote App') xrdp = 'xrdp', 'Xrdp'
lion = 'lion', 'Lion'
core = 'core', 'Core'
celery = 'celery', 'Celery'
magnus = 'magnus', 'Magnus'
razor = 'razor', 'Razor'
tinker = 'tinker', 'Tinker'
@classmethod @classmethod
def methods(cls): def types(cls):
return set(dict(cls.choices).keys())
@classmethod
def protocols(cls):
return { return {
Protocol.ssh: [cls.web_cli, cls.native_client], cls.koko: {
Protocol.rdp: ([cls.web_gui], [cls.native_client]), 'http_method': HttpMethod.web_cli,
Protocol.vnc: [cls.web_gui], 'listen': [Protocol.ssh, Protocol.http],
Protocol.telnet: [cls.web_cli, cls.native_client], 'support': [
Protocol.ssh, Protocol.telnet,
Protocol.mysql: [cls.web_cli, cls.web_gui, cls.native_client], Protocol.mysql, Protocol.postgresql,
Protocol.sqlserver: [cls.web_cli, cls.web_gui], Protocol.oracle, Protocol.sqlserver,
Protocol.oracle: [cls.web_cli, cls.web_gui], Protocol.mariadb, Protocol.redis,
Protocol.postgresql: [cls.web_cli, cls.web_gui], Protocol.mongodb,
Protocol.redis: [cls.web_cli, cls.web_gui, cls.native_client], ],
Protocol.mongodb: [cls.web_cli, cls.web_gui], 'match': 'm2m'
},
Protocol.k8s: [cls.web_cli], cls.omnidb: {
Protocol.http: [], 'http_method': HttpMethod.web_gui,
'listen': [Protocol.http],
'support': [
Protocol.mysql, Protocol.postgresql, Protocol.oracle,
Protocol.sqlserver, Protocol.mariadb
],
'match': 'm2m'
},
cls.lion: {
'http_method': HttpMethod.web_gui,
'listen': [Protocol.http],
'support': [Protocol.rdp, Protocol.vnc],
'match': 'm2m'
},
cls.magnus: {
'listen': [],
'support': [
Protocol.mysql, Protocol.postgresql, Protocol.oracle,
Protocol.mariadb
],
'match': 'map'
},
cls.razor: {
'listen': [Protocol.rdp],
'support': [Protocol.rdp],
'match': 'map'
} }
}
@classmethod
def get_protocols_connect_methods(cls, os):
methods = defaultdict(list)
native_methods = NativeClient.get_native_methods(os)
remote_app_methods = RemoteAppMethod.get_remote_app_methods()
for component, component_protocol in cls.protocols().items():
component_methods = defaultdict(list)
support = component_protocol['support']
for protocol in support:
if component_protocol['match'] == 'map':
listen = [protocol]
else:
listen = component_protocol['listen']
for listen_protocol in listen:
if listen_protocol == Protocol.http:
web_protocol = component_protocol['http_method']
component_methods[protocol.value].append({
'value': web_protocol.value,
'label': web_protocol.label,
'type': 'web',
})
# Native method
component_methods[protocol.value].extend(native_methods[listen_protocol])
component_methods[protocol.value].extend(remote_app_methods[listen_protocol])
for protocol, _methods in component_methods.items():
for method in _methods:
method['component'] = component.value
methods[protocol].extend(_methods)
return methods

View File

@ -1,19 +1,16 @@
import uuid
import time import time
import uuid
from django.utils import timezone
from django.db import models
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
from common.utils import get_logger, lazyproperty from common.utils import get_logger, lazyproperty
from users.models import User
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org
from terminal.const import TerminalType as TypeChoices, ComponentLoad as StatusChoice from terminal.const import TerminalType as TypeChoices
from users.models import User
from ..session import Session from ..session import Session
logger = get_logger(__file__) logger = get_logger(__file__)
@ -87,7 +84,8 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model):
remote_addr = models.CharField(max_length=128, blank=True, verbose_name=_('Remote Address')) remote_addr = models.CharField(max_length=128, blank=True, verbose_name=_('Remote Address'))
command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default') command_storage = models.CharField(max_length=128, verbose_name=_("Command storage"), default='default')
replay_storage = models.CharField(max_length=128, verbose_name=_("Replay storage"), default='default') replay_storage = models.CharField(max_length=128, verbose_name=_("Replay storage"), default='default')
user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True, on_delete=models.CASCADE) user = models.OneToOneField(User, related_name='terminal', verbose_name='Application User', null=True,
on_delete=models.CASCADE)
is_deleted = models.BooleanField(default=False) is_deleted = models.BooleanField(default=False)
date_created = models.DateTimeField(auto_now_add=True) date_created = models.DateTimeField(auto_now_add=True)
comment = models.TextField(blank=True, verbose_name=_('Comment')) comment = models.TextField(blank=True, verbose_name=_('Comment'))
@ -160,4 +158,3 @@ class Terminal(StorageMixin, TerminalStatusMixin, models.Model):
permissions = ( permissions = (
('view_terminalconfig', _('Can view terminal config')), ('view_terminalconfig', _('Can view terminal config')),
) )

View File

@ -136,4 +136,6 @@ class TerminalRegistrationSerializer(serializers.ModelSerializer):
class ConnectMethodSerializer(serializers.Serializer): class ConnectMethodSerializer(serializers.Serializer):
name = serializers.CharField(max_length=128) value = serializers.CharField(max_length=128)
label = serializers.CharField(max_length=128)
group = serializers.CharField(max_length=128)

View File

@ -31,7 +31,6 @@ router.register(r'applet-hosts', api.AppletHostViewSet, 'applet-host')
router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication') router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication')
router.register(r'applet-host-deployments', api.AppletHostDeploymentViewSet, 'applet-host-deployment') router.register(r'applet-host-deployments', api.AppletHostDeploymentViewSet, 'applet-host-deployment')
urlpatterns = [ urlpatterns = [
path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'), path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'),
path('terminal-registrations/', api.TerminalRegistrationApi.as_view(), name='terminal-registration'), path('terminal-registrations/', api.TerminalRegistrationApi.as_view(), name='terminal-registration'),
@ -44,10 +43,13 @@ urlpatterns = [
path('tasks/kill-session-for-ticket/', api.KillSessionForTicketAPI.as_view(), name='kill-session-for-ticket'), path('tasks/kill-session-for-ticket/', api.KillSessionForTicketAPI.as_view(), name='kill-session-for-ticket'),
path('terminals/config/', api.TerminalConfig.as_view(), name='terminal-config'), path('terminals/config/', api.TerminalConfig.as_view(), name='terminal-config'),
path('commands/insecure-command/', api.InsecureCommandAlertAPI.as_view(), name="command-alert"), path('commands/insecure-command/', api.InsecureCommandAlertAPI.as_view(), name="command-alert"),
path('replay-storages/<uuid:pk>/test-connective/', api.ReplayStorageTestConnectiveApi.as_view(), name='replay-storage-test-connective'), path('replay-storages/<uuid:pk>/test-connective/', api.ReplayStorageTestConnectiveApi.as_view(),
path('command-storages/<uuid:pk>/test-connective/', api.CommandStorageTestConnectiveApi.as_view(), name='command-storage-test-connective'), name='replay-storage-test-connective'),
path('command-storages/<uuid:pk>/test-connective/', api.CommandStorageTestConnectiveApi.as_view(),
name='command-storage-test-connective'),
# components # components
path('components/metrics/', api.ComponentsMetricsAPIView.as_view(), name='components-metrics'), path('components/metrics/', api.ComponentsMetricsAPIView.as_view(), name='components-metrics'),
path('components/connect-methods/', api.ConnectMethodListApi.as_view(), name='connect-methods'),
] ]
old_version_urlpatterns = [ old_version_urlpatterns = [
@ -55,6 +57,3 @@ old_version_urlpatterns = [
] ]
urlpatterns += router.urls + old_version_urlpatterns urlpatterns += router.urls + old_version_urlpatterns