mirror of https://github.com/jumpserver/jumpserver
chore(merge): 合并ddev
commit
c52431b5ce
|
@ -5,7 +5,7 @@ from rest_framework.generics import CreateAPIView, RetrieveDestroyAPIView
|
|||
from common.permissions import IsAppUser
|
||||
from common.utils import reverse, lazyproperty
|
||||
from orgs.utils import tmp_to_org, tmp_to_root_org
|
||||
from tickets.models import Ticket
|
||||
from tickets.api import GenericTicketStatusRetrieveCloseAPI
|
||||
from ..models import LoginAssetACL
|
||||
from .. import serializers
|
||||
|
||||
|
@ -48,7 +48,7 @@ class LoginAssetCheckAPI(CreateAPIView):
|
|||
org_id=self.serializer.org.id
|
||||
)
|
||||
confirm_status_url = reverse(
|
||||
view_name='acls:login-asset-confirm-status',
|
||||
view_name='api-acls:login-asset-confirm-status',
|
||||
kwargs={'pk': str(ticket.id)}
|
||||
)
|
||||
ticket_detail_url = reverse(
|
||||
|
@ -72,34 +72,6 @@ class LoginAssetCheckAPI(CreateAPIView):
|
|||
return serializer
|
||||
|
||||
|
||||
class LoginAssetConfirmStatusAPI(RetrieveDestroyAPIView):
|
||||
permission_classes = (IsAppUser, )
|
||||
class LoginAssetConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
|
||||
pass
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
if self.ticket.action_open:
|
||||
status = 'await'
|
||||
elif self.ticket.action_approve:
|
||||
status = 'approve'
|
||||
else:
|
||||
status = 'reject'
|
||||
data = {
|
||||
'status': status,
|
||||
'action': self.ticket.action,
|
||||
'processor': self.ticket.processor_display
|
||||
}
|
||||
return Response(data=data, status=200)
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
if self.ticket.status_open:
|
||||
self.ticket.close(processor=self.ticket.applicant)
|
||||
data = {
|
||||
'action': self.ticket.action,
|
||||
'status': self.ticket.status,
|
||||
'processor': self.ticket.processor_display
|
||||
}
|
||||
return Response(data=data, status=200)
|
||||
|
||||
@lazyproperty
|
||||
def ticket(self):
|
||||
with tmp_to_root_org():
|
||||
return get_object_or_404(Ticket, pk=self.kwargs['pk'])
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.generics import CreateAPIView, RetrieveDestroyAPIView
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from common.utils import reverse
|
||||
from common.utils import lazyproperty
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from ..hands import IsOrgAdmin
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from tickets.models import Ticket
|
||||
from tickets.api import GenericTicketStatusRetrieveCloseAPI
|
||||
from ..hands import IsOrgAdmin, IsAppUser
|
||||
from ..models import CommandFilter, CommandFilterRule
|
||||
from .. import serializers
|
||||
|
||||
|
||||
__all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
||||
__all__ = [
|
||||
'CommandFilterViewSet', 'CommandFilterRuleViewSet', 'CommandConfirmAPI',
|
||||
'CommandConfirmStatusAPI'
|
||||
]
|
||||
|
||||
|
||||
class CommandFilterViewSet(OrgBulkModelViewSet):
|
||||
|
@ -35,3 +45,50 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
|||
return cmd_filter.rules.all()
|
||||
|
||||
|
||||
class CommandConfirmAPI(CreateAPIView):
|
||||
permission_classes = (IsAppUser, )
|
||||
serializer_class = serializers.CommandConfirmSerializer
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
ticket = self.create_command_confirm_ticket()
|
||||
response_data = self.get_response_data(ticket)
|
||||
return Response(data=response_data, status=200)
|
||||
|
||||
def create_command_confirm_ticket(self):
|
||||
ticket = self.serializer.cmd_filter_rule.create_command_confirm_ticket(
|
||||
run_command=self.serializer.data.get('run_command'),
|
||||
session=self.serializer.session,
|
||||
cmd_filter_rule=self.serializer.cmd_filter_rule,
|
||||
org_id=self.serializer.org.id
|
||||
)
|
||||
return ticket
|
||||
|
||||
@staticmethod
|
||||
def get_response_data(ticket):
|
||||
confirm_status_url = reverse(
|
||||
view_name='api-assets:command-confirm-status',
|
||||
kwargs={'pk': str(ticket.id)}
|
||||
)
|
||||
ticket_detail_url = reverse(
|
||||
view_name='api-tickets:ticket-detail',
|
||||
kwargs={'pk': str(ticket.id)},
|
||||
external=True, api_to_ui=True
|
||||
)
|
||||
ticket_detail_url = '{url}?type={type}'.format(url=ticket_detail_url, type=ticket.type)
|
||||
return {
|
||||
'check_confirm_status': {'method': 'GET', 'url': confirm_status_url},
|
||||
'close_confirm': {'method': 'DELETE', 'url': confirm_status_url},
|
||||
'ticket_detail_url': ticket_detail_url,
|
||||
'reviewers': [str(user) for user in ticket.assignees.all()]
|
||||
}
|
||||
|
||||
@lazyproperty
|
||||
def serializer(self):
|
||||
serializer = self.get_serializer(data=self.request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
return serializer
|
||||
|
||||
|
||||
class CommandConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
|
||||
pass
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# Generated by Django 3.1 on 2021-04-26 07:15
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('assets', '0069_change_node_key0_to_key1'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='commandfilterrule',
|
||||
name='reviewers',
|
||||
field=models.ManyToManyField(blank=True, related_name='review_cmd_filter_rules', to=settings.AUTH_USER_MODEL, verbose_name='Reviewers'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='commandfilterrule',
|
||||
name='action',
|
||||
field=models.IntegerField(choices=[(0, 'Deny'), (1, 'Allow'), (2, 'Reconfirm')], default=0, verbose_name='Action'),
|
||||
),
|
||||
]
|
|
@ -41,11 +41,12 @@ class CommandFilterRule(OrgModelMixin):
|
|||
(TYPE_COMMAND, _('Command')),
|
||||
)
|
||||
|
||||
ACTION_DENY, ACTION_ALLOW, ACTION_UNKNOWN = range(3)
|
||||
ACTION_CHOICES = (
|
||||
(ACTION_DENY, _('Deny')),
|
||||
(ACTION_ALLOW, _('Allow')),
|
||||
)
|
||||
ACTION_UNKNOWN = 10
|
||||
|
||||
class ActionChoices(models.IntegerChoices):
|
||||
deny = 0, _('Deny')
|
||||
allow = 1, _('Allow')
|
||||
confirm = 2, _('Reconfirm')
|
||||
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules')
|
||||
|
@ -53,7 +54,13 @@ class CommandFilterRule(OrgModelMixin):
|
|||
priority = models.IntegerField(default=50, verbose_name=_("Priority"), help_text=_("1-100, the lower the value will be match first"),
|
||||
validators=[MinValueValidator(1), MaxValueValidator(100)])
|
||||
content = models.TextField(verbose_name=_("Content"), help_text=_("One line one command"))
|
||||
action = models.IntegerField(default=ACTION_DENY, choices=ACTION_CHOICES, verbose_name=_("Action"))
|
||||
action = models.IntegerField(default=ActionChoices.deny, choices=ActionChoices.choices, verbose_name=_("Action"))
|
||||
# 动作: 附加字段
|
||||
# - confirm: 命令复核人
|
||||
reviewers = models.ManyToManyField(
|
||||
'users.User', related_name='review_cmd_filter_rules', blank=True,
|
||||
verbose_name=_("Reviewers")
|
||||
)
|
||||
comment = models.CharField(max_length=64, blank=True, default='', verbose_name=_("Comment"))
|
||||
date_created = models.DateTimeField(auto_now_add=True)
|
||||
date_updated = models.DateTimeField(auto_now=True)
|
||||
|
@ -89,10 +96,32 @@ class CommandFilterRule(OrgModelMixin):
|
|||
if not found:
|
||||
return self.ACTION_UNKNOWN, ''
|
||||
|
||||
if self.action == self.ACTION_ALLOW:
|
||||
return self.ACTION_ALLOW, found.group()
|
||||
if self.action == self.ActionChoices.allow:
|
||||
return self.ActionChoices.allow, found.group()
|
||||
else:
|
||||
return self.ACTION_DENY, found.group()
|
||||
return self.ActionChoices.deny, found.group()
|
||||
|
||||
def __str__(self):
|
||||
return '{} % {}'.format(self.type, self.content)
|
||||
|
||||
def create_command_confirm_ticket(self, run_command, session, cmd_filter_rule, org_id):
|
||||
from tickets.const import TicketTypeChoices
|
||||
from tickets.models import Ticket
|
||||
data = {
|
||||
'title': _('Command confirm') + ' ({})'.format(session.user),
|
||||
'type': TicketTypeChoices.command_confirm,
|
||||
'meta': {
|
||||
'apply_run_user': session.user,
|
||||
'apply_run_asset': session.asset,
|
||||
'apply_run_system_user': session.system_user,
|
||||
'apply_run_command': run_command,
|
||||
'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)
|
||||
},
|
||||
'org_id': org_id,
|
||||
}
|
||||
ticket = Ticket.objects.create(**data)
|
||||
ticket.assignees.set(self.reviewers.all())
|
||||
ticket.open(applicant=session.user_obj)
|
||||
return ticket
|
||||
|
|
|
@ -196,9 +196,9 @@ class SystemUser(BaseUser):
|
|||
def is_command_can_run(self, command):
|
||||
for rule in self.cmd_filter_rules:
|
||||
action, matched_cmd = rule.match(command)
|
||||
if action == rule.ACTION_ALLOW:
|
||||
if action == rule.ActionChoices.allow:
|
||||
return True, None
|
||||
elif action == rule.ACTION_DENY:
|
||||
elif action == rule.ActionChoices.deny:
|
||||
return False, matched_cmd
|
||||
return True, None
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ from rest_framework import serializers
|
|||
from common.drf.serializers import AdaptedBulkListSerializer
|
||||
from ..models import CommandFilter, CommandFilterRule, SystemUser
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from common.utils import get_object_or_none, lazyproperty
|
||||
from terminal.models import Session
|
||||
|
||||
|
||||
class CommandFilterSerializer(BulkOrgResourceModelSerializer):
|
||||
|
@ -34,7 +37,7 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
|
|||
fields_mini = ['id']
|
||||
fields_small = fields_mini + [
|
||||
'type', 'type_display', 'content', 'priority',
|
||||
'action', 'action_display',
|
||||
'action', 'action_display', 'reviewers',
|
||||
'comment', 'created_by', 'date_created', 'date_updated'
|
||||
]
|
||||
fields_fk = ['filter']
|
||||
|
@ -50,3 +53,35 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
|
|||
# msg = _("Content should not be contain: {}").format(invalid_char)
|
||||
# raise serializers.ValidationError(msg)
|
||||
# return content
|
||||
|
||||
|
||||
class CommandConfirmSerializer(serializers.Serializer):
|
||||
session_id = serializers.UUIDField(required=True, allow_null=False)
|
||||
cmd_filter_rule_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_rule = None
|
||||
|
||||
def validate_session_id(self, session_id):
|
||||
self.session = self.validate_object_exist(Session, session_id)
|
||||
return session_id
|
||||
|
||||
def validate_cmd_filter_rule_id(self, cmd_filter_rule_id):
|
||||
self.cmd_filter_rule = self.validate_object_exist(CommandFilterRule, cmd_filter_rule_id)
|
||||
return cmd_filter_rule_id
|
||||
|
||||
@staticmethod
|
||||
def validate_object_exist(model, field_id):
|
||||
with tmp_to_root_org():
|
||||
obj = get_object_or_none(model, id=field_id)
|
||||
if not obj:
|
||||
error = '{} Model object does not exist'.format(model.__name__)
|
||||
raise serializers.ValidationError(error)
|
||||
return obj
|
||||
|
||||
@lazyproperty
|
||||
def org(self):
|
||||
return self.session.org
|
||||
|
|
|
@ -63,6 +63,9 @@ urlpatterns = [
|
|||
|
||||
path('gateways/<uuid:pk>/test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
||||
|
||||
path('cmd-filters/command-confirm/', api.CommandConfirmAPI.as_view(), name='command-confirm'),
|
||||
path('cmd-filters/command-confirm/<uuid:pk>/status/', api.CommandConfirmStatusAPI.as_view(), name='command-confirm-status')
|
||||
|
||||
]
|
||||
|
||||
old_version_urlpatterns = [
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from assets.signals_handler.node_assets_mapping import expire_node_assets_mapping_for_memory
|
||||
from orgs.models import Organization
|
||||
|
||||
|
||||
def expire_node_assets_mapping():
|
||||
org_ids = Organization.objects.all().values_list('id', flat=True)
|
||||
org_ids = [*org_ids, '00000000-0000-0000-0000-000000000000']
|
||||
|
||||
for org_id in org_ids:
|
||||
expire_node_assets_mapping_for_memory(org_id)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Expire caches'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
expire_node_assets_mapping()
|
|
@ -99,7 +99,7 @@ class DatesLoginMetricMixin:
|
|||
if count is not None:
|
||||
return count
|
||||
ds, de = self.get_date_start_2_end(date)
|
||||
count = len(set(Session.objects.filter(date_start__range=(ds, de)).values_list('user', flat=True)))
|
||||
count = len(set(Session.objects.filter(date_start__range=(ds, de)).values_list('user_id', flat=True)))
|
||||
self.__set_data_to_cache(date, tp, count)
|
||||
return count
|
||||
|
||||
|
@ -129,7 +129,7 @@ class DatesLoginMetricMixin:
|
|||
|
||||
@lazyproperty
|
||||
def dates_total_count_active_users(self):
|
||||
count = len(set(self.sessions_queryset.values_list('user', flat=True)))
|
||||
count = len(set(self.sessions_queryset.values_list('user_id', flat=True)))
|
||||
return count
|
||||
|
||||
@lazyproperty
|
||||
|
@ -161,10 +161,10 @@ class DatesLoginMetricMixin:
|
|||
@lazyproperty
|
||||
def dates_total_count_disabled_assets(self):
|
||||
return Asset.objects.filter(is_active=False).count()
|
||||
|
||||
|
||||
# 以下是从week中而来
|
||||
def get_dates_login_times_top5_users(self):
|
||||
users = self.sessions_queryset.values_list('user', flat=True)
|
||||
users = self.sessions_queryset.values_list('user_id', flat=True)
|
||||
users = [
|
||||
{'user': user, 'total': total}
|
||||
for user, total in Counter(users).most_common(5)
|
||||
|
@ -172,7 +172,7 @@ class DatesLoginMetricMixin:
|
|||
return users
|
||||
|
||||
def get_dates_total_count_login_users(self):
|
||||
return len(set(self.sessions_queryset.values_list('user', flat=True)))
|
||||
return len(set(self.sessions_queryset.values_list('user_id', flat=True)))
|
||||
|
||||
def get_dates_total_count_login_times(self):
|
||||
return self.sessions_queryset.count()
|
||||
|
@ -186,8 +186,8 @@ class DatesLoginMetricMixin:
|
|||
return list(assets)
|
||||
|
||||
def get_dates_login_times_top10_users(self):
|
||||
users = self.sessions_queryset.values("user") \
|
||||
.annotate(total=Count("user")) \
|
||||
users = self.sessions_queryset.values("user_id") \
|
||||
.annotate(total=Count("user_id")) \
|
||||
.annotate(last=Max("date_start")).order_by("-total")[:10]
|
||||
for user in users:
|
||||
user['last'] = str(user['last'])
|
||||
|
@ -221,7 +221,7 @@ class TotalCountMixin:
|
|||
|
||||
@staticmethod
|
||||
def get_total_count_online_users():
|
||||
count = len(set(Session.objects.filter(is_finished=False).values_list('user', flat=True)))
|
||||
count = len(set(Session.objects.filter(is_finished=False).values_list('user_id', flat=True)))
|
||||
return count
|
||||
|
||||
@staticmethod
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-04-26 10:14+0800\n"
|
||||
"POT-Creation-Date: 2021-04-27 18:00+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -34,12 +34,12 @@ msgstr ""
|
|||
msgid "Name"
|
||||
msgstr "名称"
|
||||
|
||||
#: acls/models/base.py:27 assets/models/cmd_filter.py:53
|
||||
#: acls/models/base.py:27 assets/models/cmd_filter.py:54
|
||||
#: assets/models/user.py:122
|
||||
msgid "Priority"
|
||||
msgstr "优先级"
|
||||
|
||||
#: acls/models/base.py:28 assets/models/cmd_filter.py:53
|
||||
#: acls/models/base.py:28 assets/models/cmd_filter.py:54
|
||||
#: assets/models/user.py:122
|
||||
msgid "1-100, the lower the value will be match first"
|
||||
msgstr "优先级可选范围为 1-100 (数值越小越优先)"
|
||||
|
@ -55,7 +55,7 @@ msgstr "激活中"
|
|||
#: acls/models/base.py:32 applications/models/application.py:24
|
||||
#: assets/models/asset.py:147 assets/models/asset.py:223
|
||||
#: assets/models/base.py:255 assets/models/cluster.py:29
|
||||
#: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:57
|
||||
#: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:64
|
||||
#: assets/models/domain.py:22 assets/models/domain.py:56
|
||||
#: assets/models/group.py:23 assets/models/label.py:23 ops/models/adhoc.py:37
|
||||
#: orgs/models.py:26 perms/models/base.py:57 settings/models.py:34
|
||||
|
@ -67,11 +67,11 @@ msgstr "激活中"
|
|||
msgid "Comment"
|
||||
msgstr "备注"
|
||||
|
||||
#: acls/models/login_acl.py:16 tickets/const.py:18
|
||||
#: acls/models/login_acl.py:16 tickets/const.py:19
|
||||
msgid "Reject"
|
||||
msgstr "拒绝"
|
||||
|
||||
#: acls/models/login_acl.py:17 assets/models/cmd_filter.py:47
|
||||
#: acls/models/login_acl.py:17 assets/models/cmd_filter.py:48
|
||||
msgid "Allow"
|
||||
msgstr "允许"
|
||||
|
||||
|
@ -81,7 +81,7 @@ msgstr "登录IP"
|
|||
|
||||
#: acls/models/login_acl.py:24 acls/models/login_asset_acl.py:26
|
||||
#: acls/serializers/login_acl.py:34 acls/serializers/login_asset_acl.py:75
|
||||
#: assets/models/cmd_filter.py:56 audits/models.py:57
|
||||
#: assets/models/cmd_filter.py:57 audits/models.py:57
|
||||
#: authentication/templates/authentication/_access_key_modal.html:34
|
||||
#: tickets/models/ticket.py:43 users/templates/users/_granted_assets.html:29
|
||||
#: users/templates/users/user_asset_permission.html:44
|
||||
|
@ -97,7 +97,7 @@ msgstr "动作"
|
|||
#: authentication/models.py:97 orgs/models.py:18 orgs/models.py:418
|
||||
#: perms/models/base.py:50 templates/index.html:78
|
||||
#: terminal/backends/command/models.py:18
|
||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:37
|
||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:38
|
||||
#: tickets/models/comment.py:17 users/models/user.py:164
|
||||
#: users/models/user.py:712 users/serializers/group.py:20
|
||||
#: users/templates/users/user_asset_permission.html:38
|
||||
|
@ -124,7 +124,7 @@ msgstr "系统用户"
|
|||
#: assets/serializers/system_user.py:192 audits/models.py:38
|
||||
#: perms/models/asset_permission.py:99 templates/index.html:82
|
||||
#: terminal/backends/command/models.py:19
|
||||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:39
|
||||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:40
|
||||
#: users/templates/users/user_asset_permission.html:40
|
||||
#: users/templates/users/user_asset_permission.html:70
|
||||
#: xpack/plugins/change_auth_plan/models.py:282
|
||||
|
@ -132,7 +132,8 @@ msgstr "系统用户"
|
|||
msgid "Asset"
|
||||
msgstr "资产"
|
||||
|
||||
#: acls/models/login_asset_acl.py:32 authentication/models.py:45
|
||||
#: acls/models/login_asset_acl.py:32 assets/models/cmd_filter.py:62
|
||||
#: authentication/models.py:45
|
||||
msgid "Reviewers"
|
||||
msgstr "审批人"
|
||||
|
||||
|
@ -250,7 +251,7 @@ msgstr "自定义"
|
|||
msgid "Category"
|
||||
msgstr "类别"
|
||||
|
||||
#: applications/models/application.py:16 assets/models/cmd_filter.py:52
|
||||
#: applications/models/application.py:16 assets/models/cmd_filter.py:53
|
||||
#: perms/models/application_permission.py:23
|
||||
#: perms/serializers/application/permission.py:17
|
||||
#: perms/serializers/application/user_permission.py:34
|
||||
|
@ -320,7 +321,6 @@ msgstr "目标URL"
|
|||
#: users/templates/users/user_otp_check_password.html:13
|
||||
#: users/templates/users/user_password_update.html:43
|
||||
#: users/templates/users/user_password_verify.html:18
|
||||
#: users/templates/users/user_update.html:20
|
||||
#: xpack/plugins/change_auth_plan/models.py:68
|
||||
#: xpack/plugins/change_auth_plan/models.py:190
|
||||
#: xpack/plugins/change_auth_plan/models.py:285
|
||||
|
@ -480,7 +480,7 @@ msgstr "标签管理"
|
|||
|
||||
#: assets/models/asset.py:221 assets/models/base.py:258
|
||||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
|
||||
#: assets/models/cmd_filter.py:60 assets/models/group.py:21
|
||||
#: assets/models/cmd_filter.py:67 assets/models/group.py:21
|
||||
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:24
|
||||
#: orgs/models.py:422 perms/models/base.py:55 users/models/user.py:576
|
||||
#: users/serializers/group.py:35 xpack/plugins/change_auth_plan/models.py:81
|
||||
|
@ -585,30 +585,38 @@ msgid "Regex"
|
|||
msgstr "正则表达式"
|
||||
|
||||
#: assets/models/cmd_filter.py:41 ops/models/command.py:25
|
||||
#: terminal/backends/command/serializers.py:15 terminal/models/session.py:48
|
||||
#: terminal/backends/command/serializers.py:15 terminal/models/session.py:49
|
||||
msgid "Command"
|
||||
msgstr "命令"
|
||||
|
||||
#: assets/models/cmd_filter.py:46
|
||||
#: assets/models/cmd_filter.py:47
|
||||
msgid "Deny"
|
||||
msgstr "拒绝"
|
||||
|
||||
#: assets/models/cmd_filter.py:51
|
||||
#: assets/models/cmd_filter.py:49
|
||||
msgid "Reconfirm"
|
||||
msgstr "复核"
|
||||
|
||||
#: assets/models/cmd_filter.py:52
|
||||
msgid "Filter"
|
||||
msgstr "过滤器"
|
||||
|
||||
#: assets/models/cmd_filter.py:55 xpack/plugins/license/models.py:29
|
||||
#: assets/models/cmd_filter.py:56 xpack/plugins/license/models.py:29
|
||||
msgid "Content"
|
||||
msgstr "内容"
|
||||
|
||||
#: assets/models/cmd_filter.py:55
|
||||
#: assets/models/cmd_filter.py:56
|
||||
msgid "One line one command"
|
||||
msgstr "每行一个命令"
|
||||
|
||||
#: assets/models/cmd_filter.py:64
|
||||
#: assets/models/cmd_filter.py:71
|
||||
msgid "Command filter rule"
|
||||
msgstr "命令过滤规则"
|
||||
|
||||
#: assets/models/cmd_filter.py:111 tickets/const.py:13
|
||||
msgid "Command confirm"
|
||||
msgstr "命令复核"
|
||||
|
||||
#: assets/models/domain.py:64
|
||||
msgid "Gateway"
|
||||
msgstr "网关"
|
||||
|
@ -735,7 +743,7 @@ msgstr "用户组"
|
|||
#: perms/models/application_permission.py:31
|
||||
#: perms/models/asset_permission.py:101 templates/_nav.html:45
|
||||
#: terminal/backends/command/models.py:20
|
||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:41
|
||||
#: terminal/backends/command/serializers.py:14 terminal/models/session.py:42
|
||||
#: users/templates/users/_granted_assets.html:27
|
||||
#: users/templates/users/user_asset_permission.html:42
|
||||
#: users/templates/users/user_asset_permission.html:76
|
||||
|
@ -1025,7 +1033,7 @@ msgid "Symlink"
|
|||
msgstr "建立软链接"
|
||||
|
||||
#: audits/models.py:37 audits/models.py:60 audits/models.py:71
|
||||
#: terminal/models/session.py:44
|
||||
#: terminal/models/session.py:45
|
||||
msgid "Remote addr"
|
||||
msgstr "远端地址"
|
||||
|
||||
|
@ -1042,7 +1050,7 @@ msgid "Success"
|
|||
msgstr "成功"
|
||||
|
||||
#: audits/models.py:43 ops/models/command.py:30 perms/models/base.py:53
|
||||
#: terminal/models/session.py:51
|
||||
#: terminal/models/session.py:52
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:43
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:74
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:40
|
||||
|
@ -1418,7 +1426,7 @@ msgstr "删除成功"
|
|||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:155
|
||||
#: authentication/templates/authentication/_mfa_confirm_modal.html:53
|
||||
#: templates/_modal.html:22 tickets/const.py:19
|
||||
#: templates/_modal.html:22 tickets/const.py:20
|
||||
msgid "Close"
|
||||
msgstr "关闭"
|
||||
|
||||
|
@ -2944,13 +2952,12 @@ msgstr "较高"
|
|||
|
||||
#: terminal/const.py:33 users/templates/users/reset_password.html:50
|
||||
#: users/templates/users/user_password_update.html:104
|
||||
#: users/templates/users/user_update.html:57
|
||||
msgid "Normal"
|
||||
msgstr "正常"
|
||||
|
||||
#: terminal/const.py:34
|
||||
msgid "Offline"
|
||||
msgstr ""
|
||||
msgstr "离线"
|
||||
|
||||
#: terminal/exceptions.py:8
|
||||
msgid "Bulk create not support"
|
||||
|
@ -2960,15 +2967,15 @@ msgstr "不支持批量创建"
|
|||
msgid "Storage is invalid"
|
||||
msgstr "存储无效"
|
||||
|
||||
#: terminal/models/session.py:43
|
||||
#: terminal/models/session.py:44
|
||||
msgid "Login from"
|
||||
msgstr "登录来源"
|
||||
|
||||
#: terminal/models/session.py:47
|
||||
#: terminal/models/session.py:48
|
||||
msgid "Replay"
|
||||
msgstr "回放"
|
||||
|
||||
#: terminal/models/session.py:52
|
||||
#: terminal/models/session.py:53
|
||||
msgid "Date end"
|
||||
msgstr "结束日期"
|
||||
|
||||
|
@ -3115,7 +3122,7 @@ msgstr "文档类型"
|
|||
|
||||
#: terminal/serializers/storage.py:185
|
||||
msgid "Ignore Certificate Verification"
|
||||
msgstr ""
|
||||
msgstr "忽略证书认证"
|
||||
|
||||
#: terminal/serializers/terminal.py:66 terminal/serializers/terminal.py:74
|
||||
msgid "Not found"
|
||||
|
@ -3205,15 +3212,15 @@ msgstr "申请资产"
|
|||
msgid "Apply for application"
|
||||
msgstr "申请应用"
|
||||
|
||||
#: tickets/const.py:16 tickets/const.py:23
|
||||
#: tickets/const.py:17 tickets/const.py:24
|
||||
msgid "Open"
|
||||
msgstr "打开"
|
||||
|
||||
#: tickets/const.py:17
|
||||
#: tickets/const.py:18
|
||||
msgid "Approve"
|
||||
msgstr "同意"
|
||||
|
||||
#: tickets/const.py:24
|
||||
#: tickets/const.py:25
|
||||
msgid "Closed"
|
||||
msgstr "关闭"
|
||||
|
||||
|
@ -3328,6 +3335,36 @@ msgstr "工单申请信息"
|
|||
msgid "Ticket approved info"
|
||||
msgstr "工单批准信息"
|
||||
|
||||
#: tickets/handler/command_confirm.py:24
|
||||
msgid "Applied run user"
|
||||
msgstr "申请运行的用户"
|
||||
|
||||
#: tickets/handler/command_confirm.py:25
|
||||
msgid "Applied run asset"
|
||||
msgstr "申请运行的资产"
|
||||
|
||||
#: tickets/handler/command_confirm.py:26
|
||||
msgid "Applied run system user"
|
||||
msgstr "申请运行的系统用户"
|
||||
|
||||
#: tickets/handler/command_confirm.py:27
|
||||
msgid "Applied run command"
|
||||
msgstr "申请运行的命令"
|
||||
|
||||
#: tickets/handler/command_confirm.py:28
|
||||
msgid "Applied from session"
|
||||
msgstr "申请来自会话"
|
||||
|
||||
#: tickets/handler/command_confirm.py:29
|
||||
msgid "Applied from command filter rules"
|
||||
msgstr "申请来自命令过滤规则"
|
||||
|
||||
#: tickets/handler/command_confirm.py:30
|
||||
#, fuzzy
|
||||
#| msgid "Applied from command filter rules"
|
||||
msgid "Applied from command filter"
|
||||
msgstr "申请来自命令过滤规则"
|
||||
|
||||
#: tickets/handler/login_asset_confirm.py:16
|
||||
msgid "Applied login user"
|
||||
msgstr "申请登录的用户"
|
||||
|
@ -3465,6 +3502,36 @@ msgstr "批准的资产"
|
|||
msgid "No `Asset` are found under Organization `{}`"
|
||||
msgstr "在组织 `{}` 下没有发现 `资产`"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:12
|
||||
msgid "Run user"
|
||||
msgstr "运行的用户"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:13
|
||||
msgid "Run asset"
|
||||
msgstr "运行的资产"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:15
|
||||
msgid "Run system user"
|
||||
msgstr "运行的系统用户"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:17
|
||||
msgid "Run command"
|
||||
msgstr "运行的命令"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:18
|
||||
msgid "From session"
|
||||
msgstr "来自会话"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:20
|
||||
msgid "From cmd filter rule"
|
||||
msgstr "来自命令过滤规则"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/command_confirm.py:22
|
||||
#, fuzzy
|
||||
#| msgid "From cmd filter rule"
|
||||
msgid "From cmd filter"
|
||||
msgstr "来自命令过滤规则"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/common.py:11
|
||||
msgid "Created by ticket ({}-{})"
|
||||
msgstr "通过工单创建 ({}-{})"
|
||||
|
@ -3585,7 +3652,7 @@ msgstr "原来密码错误"
|
|||
msgid "Automatically configure and download the SSH key"
|
||||
msgstr "自动配置并下载SSH密钥"
|
||||
|
||||
#: users/forms/profile.py:130 users/templates/users/user_update.html:30
|
||||
#: users/forms/profile.py:130
|
||||
msgid "ssh public key"
|
||||
msgstr "SSH公钥"
|
||||
|
||||
|
@ -3821,43 +3888,36 @@ msgstr "重置密码"
|
|||
|
||||
#: users/templates/users/reset_password.html:23
|
||||
#: users/templates/users/user_password_update.html:64
|
||||
#: users/templates/users/user_update.html:13
|
||||
msgid "Your password must satisfy"
|
||||
msgstr "您的密码必须满足:"
|
||||
|
||||
#: users/templates/users/reset_password.html:24
|
||||
#: users/templates/users/user_password_update.html:65
|
||||
#: users/templates/users/user_update.html:14
|
||||
msgid "Password strength"
|
||||
msgstr "密码强度:"
|
||||
|
||||
#: users/templates/users/reset_password.html:48
|
||||
#: users/templates/users/user_password_update.html:102
|
||||
#: users/templates/users/user_update.html:55
|
||||
msgid "Very weak"
|
||||
msgstr "很弱"
|
||||
|
||||
#: users/templates/users/reset_password.html:49
|
||||
#: users/templates/users/user_password_update.html:103
|
||||
#: users/templates/users/user_update.html:56
|
||||
msgid "Weak"
|
||||
msgstr "弱"
|
||||
|
||||
#: users/templates/users/reset_password.html:51
|
||||
#: users/templates/users/user_password_update.html:105
|
||||
#: users/templates/users/user_update.html:58
|
||||
msgid "Medium"
|
||||
msgstr "一般"
|
||||
|
||||
#: users/templates/users/reset_password.html:52
|
||||
#: users/templates/users/user_password_update.html:106
|
||||
#: users/templates/users/user_update.html:59
|
||||
msgid "Strong"
|
||||
msgstr "强"
|
||||
|
||||
#: users/templates/users/reset_password.html:53
|
||||
#: users/templates/users/user_password_update.html:107
|
||||
#: users/templates/users/user_update.html:60
|
||||
msgid "Very strong"
|
||||
msgstr "很强"
|
||||
|
||||
|
@ -3933,18 +3993,6 @@ msgstr "重置"
|
|||
msgid "Verify password"
|
||||
msgstr "校验密码"
|
||||
|
||||
#: users/templates/users/user_update.html:4
|
||||
msgid "Update user"
|
||||
msgstr "更新用户"
|
||||
|
||||
#: users/templates/users/user_update.html:22 users/views/profile/reset.py:120
|
||||
msgid "User auth from {}, go there change password"
|
||||
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
|
||||
|
||||
#: users/templates/users/user_update.html:32
|
||||
msgid "User auth from {}, ssh key login is not supported"
|
||||
msgstr "用户认证源来自 {}, 不支持使用 SSH Key 登录"
|
||||
|
||||
#: users/templates/users/user_verify_mfa.html:11
|
||||
msgid ""
|
||||
"The account protection has been opened, please complete the following "
|
||||
|
@ -4313,6 +4361,10 @@ msgstr "重置密码成功,返回到登录页面"
|
|||
msgid "Token invalid or expired"
|
||||
msgstr "Token错误或失效"
|
||||
|
||||
#: users/views/profile/reset.py:120
|
||||
msgid "User auth from {}, go there change password"
|
||||
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/meta.py:9
|
||||
#: xpack/plugins/change_auth_plan/models.py:89
|
||||
#: xpack/plugins/change_auth_plan/models.py:184
|
||||
|
@ -4985,6 +5037,12 @@ msgstr "社区版"
|
|||
#~ "corresponding private key."
|
||||
#~ msgstr "新的公钥已设置成功,请下载对应的私钥"
|
||||
|
||||
#~ msgid "Update user"
|
||||
#~ msgstr "更新用户"
|
||||
|
||||
#~ msgid "User auth from {}, ssh key login is not supported"
|
||||
#~ msgstr "用户认证源来自 {}, 不支持使用 SSH Key 登录"
|
||||
|
||||
#~ msgid "(Domain name support)"
|
||||
#~ msgstr "(支持域名)"
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ from django.core.files.storage import default_storage
|
|||
from django.core.cache import cache
|
||||
|
||||
from assets.models import Asset
|
||||
from users.models import User
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
from common.db.models import ChoiceSet
|
||||
from ..backends import get_multi_command_storage
|
||||
|
@ -79,6 +80,10 @@ class Session(OrgModelMixin):
|
|||
def asset_obj(self):
|
||||
return Asset.objects.get(id=self.asset_id)
|
||||
|
||||
@property
|
||||
def user_obj(self):
|
||||
return User.objects.get(id=self.user_id)
|
||||
|
||||
@property
|
||||
def _date_start_first_has_replay_rdp_session(self):
|
||||
if self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None:
|
||||
|
|
|
@ -3,3 +3,4 @@
|
|||
from .ticket import *
|
||||
from .assignee import *
|
||||
from .comment import *
|
||||
from .common import *
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.generics import RetrieveDestroyAPIView
|
||||
|
||||
from common.permissions import IsAppUser
|
||||
from common.utils import lazyproperty
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from ..models import Ticket
|
||||
|
||||
|
||||
__all__ = ['GenericTicketStatusRetrieveCloseAPI']
|
||||
|
||||
|
||||
class GenericTicketStatusRetrieveCloseAPI(RetrieveDestroyAPIView):
|
||||
permission_classes = (IsAppUser, )
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
if self.ticket.action_open:
|
||||
status = 'await'
|
||||
elif self.ticket.action_approve:
|
||||
status = 'approve'
|
||||
else:
|
||||
status = 'reject'
|
||||
data = {
|
||||
'status': status,
|
||||
'action': self.ticket.action,
|
||||
'processor': self.ticket.processor_display
|
||||
}
|
||||
return Response(data=data, status=200)
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
if self.ticket.status_open:
|
||||
self.ticket.close(processor=self.ticket.applicant)
|
||||
data = {
|
||||
'action': self.ticket.action,
|
||||
'status': self.ticket.status,
|
||||
'processor': self.ticket.processor_display
|
||||
}
|
||||
return Response(data=data, status=200)
|
||||
|
||||
@lazyproperty
|
||||
def ticket(self):
|
||||
with tmp_to_root_org():
|
||||
return get_object_or_404(Ticket, pk=self.kwargs['pk'])
|
|
@ -10,6 +10,7 @@ class TicketTypeChoices(TextChoices):
|
|||
apply_asset = 'apply_asset', _('Apply for asset')
|
||||
apply_application = 'apply_application', _('Apply for application')
|
||||
login_asset_confirm = 'login_asset_confirm', _('Login asset confirm')
|
||||
command_confirm = 'command_confirm', _('Command confirm')
|
||||
|
||||
|
||||
class TicketActionChoices(TextChoices):
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
from django.utils.translation import ugettext as _
|
||||
from .base import BaseHandler
|
||||
|
||||
|
||||
class Handler(BaseHandler):
|
||||
|
||||
# body
|
||||
def _construct_meta_body_of_open(self):
|
||||
apply_run_user = self.ticket.meta.get('apply_run_user')
|
||||
apply_run_asset = self.ticket.meta.get('apply_run_asset')
|
||||
apply_run_system_user = self.ticket.meta.get('apply_run_system_user')
|
||||
apply_run_command = self.ticket.meta.get('apply_run_command')
|
||||
apply_from_session_id = self.ticket.meta.get('apply_from_session_id')
|
||||
apply_from_cmd_filter_rule_id = self.ticket.meta.get('apply_from_cmd_filter_rule_id')
|
||||
apply_from_cmd_filter_id = self.ticket.meta.get('apply_from_cmd_filter_id')
|
||||
|
||||
applied_body = '''{}: {},
|
||||
{}: {},
|
||||
{}: {},
|
||||
{}: {},
|
||||
{}: {},
|
||||
{}: {},
|
||||
'''.format(
|
||||
_("Applied run user"), apply_run_user,
|
||||
_("Applied run asset"), apply_run_asset,
|
||||
_("Applied run system user"), apply_run_system_user,
|
||||
_("Applied run command"), apply_run_command,
|
||||
_("Applied from session"), apply_from_session_id,
|
||||
_("Applied from command filter rules"), apply_from_cmd_filter_rule_id,
|
||||
_("Applied from command filter"), apply_from_cmd_filter_id,
|
||||
)
|
||||
return applied_body
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1 on 2021-04-26 09:20
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tickets', '0008_auto_20210311_1113'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('general', 'General'), ('login_confirm', 'Login confirm'), ('apply_asset', 'Apply for asset'), ('apply_application', 'Apply for application'), ('login_asset_confirm', 'Login asset confirm'), ('command_confirm', 'Command confirm')], default='general', max_length=64, verbose_name='Type'),
|
||||
),
|
||||
]
|
|
@ -1,5 +1,7 @@
|
|||
from tickets import const
|
||||
from .ticket_type import apply_asset, apply_application, login_confirm, login_asset_confirm
|
||||
from .ticket_type import (
|
||||
apply_asset, apply_application, login_confirm, login_asset_confirm, command_confirm
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'type_serializer_classes_mapping',
|
||||
|
@ -35,5 +37,10 @@ type_serializer_classes_mapping = {
|
|||
'default': login_asset_confirm.LoginAssetConfirmSerializer,
|
||||
action_open: login_asset_confirm.ApplySerializer,
|
||||
action_approve: login_asset_confirm.LoginAssetConfirmSerializer(read_only=True),
|
||||
},
|
||||
const.TicketTypeChoices.command_confirm.value: {
|
||||
'default': command_confirm.CommandConfirmSerializer,
|
||||
action_open: command_confirm.ApplySerializer,
|
||||
action_approve: command_confirm.CommandConfirmSerializer(read_only=True)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
from rest_framework import serializers
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
__all__ = [
|
||||
'ApplySerializer', 'CommandConfirmSerializer',
|
||||
]
|
||||
|
||||
|
||||
class ApplySerializer(serializers.Serializer):
|
||||
# 申请信息
|
||||
apply_run_user = serializers.CharField(required=True, label=_('Run user'))
|
||||
apply_run_asset = serializers.CharField(required=True, label=_('Run asset'))
|
||||
apply_run_system_user = serializers.CharField(
|
||||
required=True, max_length=64, label=_('Run system user')
|
||||
)
|
||||
apply_run_command = serializers.CharField(required=True, label=_('Run command'))
|
||||
apply_from_session_id = serializers.UUIDField(required=False, label=_('From session'))
|
||||
apply_from_cmd_filter_rule_id = serializers.UUIDField(
|
||||
required=False, label=_('From cmd filter rule')
|
||||
)
|
||||
apply_from_cmd_filter_id = serializers.UUIDField(required=False, label=_('From cmd filter'))
|
||||
|
||||
|
||||
class CommandConfirmSerializer(ApplySerializer):
|
||||
pass
|
9
jms
9
jms
|
@ -97,6 +97,14 @@ def check_migrations():
|
|||
# sys.exit(1)
|
||||
|
||||
|
||||
def expire_caches():
|
||||
apps_dir = os.path.join(BASE_DIR, 'apps')
|
||||
code = subprocess.call("python manage.py expire_caches", shell=True, cwd=apps_dir)
|
||||
|
||||
if code == 1:
|
||||
return
|
||||
|
||||
|
||||
def perform_db_migrate():
|
||||
logging.info("Check database structure change ...")
|
||||
os.chdir(os.path.join(BASE_DIR, 'apps'))
|
||||
|
@ -116,6 +124,7 @@ def prepare():
|
|||
check_database_connection()
|
||||
check_migrations()
|
||||
upgrade_db()
|
||||
expire_caches()
|
||||
|
||||
|
||||
def check_pid(pid):
|
||||
|
|
Loading…
Reference in New Issue