pref: 修改 applet host api

pull/9029/head
ibuler 2022-11-07 20:41:18 +08:00
parent 2705c38ba1
commit afe6c8ebbd
8 changed files with 126 additions and 40 deletions

View File

@ -16,14 +16,13 @@ class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
"""Allows access to valid user, is active and not expired""" """Allows access to valid user, is active and not expired"""
def has_permission(self, request, view): def has_permission(self, request, view):
return super(IsValidUser, self).has_permission(request, view) \ return super().has_permission(request, view) \
and request.user.is_valid and request.user.is_valid
class IsValidUserOrConnectionToken(IsValidUser): class IsValidUserOrConnectionToken(IsValidUser):
def has_permission(self, request, view): def has_permission(self, request, view):
return super(IsValidUserOrConnectionToken, self).has_permission(request, view) \ return super().has_permission(request, view) \
or self.is_valid_connection_token(request) or self.is_valid_connection_token(request)
@staticmethod @staticmethod
@ -42,6 +41,12 @@ class OnlySuperUser(IsValidUser):
and request.user.is_superuser and request.user.is_superuser
class IsServiceAccount(IsValidUser):
def has_permission(self, request, view):
return super().has_permission(request, view) \
and request.user.is_service_account
class WithBootstrapToken(permissions.BasePermission): class WithBootstrapToken(permissions.BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
authorization = request.META.get('HTTP_AUTHORIZATION', '') authorization = request.META.get('HTTP_AUTHORIZATION', '')

View File

@ -1,2 +1,3 @@
from .applet import * from .applet import *
from .host import * from .host import *
from .relation import *

View File

@ -2,10 +2,13 @@ from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from common.permissions import IsServiceAccount
from common.drf.api import JMSModelViewSet from common.drf.api import JMSModelViewSet
from orgs.utils import tmp_to_builtin_org from terminal.serializers import (
from terminal import serializers AppletHostSerializer, AppletHostDeploymentSerializer,
from terminal.models import AppletHost, Applet, AppletHostDeployment AppletHostStartupSerializer
)
from terminal.models import AppletHost, AppletHostDeployment
from terminal.tasks import run_applet_host_deployment from terminal.tasks import run_applet_host_deployment
@ -13,37 +16,25 @@ __all__ = ['AppletHostViewSet', 'AppletHostDeploymentViewSet']
class AppletHostViewSet(JMSModelViewSet): class AppletHostViewSet(JMSModelViewSet):
serializer_class = serializers.AppletHostSerializer serializer_class = AppletHostSerializer
queryset = AppletHost.objects.all() queryset = AppletHost.objects.all()
rbac_perms = {
'accounts': 'terminal.view_applethost',
'reports': '*'
}
@action(methods=['post'], detail=True, serializer_class=serializers.AppletHostReportSerializer) def get_permissions(self):
def reports(self, request, *args, **kwargs): if self.action == 'startup':
# 1. Host 和 Terminal 关联 return [IsServiceAccount()]
# 2. 上报 安装的 Applets 每小时 return super().get_permissions()
@action(methods=['post'], detail=True, serializer_class=AppletHostStartupSerializer)
def startup(self, request, *args, **kwargs):
instance = self.get_object() instance = self.get_object()
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
data = serializer.validated_data
instance.check_terminal_binding(request) instance.check_terminal_binding(request)
instance.check_applets_state(data['applets'])
return Response({'msg': 'ok'}) return Response({'msg': 'ok'})
@action(methods=['get'], detail=True, serializer_class=serializers.AppletHostAccountSerializer)
def accounts(self, request, *args, **kwargs):
host = self.get_object()
with tmp_to_builtin_org(system=1):
accounts = host.accounts.all().filter(privileged=False)
response = self.get_paginated_response_from_queryset(accounts)
return response
class AppletHostDeploymentViewSet(viewsets.ModelViewSet): class AppletHostDeploymentViewSet(viewsets.ModelViewSet):
serializer_class = serializers.AppletHostDeploymentSerializer serializer_class = AppletHostDeploymentSerializer
queryset = AppletHostDeployment.objects.all() queryset = AppletHostDeployment.objects.all()
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):

