# -*- coding: utf-8 -*- # from django.conf import settings from django.core.cache import cache from django.shortcuts import get_object_or_404 from django.http import HttpResponse from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from common.utils import get_logger, random_string from common.drf.api import SerializerMixin2 from common.permissions import IsSuperUserOrAppUser, IsValidUser, IsSuperUser from orgs.mixins.api import RootOrgViewMixin from ..serializers import ( ConnectionTokenSerializer, ConnectionTokenSecretSerializer, RDPFileSerializer ) logger = get_logger(__name__) __all__ = ['UserConnectionTokenViewSet'] class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericViewSet): permission_classes = (IsSuperUserOrAppUser,) serializer_classes = { 'default': ConnectionTokenSerializer, 'get_secret_detail': ConnectionTokenSecretSerializer, 'get_rdp_file': RDPFileSerializer } CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}' @staticmethod def check_resource_permission(user, asset, application, system_user): from perms.utils.asset import has_asset_system_permission from perms.utils.application import has_application_system_permission if asset and not has_asset_system_permission(user, asset, system_user): error = f'User not has this asset and system user permission: ' \ f'user={user.id} system_user={system_user.id} asset={asset.id}' raise PermissionDenied(error) if application and not has_application_system_permission(user, application, system_user): error = f'User not has this application and system user permission: ' \ f'user={user.id} system_user={system_user.id} application={application.id}' raise PermissionDenied(error) 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 = { 'user': str(user.id), 'username': user.username, 'system_user': str(system_user.id), 'system_user_name': system_user.name } if asset: value.update({ 'type': 'asset', 'asset': str(asset.id), 'hostname': asset.hostname, }) elif application: value.update({ 'type': 'application', 'application': application.id, 'application_name': str(application) }) key = self.CACHE_KEY_PREFIX.format(token) cache.set(key, value, timeout=20) return token def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.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') 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 from perms.models import Action application = get_object_or_404(Application, id=value.get('application')) gateway = None if not application.category_remote_app: actions = Action.NONE remote_app = {} asset = None domain = application.domain else: remote_app = application.get_rdp_remote_app_setting() actions = Action.CONNECT asset = application.get_remote_app_asset() domain = asset.domain if domain and domain.has_gateway(): gateway = domain.random_gateway() return { 'asset': asset, 'application': application, 'gateway': gateway, 'remote_app': remote_app, 'actions': actions } @staticmethod def _get_asset_secret_detail(value, user, system_user): from assets.models import Asset from perms.utils.asset import get_asset_system_user_ids_with_actions_by_user asset = get_object_or_404(Asset, id=value.get('asset')) systemuserid_actions_mapper = get_asset_system_user_ids_with_actions_by_user(user, asset) actions = systemuserid_actions_mapper.get(system_user.id, []) gateway = None if asset and asset.domain and asset.domain.has_gateway(): gateway = asset.domain.random_gateway() return { 'asset': asset, 'application': None, 'gateway': gateway, 'remote_app': None, 'actions': actions, } @action(methods=['POST'], detail=False, permission_classes=[IsSuperUserOrAppUser], url_path='secret-info/detail') def get_secret_detail(self, request, *args, **kwargs): from users.models import User from assets.models import SystemUser token = request.data.get('token', '') key = self.CACHE_KEY_PREFIX.format(token) value = cache.get(key, None) if not value: return Response(status=404) user = get_object_or_404(User, id=value.get('user')) system_user = get_object_or_404(SystemUser, id=value.get('system_user')) data = dict(user=user, system_user=system_user) if value.get('type') == 'asset': asset_detail = self._get_asset_secret_detail(value, user=user, system_user=system_user) data['type'] = 'asset' data.update(asset_detail) else: app_detail = self._get_application_secret_detail(value) data['type'] = 'application' data.update(app_detail) serializer = self.get_serializer(data) return Response(data=serializer.data, status=200) def get_permissions(self): if self.action in ["create", "get_rdp_file"]: if self.request.data.get('user', None): self.permission_classes = (IsSuperUser,) else: self.permission_classes = (IsValidUser,) return super().get_permissions() def get(self, request): token = request.query_params.get('token') key = self.CACHE_KEY_PREFIX.format(token) value = cache.get(key, None) if not value: return Response('', status=404) return Response(value)