feat: 添加 command-review API

pull/9167/head
Bai 2022-12-06 18:26:07 +08:00 committed by Jiangjie.Bai
parent 71e76e5075
commit c9bf99468c
12 changed files with 134 additions and 39 deletions

View File

@ -1,3 +1,7 @@
from rest_framework.decorators import action
from rest_framework.response import Response
from common.utils import reverse
from orgs.mixins.api import OrgBulkModelViewSet
from .. import models, serializers
@ -16,3 +20,36 @@ class CommandFilterACLViewSet(OrgBulkModelViewSet):
filterset_fields = ('name',)
search_fields = filterset_fields
serializer_class = serializers.CommandFilterACLSerializer
rbac_perms = {
'command_review': 'tickets.add_superticket'
}
@action(['POST'], detail=False, url_path='command-review')
def command_review(self, request, *args, **kwargs):
serializer = serializers.CommandReviewSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = {
'run_command': serializer.validated_data['run_command'],
'session': serializer.session,
'cmd_filter_acl': serializer.cmd_filter_acl,
'org_id': serializer.org.id
}
ticket = serializer.cmd_filter_acl.create_command_review_ticket(**data)
url_review_status = reverse(
view_name='api-tickets:super-ticket-status', kwargs={'pk': str(ticket.id)}
)
url_ticket_detail = reverse(
view_name='api-tickets:ticket-detail', kwargs={'pk': str(ticket.id)},
external=True, api_to_ui=True
)
resp_data = {
'check_review_status': {'method': 'GET', 'url': url_review_status},
'close_review': {'method': 'DELETE', 'url': url_review_status},
'ticket_detail_url': url_ticket_detail,
'reviewers': [
str(ticket_assignee.assignee)
for ticket_assignee in ticket.current_step.ticket_assignees.all()
]
}
return Response(resp_data)

View File

