mirror of https://github.com/jumpserver/jumpserver
perf: connection token 分api权限
parent
2493647e5c
commit
d23953932f
|
@ -7,7 +7,6 @@ import os
|
||||||
import base64
|
import base64
|
||||||
import ctypes
|
import ctypes
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import HttpResponse
|
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 common.const.http import PATCH
|
||||||
from terminal.models import EndpointRule
|
from terminal.models import EndpointRule
|
||||||
from ..serializers import (
|
from ..serializers import (
|
||||||
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
|
ConnectionTokenSerializer, ConnectionTokenSecretSerializer, SuperConnectionTokenSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
__all__ = ['UserConnectionTokenViewSet', 'TokenCacheMixin']
|
__all__ = ['UserConnectionTokenViewSet', 'UserSuperConnectionTokenViewSet', 'TokenCacheMixin']
|
||||||
|
|
||||||
|
|
||||||
class ClientProtocolMixin:
|
class ClientProtocolMixin:
|
||||||
|
@ -70,8 +69,7 @@ class ClientProtocolMixin:
|
||||||
system_user = serializer.validated_data['system_user']
|
system_user = serializer.validated_data['system_user']
|
||||||
|
|
||||||
user = serializer.validated_data.get('user')
|
user = serializer.validated_data.get('user')
|
||||||
if not user or not self.request.user.is_superuser:
|
user = user if user else self.request.user
|
||||||
user = self.request.user
|
|
||||||
return asset, application, system_user, user
|
return asset, application, system_user, user
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -105,7 +103,7 @@ class ClientProtocolMixin:
|
||||||
'bookmarktype:i': '3',
|
'bookmarktype:i': '3',
|
||||||
'use redirection server name:i': '0',
|
'use redirection server name:i': '0',
|
||||||
'smart sizing:i': '1',
|
'smart sizing:i': '1',
|
||||||
#'drivestoredirect:s': '*',
|
# 'drivestoredirect:s': '*',
|
||||||
# 'domain:s': ''
|
# 'domain:s': ''
|
||||||
# 'alternate shell:s:': '||MySQLWorkbench',
|
# 'alternate shell:s:': '||MySQLWorkbench',
|
||||||
# 'remoteapplicationname:s': 'Firefox',
|
# 'remoteapplicationname:s': 'Firefox',
|
||||||
|
@ -206,21 +204,6 @@ class ClientProtocolMixin:
|
||||||
rst = rst.decode('ascii')
|
rst = rst.decode('ascii')
|
||||||
return rst
|
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):
|
def get_valid_serializer(self):
|
||||||
if self.request.method == 'GET':
|
if self.request.method == 'GET':
|
||||||
data = self.request.query_params
|
data = self.request.query_params
|
||||||
|
@ -252,6 +235,21 @@ class ClientProtocolMixin:
|
||||||
}
|
}
|
||||||
return data
|
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')
|
@action(methods=['POST', 'GET'], detail=False, url_path='client-url')
|
||||||
def get_client_protocol_url(self, request, *args, **kwargs):
|
def get_client_protocol_url(self, request, *args, **kwargs):
|
||||||
serializer = self.get_valid_serializer()
|
serializer = self.get_valid_serializer()
|
||||||
|
@ -370,7 +368,7 @@ class TokenCacheMixin:
|
||||||
key = self.get_token_cache_key(token)
|
key = self.get_token_cache_key(token)
|
||||||
return cache.ttl(key)
|
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)
|
key = self.get_token_cache_key(token)
|
||||||
cache.set(key, value, timeout=ttl)
|
cache.set(key, value, timeout=ttl)
|
||||||
|
|
||||||
|
@ -379,7 +377,7 @@ class TokenCacheMixin:
|
||||||
value = cache.get(key, None)
|
value = cache.get(key, None)
|
||||||
return value
|
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)
|
value = self.get_token_from_cache(token)
|
||||||
if value:
|
if value:
|
||||||
pre_ttl = self.get_token_ttl(token)
|
pre_ttl = self.get_token_ttl(token)
|
||||||
|
@ -397,22 +395,10 @@ class TokenCacheMixin:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
class UserConnectionTokenViewSet(
|
class BaseUserConnectionTokenViewSet(
|
||||||
RootOrgViewMixin, SerializerMixin, ClientProtocolMixin,
|
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
|
@staticmethod
|
||||||
def check_resource_permission(user, asset, application, system_user):
|
def check_resource_permission(user, asset, application, system_user):
|
||||||
|
@ -429,22 +415,7 @@ class UserConnectionTokenViewSet(
|
||||||
raise PermissionDenied(error)
|
raise PermissionDenied(error)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@action(methods=[PATCH], detail=False)
|
def create_token(self, user, asset, application, system_user, ttl=5 * 60):
|
||||||
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')
|
|
||||||
self.check_resource_permission(user, asset, application, system_user)
|
self.check_resource_permission(user, asset, application, system_user)
|
||||||
token = random_string(36)
|
token = random_string(36)
|
||||||
secret = random_string(16)
|
secret = random_string(16)
|
||||||
|
@ -489,6 +460,20 @@ class UserConnectionTokenViewSet(
|
||||||
}
|
}
|
||||||
return Response(data, status=201)
|
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):
|
def valid_token(self, token):
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from assets.models import SystemUser, Asset
|
from assets.models import SystemUser, Asset
|
||||||
|
@ -526,3 +511,23 @@ class UserConnectionTokenViewSet(
|
||||||
if not value:
|
if not value:
|
||||||
return Response('', status=404)
|
return Response('', status=404)
|
||||||
return Response(value)
|
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)
|
||||||
|
|
|
@ -13,24 +13,16 @@ __all__ = [
|
||||||
'ConnectionTokenUserSerializer', 'ConnectionTokenFilterRuleSerializer',
|
'ConnectionTokenUserSerializer', 'ConnectionTokenFilterRuleSerializer',
|
||||||
'ConnectionTokenAssetSerializer', 'ConnectionTokenSystemUserSerializer',
|
'ConnectionTokenAssetSerializer', 'ConnectionTokenSystemUserSerializer',
|
||||||
'ConnectionTokenDomainSerializer', 'ConnectionTokenRemoteAppSerializer',
|
'ConnectionTokenDomainSerializer', 'ConnectionTokenRemoteAppSerializer',
|
||||||
'ConnectionTokenGatewaySerializer', 'ConnectionTokenSecretSerializer'
|
'ConnectionTokenGatewaySerializer', 'ConnectionTokenSecretSerializer',
|
||||||
|
'SuperConnectionTokenSerializer'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ConnectionTokenSerializer(serializers.Serializer):
|
class ConnectionTokenSerializer(serializers.Serializer):
|
||||||
user = serializers.CharField(max_length=128, required=False, allow_blank=True)
|
|
||||||
system_user = serializers.CharField(max_length=128, required=True)
|
system_user = serializers.CharField(max_length=128, required=True)
|
||||||
asset = serializers.CharField(max_length=128, required=False)
|
asset = serializers.CharField(max_length=128, required=False)
|
||||||
application = 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
|
@staticmethod
|
||||||
def validate_system_user(system_user_id):
|
def validate_system_user(system_user_id):
|
||||||
from assets.models import SystemUser
|
from assets.models import SystemUser
|
||||||
|
@ -65,6 +57,18 @@ class ConnectionTokenSerializer(serializers.Serializer):
|
||||||
return super().validate(attrs)
|
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 ConnectionTokenUserSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -114,7 +118,6 @@ class ConnectionTokenDomainSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class ConnectionTokenFilterRuleSerializer(serializers.ModelSerializer):
|
class ConnectionTokenFilterRuleSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CommandFilterRule
|
model = CommandFilterRule
|
||||||
fields = [
|
fields = [
|
||||||
|
|
|
@ -11,6 +11,7 @@ router.register('access-keys', api.AccessKeyViewSet, 'access-key')
|
||||||
router.register('sso', api.SSOViewSet, 'sso')
|
router.register('sso', api.SSOViewSet, 'sso')
|
||||||
router.register('temp-tokens', api.TempTokenViewSet, 'temp-token')
|
router.register('temp-tokens', api.TempTokenViewSet, 'temp-token')
|
||||||
router.register('connection-token', api.UserConnectionTokenViewSet, 'connection-token')
|
router.register('connection-token', api.UserConnectionTokenViewSet, 'connection-token')
|
||||||
|
router.register('super-connection-token', api.UserSuperConnectionTokenViewSet, 'super-connection-token')
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
Loading…
Reference in New Issue