chore(merge): 合并ddev

pull/6037/head
ibuler 2021-04-27 18:01:15 +08:00
commit c52431b5ce
20 changed files with 448 additions and 107 deletions

View File

@ -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'])

View File

@ -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

View File

@ -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'),
),
]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 = [

View File

@ -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()

View File

@ -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.

View File

@ -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 "(支持域名)"

View File

@ -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:

View File

@ -3,3 +3,4 @@
from .ticket import *
from .assignee import *
from .comment import *
from .common import *

View File

@ -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'])

View File

@ -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):

View File

@ -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

View File

@ -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'),
),
]

View File

@ -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)
}
}

View File

@ -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
View File

@ -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):