@ -20,7 +20,7 @@ class LoginAssetCheckAPI(CreateAPIView):
return LoginAssetACL.objects.all()
def create(self, request, *args, **kwargs):
data = self.check_confirm()
data = self.check_review()
return Response(data=data, status=200)
@lazyproperty
@ -29,7 +29,7 @@ class LoginAssetCheckAPI(CreateAPIView):
serializer.is_valid(raise_exception=True)
return serializer
def check_confirm(self):
def check_review(self):
with tmp_to_org(self.serializer.asset.org):
kwargs = {
'user': self.serializer.user,
@ -39,15 +39,15 @@ class LoginAssetCheckAPI(CreateAPIView):
}
acl = LoginAssetACL.filter_queryset(**kwargs).valid().first()
if acl:
need_confirm = True
response_data = self._get_response_data_of_need_confirm(acl)
need_review = True
response_data = self._get_response_data_of_need_review(acl)
else:
need_confirm = False
need_review = False
response_data = {}
response_data['need_confirm'] = need_confirm
response_data['need_review'] = need_review
return response_data
def _get_response_data_of_need_confirm(self, acl) -> dict:
def _get_response_data_of_need_review(self, acl) -> dict:
ticket = LoginAssetACL.create_login_asset_confirm_ticket(
user=self.serializer.user,
asset=self.serializer.asset,
@ -55,7 +55,7 @@ class LoginAssetCheckAPI(CreateAPIView):
assignees=acl.reviewers.all(),
org_id=self.serializer.asset.org.id,
)
confirm_status_url = reverse(
review_status_url = reverse(
view_name='api-tickets:super-ticket-status',
kwargs={'pk': str(ticket.id)}
)
@ -67,8 +67,8 @@ class LoginAssetCheckAPI(CreateAPIView):
ticket_detail_url = '{url}?type={type}'.format(url=ticket_detail_url, type=ticket.type)
ticket_assignees = ticket.current_step.ticket_assignees.all()
data = {
'check_confirm_status': {'method': 'GET', 'url': confirm_status_url},
'close_confirm': {'method': 'DELETE', 'url': confirm_status_url},
'check_review_status': {'method': 'GET', 'url': review_status_url},
'close_review': {'method': 'DELETE', 'url': review_status_url},
'ticket_detail_url': ticket_detail_url,
'reviewers': [str(ticket_assignee.assignee) for ticket_assignee in ticket_assignees],
'ticket_id': str(ticket.id)

View File

@ -104,7 +104,7 @@ class CommandFilterACL(UserAssetAccountBaseACL):
def __str__(self):
return self.name
def create_command_confirm_ticket(self, run_command, session, cmd_filter_rule, org_id):
def create_command_review_ticket(self, run_command, session, cmd_filter_acl, org_id):
from tickets.const import TicketType
from tickets.models import ApplyCommandTicket
data = {
@ -116,8 +116,7 @@ class CommandFilterACL(UserAssetAccountBaseACL):
'apply_run_account': str(session.account),
'apply_run_command': run_command[:4090],
'apply_from_session_id': str(session.id),
'apply_from_cmd_filter_rule_id': str(cmd_filter_rule.id),
'apply_from_cmd_filter_id': str(cmd_filter_rule.filter.id),
'apply_from_cmd_filter_acl_id': str(cmd_filter_acl.id),
'org_id': org_id,
}
ticket = ApplyCommandTicket.objects.create(**data)

View File

@ -1,11 +1,16 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from terminal.models import Session
from acls.models import CommandGroup, CommandFilterACL
from common.utils import lazyproperty, get_object_or_none
from common.drf.fields import ObjectRelatedField, LabeledChoiceField
from orgs.utils import tmp_to_root_org
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import BaseUserAssetAccountACLSerializerMixin as BaseSerializer
__all__ = ["CommandFilterACLSerializer", "CommandGroupSerializer"]
__all__ = ["CommandFilterACLSerializer", "CommandGroupSerializer", "CommandReviewSerializer"]
class CommandGroupSerializer(BulkOrgResourceModelSerializer):
@ -27,3 +32,35 @@ class CommandFilterACLSerializer(BaseSerializer, BulkOrgResourceModelSerializer)
class Meta(BaseSerializer.Meta):
model = CommandFilterACL
fields = BaseSerializer.Meta.fields + ['command_groups']
class CommandReviewSerializer(serializers.Serializer):
session_id = serializers.UUIDField(required=True, allow_null=False)
cmd_filter_acl_id = serializers.UUIDField(required=True, allow_null=False)
run_command = serializers.CharField(required=True, allow_null=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.session = None
self.cmd_filter_acl = None
def validate_session_id(self, pk):
self.session = self.validate_object(Session, pk)
return pk
def validate_cmd_filter_acl_id(self, pk):
self.cmd_filter_acl = self.validate_object(CommandFilterACL, pk)
return pk
@lazyproperty
def org(self):
return self.session.org
@staticmethod
def validate_object(model, pk):
with tmp_to_root_org():
obj = get_object_or_none(model, id=pk)
if obj:
return obj
error = '{} Model object does not exist'.format(model.__name__)
raise serializers.ValidationError(error)

View File

@ -8,8 +8,8 @@ app_name = 'acls'
router = BulkRouter()
router.register(r'login-acls', api.LoginACLViewSet, 'login-acl')
router.register(r'login-asset-acls', api.LoginAssetACLViewSet, 'login-asset-acl')
router.register(r'command-groups', api.CommandGroupViewSet, 'command-group')
router.register(r'command-filter-acls', api.CommandFilterACLViewSet, 'command-filter-acl')
router.register(r'command-groups', api.CommandGroupViewSet, 'command-group')
urlpatterns = [
path('login-asset/check/', api.LoginAssetCheckAPI.as_view(), name='login-asset-check'),

View File

@ -104,16 +104,16 @@ class ApplyAssetTicketViewSet(TicketViewSet):
class ApplyLoginTicketViewSet(TicketViewSet):
model = ApplyLoginTicket
filterset_class = filters.ApplyLoginTicketFilter
serializer_class = serializers.LoginConfirmSerializer
serializer_class = serializers.LoginReviewSerializer
class ApplyLoginAssetTicketViewSet(TicketViewSet):
model = ApplyLoginAssetTicket
filterset_class = filters.ApplyLoginAssetTicketFilter
serializer_class = serializers.LoginAssetConfirmSerializer
serializer_class = serializers.LoginAssetReviewSerializer
class ApplyCommandTicketViewSet(TicketViewSet):
model = ApplyCommandTicket
filterset_class = filters.ApplyCommandTicketFilter
serializer_class = serializers.ApplyCommandConfirmSerializer
serializer_class = serializers.ApplyCommandReviewSerializer

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.14 on 2022-12-06 10:20
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('acls', '0010_auto_20221205_1122'),
('tickets', '0024_auto_20221121_1800'),
]
operations = [
migrations.RemoveField(
model_name='applycommandticket',
name='apply_from_cmd_filter',
),
migrations.RemoveField(
model_name='applycommandticket',
name='apply_from_cmd_filter_rule',
),
migrations.AddField(
model_name='applycommandticket',
name='apply_from_cmd_filter_acl',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='acls.commandfilteracl', verbose_name='Command filter acl'),
),
]

View File

@ -6,21 +6,15 @@ from .general import Ticket
class ApplyCommandTicket(Ticket):
apply_run_user = models.ForeignKey(
'users.User', on_delete=models.SET_NULL,
null=True, verbose_name=_('Run user')
'users.User', on_delete=models.SET_NULL, null=True, verbose_name=_('Run user')
)
apply_run_asset = models.CharField(max_length=128, verbose_name=_('Run asset'))
apply_run_command = models.CharField(max_length=4096, verbose_name=_('Run command'))
apply_run_account = models.CharField(max_length=128, default='', verbose_name=_('Run account'))
apply_from_session = models.ForeignKey(
'terminal.Session', on_delete=models.SET_NULL,
null=True, verbose_name=_("Session")
'terminal.Session', on_delete=models.SET_NULL, null=True, verbose_name=_("Session")
)
apply_from_cmd_filter = models.ForeignKey(
'assets.CommandFilter', on_delete=models.SET_NULL,
null=True, verbose_name=_('From cmd filter')
)
apply_from_cmd_filter_rule = models.ForeignKey(
'assets.CommandFilterRule', on_delete=models.SET_NULL,
null=True, verbose_name=_('From cmd filter rule')
apply_from_cmd_filter_acl = models.ForeignKey(
'acls.CommandFilterACL', on_delete=models.SET_NULL,
null=True, verbose_name=_('Command filter acl')
)

View File

@ -1,6 +1,6 @@
from .common import *
from .ticket import *
from .apply_asset import *
from .login_confirm import *
from .command_confirm import *
from .login_asset_confirm import *
from .login_review import *
from .command_review import *
from .login_asset_review import *

View File

@ -2,15 +2,15 @@ from tickets.models import ApplyCommandTicket
from .ticket import TicketApplySerializer
__all__ = [
'ApplyCommandConfirmSerializer',
'ApplyCommandReviewSerializer',
]
class ApplyCommandConfirmSerializer(TicketApplySerializer):
class ApplyCommandReviewSerializer(TicketApplySerializer):
class Meta:
model = ApplyCommandTicket
writeable_fields = [
'apply_run_user', 'apply_run_asset', 'apply_run_account', 'apply_run_command',
'apply_from_session', 'apply_from_cmd_filter', 'apply_from_cmd_filter_rule'
'apply_from_session', 'apply_from_cmd_filter_acl'
]
fields = TicketApplySerializer.Meta.fields + writeable_fields

View File

@ -2,11 +2,11 @@ from tickets.models import ApplyLoginAssetTicket
from .ticket import TicketApplySerializer
__all__ = [
'LoginAssetConfirmSerializer'
'LoginAssetReviewSerializer'
]
class LoginAssetConfirmSerializer(TicketApplySerializer):
class LoginAssetReviewSerializer(TicketApplySerializer):
class Meta:
model = ApplyLoginAssetTicket
writeable_fields = ['apply_login_user', 'apply_login_asset', 'apply_login_account']

View File

@ -2,11 +2,11 @@ from tickets.models import ApplyLoginTicket
from .ticket import TicketApplySerializer
__all__ = [
'LoginConfirmSerializer'
'LoginReviewSerializer'
]
class LoginConfirmSerializer(TicketApplySerializer):
class LoginReviewSerializer(TicketApplySerializer):
class Meta(TicketApplySerializer.Meta):
model = ApplyLoginTicket
writeable_fields = ['apply_login_ip', 'apply_login_city', 'apply_login_datetime']