View File

@ -0,0 +1,77 @@
from typing import Callable
from django.shortcuts import get_object_or_404
from rest_framework.request import Request
from rest_framework.decorators import action
from rest_framework.response import Response
from common.drf.api import JMSModelViewSet
from common.permissions import IsServiceAccount
from orgs.utils import tmp_to_builtin_org
from rbac.permissions import RBACPermission
from terminal.models import AppletHost
from terminal.serializers import (
AppletHostAccountSerializer,
AppletPublicationSerializer,
AppletHostAppletReportSerializer,
)
class HostMixin:
request: Request
permission_denied: Callable
kwargs: dict
rbac_perms = (
('list', 'terminal.view_applethost'),
('retrieve', 'terminal.view_applethost'),
)
def get_permissions(self):
if self.kwargs.get('host'):
return [RBACPermission()]
else:
return [IsServiceAccount()]
def self_host(self):
try:
return self.request.user.terminal.applet_host
except AttributeError:
raise self.permission_denied(self.request, 'User has no applet host')
def pk_host(self):
return get_object_or_404(AppletHost, id=self.kwargs.get('host'))
@property
def host(self):
if self.kwargs.get('host'):
return self.pk_host()
else:
return self.self_host()
class AppletHostAccountsViewSet(HostMixin, JMSModelViewSet):
serializer_class = AppletHostAccountSerializer
def get_queryset(self):
with tmp_to_builtin_org(system=1):
queryset = self.host.accounts.all()
return queryset
class AppletHostAppletViewSet(HostMixin, JMSModelViewSet):
host: AppletHost
serializer_class = AppletPublicationSerializer
def get_queryset(self):
queryset = self.host.publications.all()
return queryset
@action(methods=['post'], detail=False)
def reports(self, request, *args, **kwargs):
serializer = AppletHostAppletReportSerializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
self.host.check_applets_state(data)
publications = self.host.publications.all()
serializer = AppletPublicationSerializer(publications, many=True)
return Response(serializer.data)

View File

@ -34,10 +34,10 @@ class AppletHost(Host):
return self.name return self.name
@property @property
def status(self): def load(self):
if self.terminal: if not self.terminal:
return 'online' return 'offline'
return self.terminal.status return self.terminal.load
def check_terminal_binding(self, request): def check_terminal_binding(self, request):
request_terminal = getattr(request.user, 'terminal', None) request_terminal = getattr(request.user, 'terminal', None)

View File

@ -17,7 +17,7 @@ class AppletPublicationSerializer(serializers.ModelSerializer):
UNPUBLISHED = 'unpublished', _('Unpublished') UNPUBLISHED = 'unpublished', _('Unpublished')
NOT_MATCH = 'not_match', _('Not match') NOT_MATCH = 'not_match', _('Not match')
applet = ObjectRelatedField(attrs=('id', 'display_name', 'icon', 'version'), queryset=Applet.objects.all()) applet = ObjectRelatedField(attrs=('id', 'name', 'display_name', 'icon', 'version'), queryset=Applet.objects.all())
host = ObjectRelatedField(queryset=AppletHost.objects.all()) host = ObjectRelatedField(queryset=AppletHost.objects.all())
status = LabeledChoiceField(choices=Status.choices, label=_("Status")) status = LabeledChoiceField(choices=Status.choices, label=_("Status"))

View File

