feat: 为rdp 添加一个api

This commit is contained in:
ibuler
2021-02-23 14:37:42 +08:00
committed by 老广
parent 9be3cbb936
commit bb9790a50f
12 changed files with 290 additions and 91 deletions

View File

@@ -1,40 +1,73 @@
# -*- coding: utf-8 -*-
#
import uuid
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 rest_framework.response import Response
from rest_framework.generics import CreateAPIView
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 common.utils import get_logger
from common.permissions import IsSuperUserOrAppUser
from orgs.mixins.api import RootOrgViewMixin
from ..serializers import ConnectionTokenSerializer
from ..serializers import ConnectionTokenSerializer, ConnectionTokenSecretSerializer
logger = get_logger(__name__)
__all__ = ['UserConnectionTokenApi']
__all__ = ['UserConnectionTokenViewSet']
class UserConnectionTokenApi(RootOrgViewMixin, CreateAPIView):
class UserConnectionTokenViewSet(RootOrgViewMixin, SerializerMixin2, GenericViewSet):
permission_classes = (IsSuperUserOrAppUser,)
serializer_class = ConnectionTokenSerializer
serializer_classes = {
'default': ConnectionTokenSerializer,
'get_secret_detail': ConnectionTokenSecretSerializer
}
CACHE_KEY_PREFIX = 'CONNECTION_TOKEN_{}'
def perform_create(self, serializer):
user = serializer.validated_data['user']
asset = serializer.validated_data['asset']
system_user = serializer.validated_data['system_user']
token = str(uuid.uuid4())
@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):
self.check_resource_permission(user, asset, application, system_user)
token = random_string(36)
value = {
'user': str(user.id),
'username': user.username,
'asset': str(asset.id),
'hostname': asset.hostname,
'system_user': str(system_user.id),
'system_user_name': system_user.name
}
cache.set(token, value, timeout=20)
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):
@@ -42,18 +75,107 @@ class UserConnectionTokenApi(RootOrgViewMixin, CreateAPIView):
data = {'error': 'Connection token disabled'}
return Response(data, status=400)
if not request.user.is_superuser:
data = {'error': 'Only super user can create token'}
return Response(data, status=403)
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
token = self.perform_create(serializer)
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']
token = self.create_token(user, asset, application, system_user)
return Response({"token": token}, status=201)
@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_users_id_with_actions_by_user
asset = get_object_or_404(Asset, id=value.get('asset'))
systemuserid_actions_mapper = get_asset_system_users_id_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 == "create":
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')
value = cache.get(token, None)
key = self.CACHE_KEY_PREFIX.format(token)
value = cache.get(key, None)
if not value:
return Response('', status=404)

View File

@@ -4,14 +4,17 @@ from rest_framework import serializers
from common.utils import get_object_or_none
from users.models import User
from assets.models import Asset, SystemUser, Gateway
from applications.models import Application
from users.serializers import UserProfileSerializer
from perms.serializers.asset.permission import ActionsField
from .models import AccessKey, LoginConfirmSetting, SSOToken
__all__ = [
'AccessKeySerializer', 'OtpVerifySerializer', 'BearerTokenSerializer',
'MFAChallengeSerializer', 'LoginConfirmSettingSerializer', 'SSOTokenSerializer',
'ConnectionTokenSerializer',
'ConnectionTokenSerializer', 'ConnectionTokenSecretSerializer'
]
@@ -86,9 +89,10 @@ class SSOTokenSerializer(serializers.Serializer):
class ConnectionTokenSerializer(serializers.Serializer):
user = serializers.CharField(max_length=128, required=True)
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=True)
asset = serializers.CharField(max_length=128, required=False)
application = serializers.CharField(max_length=128, required=False)
@staticmethod
def validate_user(user_id):
@@ -113,3 +117,67 @@ class ConnectionTokenSerializer(serializers.Serializer):
if asset is None:
raise serializers.ValidationError('asset id not exist')
return asset
@staticmethod
def validate_application(app_id):
from applications.models import Application
app = Application.objects.filter(id=app_id).first()
if app is None:
raise serializers.ValidationError('app id not exist')
return app
def validate(self, attrs):
asset = attrs.get('asset')
application = attrs.get('application')
if not asset and not application:
raise serializers.ValidationError('asset or application required')
if asset and application:
raise serializers.ValidationError('asset and application should only one')
return super().validate(attrs)
class ConnectionTokenUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'name', 'username', 'email']
class ConnectionTokenAssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ['id', 'hostname', 'ip', 'port', 'org_id']
class ConnectionTokenSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ['id', 'name', 'username', 'password', 'private_key']
class ConnectionTokenGatewaySerializer(serializers.ModelSerializer):
class Meta:
model = Gateway
fields = ['id', 'ip', 'port', 'username', 'password', 'private_key']
class ConnectionTokenRemoteAppSerializer(serializers.Serializer):
program = serializers.CharField()
working_directory = serializers.CharField()
parameters = serializers.CharField()
class ConnectionTokenApplicationSerializer(serializers.ModelSerializer):
class Meta:
model = Application
fields = ['id', 'name', 'category', 'type']
class ConnectionTokenSecretSerializer(serializers.Serializer):
type = serializers.ChoiceField(choices=[('application', 'Application'), ('asset', 'Asset')])
user = ConnectionTokenUserSerializer(read_only=True)
asset = ConnectionTokenAssetSerializer(read_only=True)
remote_app = ConnectionTokenRemoteAppSerializer(read_only=True)
application = ConnectionTokenApplicationSerializer(read_only=True)
system_user = ConnectionTokenSystemUserSerializer(read_only=True)
gateway = ConnectionTokenGatewaySerializer(read_only=True)
actions = ActionsField()

View File

@@ -9,6 +9,7 @@ app_name = 'authentication'
router = DefaultRouter()
router.register('access-keys', api.AccessKeyViewSet, 'access-key')
router.register('sso', api.SSOViewSet, 'sso')
router.register('connection-token', api.UserConnectionTokenViewSet, 'connection-token')
urlpatterns = [
@@ -16,8 +17,6 @@ urlpatterns = [
path('auth/', api.TokenCreateApi.as_view(), name='user-auth'),
path('tokens/', api.TokenCreateApi.as_view(), name='auth-token'),
path('mfa/challenge/', api.MFAChallengeApi.as_view(), name='mfa-challenge'),
path('connection-token/',
api.UserConnectionTokenApi.as_view(), name='connection-token'),
path('otp/verify/', api.UserOtpVerifyApi.as_view(), name='user-otp-verify'),
path('login-confirm-ticket/status/', api.TicketStatusApi.as_view(), name='login-confirm-ticket-status'),
path('login-confirm-settings/<uuid:user_id>/', api.LoginConfirmSettingUpdateApi.as_view(), name='login-confirm-setting-update')