diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index ec902a21d..16f8907be 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -3,7 +3,7 @@ from django.conf import settings from django.core.cache import cache from django.shortcuts import get_object_or_404 -from rest_framework.serializers import ValidationError +from django.http import HttpResponse from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action @@ -15,7 +15,10 @@ from common.permissions import IsSuperUserOrAppUser, IsValidUser, IsSuperUser from orgs.mixins.api import RootOrgViewMixin -from ..serializers import ConnectionTokenSerializer, ConnectionTokenSecretSerializer +from ..serializers import ( + ConnectionTokenSerializer, ConnectionTokenSecretSerializer, + RDPFileSerializer +) logger = get_logger(__name__) __all__ = ['UserConnectionTokenViewSet'] @@ -25,7 +28,8 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView permission_classes = (IsSuperUserOrAppUser,) serializer_classes = { 'default': ConnectionTokenSerializer, - 'get_secret_detail': ConnectionTokenSecretSerializer + 'get_secret_detail': ConnectionTokenSecretSerializer, + 'get_rdp_file': RDPFileSerializer } CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}' @@ -44,6 +48,12 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView return True def create_token(self, user, asset, application, system_user): + if not settings.CONNECTION_TOKEN_ENABLED: + raise PermissionDenied('Connection token disabled') + if not user: + user = self.request.user + if not self.request.user.is_superuser and user != self.request.user: + raise PermissionDenied('Only super user can create user token') self.check_resource_permission(user, asset, application, system_user) token = random_string(36) value = { @@ -71,26 +81,76 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView return token def create(self, request, *args, **kwargs): - if not settings.CONNECTION_TOKEN_ENABLED: - data = {'error': 'Connection token disabled'} - return Response(data, status=400) - serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - user = serializer.validated_data.get('user', None) - if not request.user.is_superuser and user: - raise PermissionDenied('Only super user can create user token') - if not request.user.is_superuser: - user = self.request.user - asset = serializer.validated_data.get('asset') application = serializer.validated_data.get('application') system_user = serializer.validated_data['system_user'] - + user = serializer.validated_data.get('user') token = self.create_token(user, asset, application, system_user) return Response({"token": token}, status=201) + @action(methods=['POST', 'GET'], detail=False, url_path='rdp/file') + def get_rdp_file(self, request, *args, **kwargs): + options = { + 'full address:s': '', + 'username:s': '', + 'screen mode id:i': '0', + 'desktopwidth:i': '1280', + 'desktopheight:i': '800', + 'use multimon:i': '1', + 'session bpp:i': '24', + 'audiomode:i': '0', + 'disable wallpaper:i': '0', + 'disable full window drag:i': '0', + 'disable menu anims:i': '0', + 'disable themes:i': '0', + 'alternate shell:s': '', + 'shell working directory:s': '', + 'authentication level:i': '2', + 'connect to console:i': '0', + 'disable cursor setting:i': '0', + 'allow font smoothing:i': '1', + 'allow desktop composition:i': '1', + 'redirectprinters:i': '0', + 'prompt for credentials on client:i': '0', + 'autoreconnection enabled:i': '1', + 'bookmarktype:i': '3', + 'use redirection server name:i': '0', + # 'alternate shell:s:': '||MySQLWorkbench', + # 'remoteapplicationname:s': 'Firefox', + # 'remoteapplicationcmdline:s': '', + } + if self.request.method == 'GET': + data = self.request.query_params + else: + data = request.data + serializer = self.get_serializer(data=data) + serializer.is_valid(raise_exception=True) + + asset = serializer.validated_data.get('asset') + application = serializer.validated_data.get('application') + system_user = serializer.validated_data['system_user'] + user = serializer.validated_data.get('user') + height = serializer.validated_data.get('height') + width = serializer.validated_data.get('width') + token = self.create_token(user, asset, application, system_user) + + # Todo: 上线后地址是 JumpServerAddr:3389 + address = self.request.query_params.get('address') or '1.1.1.1' + options['full address:s'] = address + options['username:s'] = '{}@{}'.format(user.username, token) + options['desktopwidth:i'] = width + options['desktopheight:i'] = height + data = '' + for k, v in options.items(): + data += f'{k}:{v}\n' + response = HttpResponse(data, content_type='text/plain') + filename = "{}-{}-jumpserver.rdp".format(user.username, asset.hostname) + response['Content-Disposition'] = 'attachment; filename={}'.format(filename) + return response + @staticmethod def _get_application_secret_detail(value): from applications.models import Application @@ -165,7 +225,7 @@ class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericView return Response(data=serializer.data, status=200) def get_permissions(self): - if self.action == "create": + if self.action in ["create", "get_rdp_file"]: if self.request.data.get('user', None): self.permission_classes = (IsSuperUser,) else: diff --git a/apps/authentication/serializers.py b/apps/authentication/serializers.py index fe56d6fa7..528cc7cf9 100644 --- a/apps/authentication/serializers.py +++ b/apps/authentication/serializers.py @@ -14,7 +14,7 @@ from .models import AccessKey, LoginConfirmSetting, SSOToken __all__ = [ 'AccessKeySerializer', 'OtpVerifySerializer', 'BearerTokenSerializer', 'MFAChallengeSerializer', 'LoginConfirmSettingSerializer', 'SSOTokenSerializer', - 'ConnectionTokenSerializer', 'ConnectionTokenSecretSerializer' + 'ConnectionTokenSerializer', 'ConnectionTokenSecretSerializer', 'RDPFileSerializer' ] @@ -181,3 +181,8 @@ class ConnectionTokenSecretSerializer(serializers.Serializer): system_user = ConnectionTokenSystemUserSerializer(read_only=True) gateway = ConnectionTokenGatewaySerializer(read_only=True) actions = ActionsField() + + +class RDPFileSerializer(ConnectionTokenSerializer): + width = serializers.IntegerField(default=1280) + height = serializers.IntegerField(default=800)