@ -2,16 +2,18 @@ from rest_framework import serializers
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from common.validators import ProjectUniqueValidator from common.validators import ProjectUniqueValidator
from common.drf.fields import ObjectRelatedField from common.drf.fields import ObjectRelatedField, LabeledChoiceField
from assets.models import Platform, Account from assets.models import Platform, Account
from assets.serializers import HostSerializer from assets.serializers import HostSerializer
from ..models import AppletHost, AppletHostDeployment, Applet from ..models import AppletHost, AppletHostDeployment, Applet
from .applet import AppletSerializer from .applet import AppletSerializer
from .. import const
__all__ = [ __all__ = [
'AppletHostSerializer', 'AppletHostDeploymentSerializer', 'AppletHostSerializer', 'AppletHostDeploymentSerializer',
'AppletHostAccountSerializer', 'AppletHostReportSerializer' 'AppletHostAccountSerializer', 'AppletHostAppletReportSerializer',
'AppletHostStartupSerializer',
] ]
@ -34,14 +36,16 @@ class DeployOptionsSerializer(serializers.Serializer):
class AppletHostSerializer(HostSerializer): class AppletHostSerializer(HostSerializer):
deploy_options = DeployOptionsSerializer(required=False, label=_("Deploy options")) deploy_options = DeployOptionsSerializer(required=False, label=_("Deploy options"))
load = LabeledChoiceField(
read_only=True, label=_('Load status'), choices=const.ComponentLoad.choices,
)
class Meta(HostSerializer.Meta): class Meta(HostSerializer.Meta):
model = AppletHost model = AppletHost
fields = HostSerializer.Meta.fields + [ fields = HostSerializer.Meta.fields + [
'status', 'date_synced', 'deploy_options' 'load', 'date_synced', 'deploy_options'
] ]
extra_kwargs = { extra_kwargs = {
'status': {'read_only': True},
'date_synced': {'read_only': True} 'date_synced': {'read_only': True}
} }
@ -96,5 +100,11 @@ class AppletHostAccountSerializer(serializers.ModelSerializer):
fields = ['id', 'username', 'secret', 'date_updated'] fields = ['id', 'username', 'secret', 'date_updated']
class AppletHostReportSerializer(serializers.Serializer): class AppletHostAppletReportSerializer(serializers.Serializer):
applets = ObjectRelatedField(attrs=('id', 'name', 'version'), queryset=Applet.objects.all(), many=True) id = serializers.UUIDField(read_only=True)
name = serializers.CharField()
version = serializers.CharField()
class AppletHostStartupSerializer(serializers.Serializer):
pass

View File

@ -12,8 +12,8 @@ app_name = 'terminal'
router = BulkRouter() router = BulkRouter()
router.register(r'sessions', api.SessionViewSet, 'session') router.register(r'sessions', api.SessionViewSet, 'session')
router.register(r'terminals/(?P<terminal>[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status') router.register(r'terminals/((?P<terminal>[^/.]{36})/)?status', api.StatusViewSet, 'terminal-status')
router.register(r'terminals/(?P<terminal>[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions') router.register(r'terminals/((?P<terminal>[^/.]{36})/)?sessions', api.SessionViewSet, 'terminal-sessions')
router.register(r'terminals', api.TerminalViewSet, 'terminal') router.register(r'terminals', api.TerminalViewSet, 'terminal')
router.register(r'tasks', api.TaskViewSet, 'tasks') router.register(r'tasks', api.TaskViewSet, 'tasks')
router.register(r'commands', api.CommandViewSet, 'command') router.register(r'commands', api.CommandViewSet, 'command')
@ -25,6 +25,8 @@ router.register(r'session-join-records', api.SessionJoinRecordsViewSet, 'session
router.register(r'endpoints', api.EndpointViewSet, 'endpoint') router.register(r'endpoints', api.EndpointViewSet, 'endpoint')
router.register(r'endpoint-rules', api.EndpointRuleViewSet, 'endpoint-rule') router.register(r'endpoint-rules', api.EndpointRuleViewSet, 'endpoint-rule')
router.register(r'applets', api.AppletViewSet, 'applet') router.register(r'applets', api.AppletViewSet, 'applet')
router.register(r'applet-hosts/((?P<host>[^/.]+)/)?accounts', api.AppletHostAccountsViewSet, 'applet-host-account')
router.register(r'applet-hosts/((?P<host>[^/.]+)/)?applets', api.AppletHostAppletViewSet, 'applet-host-applet')
router.register(r'applet-hosts', api.AppletHostViewSet, 'applet-host') router.register(r'applet-hosts', api.AppletHostViewSet, 'applet-host')
router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication') router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication')
router.register(r'applet-host-deployments', api.AppletHostDeploymentViewSet, 'applet-host-deployment') router.register(r'applet-host-deployments', api.AppletHostDeploymentViewSet, 'applet-host-deployment')