mirror of https://github.com/jumpserver/jumpserver
pref: 后端返回 connect types
parent
4591b03e17
commit
04ee7ee0e7
|
@ -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)
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue