diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 9e16d31ba..ef69890e5 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -7,7 +7,6 @@ import os import base64 import ctypes -from django.conf import settings from django.core.cache import cache from django.shortcuts import get_object_or_404 from django.http import HttpResponse @@ -33,11 +32,11 @@ 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, + ConnectionTokenSerializer, ConnectionTokenSecretSerializer, SuperConnectionTokenSerializer ) logger = get_logger(__name__) -__all__ = ['UserConnectionTokenViewSet', 'TokenCacheMixin'] +__all__ = ['UserConnectionTokenViewSet', 'UserSuperConnectionTokenViewSet', 'TokenCacheMixin'] class ClientProtocolMixin: @@ -70,8 +69,7 @@ class ClientProtocolMixin: system_user = serializer.validated_data['system_user'] user = serializer.validated_data.get('user') - if not user or not self.request.user.is_superuser: - user = self.request.user + user = user if user else self.request.user return asset, application, system_user, user @staticmethod @@ -105,7 +103,7 @@ class ClientProtocolMixin: 'bookmarktype:i': '3', 'use redirection server name:i': '0', 'smart sizing:i': '1', - #'drivestoredirect:s': '*', + # 'drivestoredirect:s': '*', # 'domain:s': '' # 'alternate shell:s:': '||MySQLWorkbench', # 'remoteapplicationname:s': 'Firefox', @@ -206,21 +204,6 @@ class ClientProtocolMixin: rst = rst.decode('ascii') return rst - @action(methods=['POST', 'GET'], detail=False, url_path='rdp/file') - def get_rdp_file(self, request, *args, **kwargs): - if self.request.method == 'GET': - data = self.request.query_params - else: - data = self.request.data - serializer = self.get_serializer(data=data) - serializer.is_valid(raise_exception=True) - name, data = self.get_rdp_file_content(serializer) - response = HttpResponse(data, content_type='application/octet-stream') - filename = "{}-{}-jumpserver.rdp".format(self.request.user.username, name) - filename = urllib.parse.quote(filename) - response['Content-Disposition'] = 'attachment; filename*=UTF-8\'\'%s' % filename - return response - def get_valid_serializer(self): if self.request.method == 'GET': data = self.request.query_params @@ -252,6 +235,21 @@ class ClientProtocolMixin: } return data + @action(methods=['POST', 'GET'], detail=False, url_path='rdp/file') + def get_rdp_file(self, request, *args, **kwargs): + if self.request.method == 'GET': + data = self.request.query_params + else: + data = self.request.data + serializer = self.get_serializer(data=data) + serializer.is_valid(raise_exception=True) + name, data = self.get_rdp_file_content(serializer) + response = HttpResponse(data, content_type='application/octet-stream') + filename = "{}-{}-jumpserver.rdp".format(self.request.user.username, name) + filename = urllib.parse.quote(filename) + response['Content-Disposition'] = 'attachment; filename*=UTF-8\'\'%s' % filename + return response + @action(methods=['POST', 'GET'], detail=False, url_path='client-url') def get_client_protocol_url(self, request, *args, **kwargs): serializer = self.get_valid_serializer() @@ -370,7 +368,7 @@ class TokenCacheMixin: key = self.get_token_cache_key(token) return cache.ttl(key) - def set_token_to_cache(self, token, value, ttl=5*60): + def set_token_to_cache(self, token, value, ttl=5 * 60): key = self.get_token_cache_key(token) cache.set(key, value, timeout=ttl) @@ -379,7 +377,7 @@ class TokenCacheMixin: value = cache.get(key, None) return value - def renewal_token(self, token, ttl=5*60): + def renewal_token(self, token, ttl=5 * 60): value = self.get_token_from_cache(token) if value: pre_ttl = self.get_token_ttl(token) @@ -397,22 +395,10 @@ class TokenCacheMixin: return data -class UserConnectionTokenViewSet( +class BaseUserConnectionTokenViewSet( RootOrgViewMixin, SerializerMixin, ClientProtocolMixin, - SecretDetailMixin, TokenCacheMixin, GenericViewSet + TokenCacheMixin, GenericViewSet ): - serializer_classes = { - 'default': ConnectionTokenSerializer, - 'get_secret_detail': ConnectionTokenSecretSerializer, - } - rbac_perms = { - 'GET': 'authentication.view_connectiontoken', - 'create': 'authentication.add_connectiontoken', - 'renewal': 'authentication.add_superconnectiontoken', - 'get_secret_detail': 'authentication.view_connectiontokensecret', - 'get_rdp_file': 'authentication.add_connectiontoken', - 'get_client_protocol_url': 'authentication.add_connectiontoken', - } @staticmethod def check_resource_permission(user, asset, application, system_user): @@ -429,22 +415,7 @@ class UserConnectionTokenViewSet( raise PermissionDenied(error) return True - @action(methods=[PATCH], detail=False) - def renewal(self, request, *args, **kwargs): - """ 续期 Token """ - perm_required = 'authentication.add_superconnectiontoken' - if not request.user.has_perm(perm_required): - raise PermissionDenied('No permissions for authentication.add_superconnectiontoken') - token = request.data.get('token', '') - data = self.renewal_token(token) - status_code = 200 if data.get('ok') else 404 - return Response(data=data, status=status_code) - - def create_token(self, user, asset, application, system_user, ttl=5*60): - # 再次强调一下权限 - perm_required = 'authentication.add_superconnectiontoken' - if user != self.request.user and not self.request.user.has_perm(perm_required): - raise PermissionDenied('Only can create user token') + def create_token(self, user, asset, application, system_user, ttl=5 * 60): self.check_resource_permission(user, asset, application, system_user) token = random_string(36) secret = random_string(16) @@ -489,6 +460,20 @@ class UserConnectionTokenViewSet( } return Response(data, status=201) + +class UserConnectionTokenViewSet(BaseUserConnectionTokenViewSet, SecretDetailMixin): + serializer_classes = { + 'default': ConnectionTokenSerializer, + 'get_secret_detail': ConnectionTokenSecretSerializer, + } + rbac_perms = { + 'GET': 'authentication.view_connectiontoken', + 'create': 'authentication.add_connectiontoken', + 'get_secret_detail': 'authentication.view_connectiontokensecret', + 'get_rdp_file': 'authentication.add_connectiontoken', + 'get_client_protocol_url': 'authentication.add_connectiontoken', + } + def valid_token(self, token): from users.models import User from assets.models import SystemUser, Asset @@ -526,3 +511,23 @@ class UserConnectionTokenViewSet( if not value: return Response('', status=404) return Response(value) + + +class UserSuperConnectionTokenViewSet( + BaseUserConnectionTokenViewSet, TokenCacheMixin, GenericViewSet +): + serializer_classes = { + 'default': SuperConnectionTokenSerializer, + } + rbac_perms = { + 'create': 'authentication.add_superconnectiontoken', + 'renewal': 'authentication.add_superconnectiontoken' + } + + @action(methods=[PATCH], detail=False) + def renewal(self, request, *args, **kwargs): + """ 续期 Token """ + token = request.data.get('token', '') + data = self.renewal_token(token) + status_code = 200 if data.get('ok') else 404 + return Response(data=data, status=status_code) diff --git a/apps/authentication/serializers/connect_token.py b/apps/authentication/serializers/connect_token.py index c8f94909d..d9694b5bd 100644 --- a/apps/authentication/serializers/connect_token.py +++ b/apps/authentication/serializers/connect_token.py @@ -13,24 +13,16 @@ __all__ = [ 'ConnectionTokenUserSerializer', 'ConnectionTokenFilterRuleSerializer', 'ConnectionTokenAssetSerializer', 'ConnectionTokenSystemUserSerializer', 'ConnectionTokenDomainSerializer', 'ConnectionTokenRemoteAppSerializer', - 'ConnectionTokenGatewaySerializer', 'ConnectionTokenSecretSerializer' + 'ConnectionTokenGatewaySerializer', 'ConnectionTokenSecretSerializer', + 'SuperConnectionTokenSerializer' ] class ConnectionTokenSerializer(serializers.Serializer): - user = serializers.CharField(max_length=128, required=False, allow_blank=True) system_user = serializers.CharField(max_length=128, required=True) asset = serializers.CharField(max_length=128, required=False) application = serializers.CharField(max_length=128, required=False) - @staticmethod - def validate_user(user_id): - from users.models import User - user = User.objects.filter(id=user_id).first() - if user is None: - raise serializers.ValidationError('user id not exist') - return user - @staticmethod def validate_system_user(system_user_id): from assets.models import SystemUser @@ -65,6 +57,18 @@ class ConnectionTokenSerializer(serializers.Serializer): return super().validate(attrs) +class SuperConnectionTokenSerializer(ConnectionTokenSerializer): + user = serializers.CharField(max_length=128, required=False, allow_blank=True) + + @staticmethod + def validate_user(user_id): + from users.models import User + user = User.objects.filter(id=user_id).first() + if user is None: + raise serializers.ValidationError('user id not exist') + return user + + class ConnectionTokenUserSerializer(serializers.ModelSerializer): class Meta: model = User @@ -114,7 +118,6 @@ class ConnectionTokenDomainSerializer(serializers.ModelSerializer): class ConnectionTokenFilterRuleSerializer(serializers.ModelSerializer): - class Meta: model = CommandFilterRule fields = [ diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index 920988ee4..754b7c793 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -11,6 +11,7 @@ router.register('access-keys', api.AccessKeyViewSet, 'access-key') router.register('sso', api.SSOViewSet, 'sso') router.register('temp-tokens', api.TempTokenViewSet, 'temp-token') router.register('connection-token', api.UserConnectionTokenViewSet, 'connection-token') +router.register('super-connection-token', api.UserSuperConnectionTokenViewSet, 'super-connection-token') urlpatterns = [