mirror of https://github.com/jumpserver/jumpserver
feat: 支持续期Connection Token
parent
71f8b40e21
commit
54d1996507
|
@ -30,7 +30,7 @@ from common.http import is_true
|
||||||
from perms.models.base import Action
|
from perms.models.base import Action
|
||||||
from perms.utils.application.permission import get_application_actions
|
from perms.utils.application.permission import get_application_actions
|
||||||
from perms.utils.asset.permission import get_asset_actions
|
from perms.utils.asset.permission import get_asset_actions
|
||||||
|
from common.const.http import PATCH
|
||||||
from ..serializers import (
|
from ..serializers import (
|
||||||
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
|
ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
|
||||||
)
|
)
|
||||||
|
@ -344,18 +344,55 @@ class SecretDetailMixin:
|
||||||
return Response(data=serializer.data, status=200)
|
return Response(data=serializer.data, status=200)
|
||||||
|
|
||||||
|
|
||||||
|
class TokenCacheMixin:
|
||||||
|
CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}'
|
||||||
|
|
||||||
|
def get_token_cache_key(self, token):
|
||||||
|
return self.CACHE_KEY_PREFIX.format(token)
|
||||||
|
|
||||||
|
def get_token_ttl(self, token):
|
||||||
|
key = self.get_token_cache_key(token)
|
||||||
|
return cache.ttl(key)
|
||||||
|
|
||||||
|
def set_token_to_cache(self, token, value, ttl=5*60):
|
||||||
|
key = self.get_token_cache_key(token)
|
||||||
|
cache.set(key, value, timeout=ttl)
|
||||||
|
|
||||||
|
def get_token_from_cache(self, token):
|
||||||
|
key = self.get_token_cache_key(token)
|
||||||
|
value = cache.get(key, None)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def renewal_token(self, token, ttl=5*60):
|
||||||
|
value = self.get_token_from_cache(token)
|
||||||
|
if value:
|
||||||
|
pre_ttl = self.get_token_ttl(token)
|
||||||
|
self.set_token_to_cache(token, value, ttl)
|
||||||
|
post_ttl = self.get_token_ttl(token)
|
||||||
|
ok = True
|
||||||
|
msg = f'{pre_ttl}s is renewed to {post_ttl}s.'
|
||||||
|
else:
|
||||||
|
ok = False
|
||||||
|
msg = 'Token is not found.'
|
||||||
|
data = {
|
||||||
|
'ok': ok,
|
||||||
|
'msg': msg
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class UserConnectionTokenViewSet(
|
class UserConnectionTokenViewSet(
|
||||||
RootOrgViewMixin, SerializerMixin, ClientProtocolMixin,
|
RootOrgViewMixin, SerializerMixin, ClientProtocolMixin,
|
||||||
SecretDetailMixin, GenericViewSet
|
SecretDetailMixin, TokenCacheMixin, GenericViewSet
|
||||||
):
|
):
|
||||||
serializer_classes = {
|
serializer_classes = {
|
||||||
'default': ConnectionTokenSerializer,
|
'default': ConnectionTokenSerializer,
|
||||||
'get_secret_detail': ConnectionTokenSecretSerializer,
|
'get_secret_detail': ConnectionTokenSecretSerializer,
|
||||||
}
|
}
|
||||||
CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}'
|
|
||||||
rbac_perms = {
|
rbac_perms = {
|
||||||
'GET': 'authentication.view_connectiontoken',
|
'GET': 'authentication.view_connectiontoken',
|
||||||
'create': 'authentication.add_connectiontoken',
|
'create': 'authentication.add_connectiontoken',
|
||||||
|
'renewal': 'authentication.add_superconnectiontoken',
|
||||||
'get_secret_detail': 'authentication.view_connectiontokensecret',
|
'get_secret_detail': 'authentication.view_connectiontokensecret',
|
||||||
'get_rdp_file': 'authentication.add_connectiontoken',
|
'get_rdp_file': 'authentication.add_connectiontoken',
|
||||||
'get_client_protocol_url': 'authentication.add_connectiontoken',
|
'get_client_protocol_url': 'authentication.add_connectiontoken',
|
||||||
|
@ -376,7 +413,18 @@ class UserConnectionTokenViewSet(
|
||||||
raise PermissionDenied(error)
|
raise PermissionDenied(error)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def create_token(self, user, asset, application, system_user, ttl=5 * 60):
|
@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'
|
perm_required = 'authentication.add_superconnectiontoken'
|
||||||
if user != self.request.user and not self.request.user.has_perm(perm_required):
|
if user != self.request.user and not self.request.user.has_perm(perm_required):
|
||||||
|
@ -408,8 +456,7 @@ class UserConnectionTokenViewSet(
|
||||||
'application_name': str(application)
|
'application_name': str(application)
|
||||||
})
|
})
|
||||||
|
|
||||||
key = self.CACHE_KEY_PREFIX.format(token)
|
self.set_token_to_cache(token, value, ttl)
|
||||||
cache.set(key, value, timeout=ttl)
|
|
||||||
return token, secret
|
return token, secret
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
|
@ -432,8 +479,7 @@ class UserConnectionTokenViewSet(
|
||||||
from perms.utils.asset.permission import validate_permission as asset_validate_permission
|
from perms.utils.asset.permission import validate_permission as asset_validate_permission
|
||||||
from perms.utils.application.permission import validate_permission as app_validate_permission
|
from perms.utils.application.permission import validate_permission as app_validate_permission
|
||||||
|
|
||||||
key = self.CACHE_KEY_PREFIX.format(token)
|
value = self.get_token_from_cache(token)
|
||||||
value = cache.get(key, None)
|
|
||||||
if not value:
|
if not value:
|
||||||
raise serializers.ValidationError('Token not found')
|
raise serializers.ValidationError('Token not found')
|
||||||
|
|
||||||
|
@ -459,9 +505,7 @@ class UserConnectionTokenViewSet(
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
token = request.query_params.get('token')
|
token = request.query_params.get('token')
|
||||||
key = self.CACHE_KEY_PREFIX.format(token)
|
value = self.get_token_from_cache(token)
|
||||||
value = cache.get(key, None)
|
|
||||||
|
|
||||||
if not value:
|
if not value:
|
||||||
return Response('', status=404)
|
return Response('', status=404)
|
||||||
return Response(value)
|
return Response(value)
|
||||||
|
|
|
@ -366,7 +366,7 @@ class RoleMixin:
|
||||||
"""
|
"""
|
||||||
由于这里用了 cache ,所以不能改成 self.system_roles.filter().exists() 会查询的
|
由于这里用了 cache ,所以不能改成 self.system_roles.filter().exists() 会查询的
|
||||||
"""
|
"""
|
||||||
if not self._is_superuser:
|
if self._is_superuser is not None:
|
||||||
return self._is_superuser
|
return self._is_superuser
|
||||||
|
|
||||||
from rbac.builtin import BuiltinRole
|
from rbac.builtin import BuiltinRole
|
||||||
|
|
Loading…
Reference in New Issue