mirror of https://github.com/jumpserver/jumpserver
feat: 添加 command-review API
parent
71e76e5075
commit
c9bf99468c
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -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')
|
||||
)
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
|
@ -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']
|
|
@ -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']
|
Loading…
Reference in New Issue