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.permissions import IsAppUser
from common.utils import reverse, lazyproperty from common.utils import reverse, lazyproperty
from orgs.utils import tmp_to_org, tmp_to_root_org 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 ..models import LoginAssetACL
from .. import serializers from .. import serializers
@ -48,7 +48,7 @@ class LoginAssetCheckAPI(CreateAPIView):
org_id=self.serializer.org.id org_id=self.serializer.org.id
) )
confirm_status_url = reverse( confirm_status_url = reverse(
view_name='acls:login-asset-confirm-status', view_name='api-acls:login-asset-confirm-status',
kwargs={'pk': str(ticket.id)} kwargs={'pk': str(ticket.id)}
) )
ticket_detail_url = reverse( ticket_detail_url = reverse(
@ -72,34 +72,6 @@ class LoginAssetCheckAPI(CreateAPIView):
return serializer return serializer
class LoginAssetConfirmStatusAPI(RetrieveDestroyAPIView): class LoginAssetConfirmStatusAPI(GenericTicketStatusRetrieveCloseAPI):
permission_classes = (IsAppUser, ) 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 -*- # -*- 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 django.shortcuts import get_object_or_404
from common.utils import reverse
from common.utils import lazyproperty
from orgs.mixins.api import OrgBulkModelViewSet 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 ..models import CommandFilter, CommandFilterRule
from .. import serializers from .. import serializers
__all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet'] __all__ = [
'CommandFilterViewSet', 'CommandFilterRuleViewSet', 'CommandConfirmAPI',
'CommandConfirmStatusAPI'
]
class CommandFilterViewSet(OrgBulkModelViewSet): class CommandFilterViewSet(OrgBulkModelViewSet):
@ -35,3 +45,50 @@ class CommandFilterRuleViewSet(OrgBulkModelViewSet):
return cmd_filter.rules.all() 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')), (TYPE_COMMAND, _('Command')),
) )
ACTION_DENY, ACTION_ALLOW, ACTION_UNKNOWN = range(3) ACTION_UNKNOWN = 10
ACTION_CHOICES = (
(ACTION_DENY, _('Deny')), class ActionChoices(models.IntegerChoices):
(ACTION_ALLOW, _('Allow')), deny = 0, _('Deny')
) allow = 1, _('Allow')
confirm = 2, _('Reconfirm')
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules') 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"), 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)]) validators=[MinValueValidator(1), MaxValueValidator(100)])
content = models.TextField(verbose_name=_("Content"), help_text=_("One line one command")) 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")) comment = models.CharField(max_length=64, blank=True, default='', verbose_name=_("Comment"))
date_created = models.DateTimeField(auto_now_add=True) date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True) date_updated = models.DateTimeField(auto_now=True)
@ -89,10 +96,32 @@ class CommandFilterRule(OrgModelMixin):
if not found: if not found:
return self.ACTION_UNKNOWN, '' return self.ACTION_UNKNOWN, ''
if self.action == self.ACTION_ALLOW: if self.action == self.ActionChoices.allow:
return self.ACTION_ALLOW, found.group() return self.ActionChoices.allow, found.group()
else: else:
return self.ACTION_DENY, found.group() return self.ActionChoices.deny, found.group()
def __str__(self): def __str__(self):
return '{} % {}'.format(self.type, self.content) 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): def is_command_can_run(self, command):
for rule in self.cmd_filter_rules: for rule in self.cmd_filter_rules:
action, matched_cmd = rule.match(command) action, matched_cmd = rule.match(command)
if action == rule.ACTION_ALLOW: if action == rule.ActionChoices.allow:
return True, None return True, None
elif action == rule.ACTION_DENY: elif action == rule.ActionChoices.deny:
return False, matched_cmd return False, matched_cmd
return True, None return True, None

View File

@ -6,6 +6,9 @@ from rest_framework import serializers
from common.drf.serializers import AdaptedBulkListSerializer from common.drf.serializers import AdaptedBulkListSerializer
from ..models import CommandFilter, CommandFilterRule, SystemUser from ..models import CommandFilter, CommandFilterRule, SystemUser
from orgs.mixins.serializers import BulkOrgResourceModelSerializer 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): class CommandFilterSerializer(BulkOrgResourceModelSerializer):
@ -34,7 +37,7 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
fields_mini = ['id'] fields_mini = ['id']
fields_small = fields_mini + [ fields_small = fields_mini + [
'type', 'type_display', 'content', 'priority', 'type', 'type_display', 'content', 'priority',
'action', 'action_display', 'action', 'action_display', 'reviewers',
'comment', 'created_by', 'date_created', 'date_updated' 'comment', 'created_by', 'date_created', 'date_updated'
] ]
fields_fk = ['filter'] fields_fk = ['filter']
@ -50,3 +53,35 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
# msg = _("Content should not be contain: {}").format(invalid_char) # msg = _("Content should not be contain: {}").format(invalid_char)
# raise serializers.ValidationError(msg) # raise serializers.ValidationError(msg)
# return content # 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('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 = [ 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: if count is not None:
return count return count
ds, de = self.get_date_start_2_end(date) 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) self.__set_data_to_cache(date, tp, count)
return count return count
@ -129,7 +129,7 @@ class DatesLoginMetricMixin:
@lazyproperty @lazyproperty
def dates_total_count_active_users(self): 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 return count
@lazyproperty @lazyproperty
@ -164,7 +164,7 @@ class DatesLoginMetricMixin:
# 以下是从week中而来 # 以下是从week中而来
def get_dates_login_times_top5_users(self): 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 = [ users = [
{'user': user, 'total': total} {'user': user, 'total': total}
for user, total in Counter(users).most_common(5) for user, total in Counter(users).most_common(5)
@ -172,7 +172,7 @@ class DatesLoginMetricMixin:
return users return users
def get_dates_total_count_login_users(self): 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): def get_dates_total_count_login_times(self):
return self.sessions_queryset.count() return self.sessions_queryset.count()
@ -186,8 +186,8 @@ class DatesLoginMetricMixin:
return list(assets) return list(assets)
def get_dates_login_times_top10_users(self): def get_dates_login_times_top10_users(self):
users = self.sessions_queryset.values("user") \ users = self.sessions_queryset.values("user_id") \
.annotate(total=Count("user")) \ .annotate(total=Count("user_id")) \
.annotate(last=Max("date_start")).order_by("-total")[:10] .annotate(last=Max("date_start")).order_by("-total")[:10]
for user in users: for user in users:
user['last'] = str(user['last']) user['last'] = str(user['last'])
@ -221,7 +221,7 @@ class TotalCountMixin:
@staticmethod @staticmethod
def get_total_count_online_users(): 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 return count
@staticmethod @staticmethod

Binary file not shown.

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n" "Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n" "Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n" "Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -34,12 +34,12 @@ msgstr ""
msgid "Name" msgid "Name"
msgstr "名称" 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 #: assets/models/user.py:122
msgid "Priority" msgid "Priority"
msgstr "优先级" 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 #: assets/models/user.py:122
msgid "1-100, the lower the value will be match first" msgid "1-100, the lower the value will be match first"
msgstr "优先级可选范围为 1-100 (数值越小越优先)" msgstr "优先级可选范围为 1-100 (数值越小越优先)"
@ -55,7 +55,7 @@ msgstr "激活中"
#: acls/models/base.py:32 applications/models/application.py:24 #: acls/models/base.py:32 applications/models/application.py:24
#: assets/models/asset.py:147 assets/models/asset.py:223 #: assets/models/asset.py:147 assets/models/asset.py:223
#: assets/models/base.py:255 assets/models/cluster.py:29 #: 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/domain.py:22 assets/models/domain.py:56
#: assets/models/group.py:23 assets/models/label.py:23 ops/models/adhoc.py:37 #: 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 #: orgs/models.py:26 perms/models/base.py:57 settings/models.py:34
@ -67,11 +67,11 @@ msgstr "激活中"
msgid "Comment" msgid "Comment"
msgstr "备注" msgstr "备注"
#: acls/models/login_acl.py:16 tickets/const.py:18 #: acls/models/login_acl.py:16 tickets/const.py:19
msgid "Reject" msgid "Reject"
msgstr "拒绝" 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" msgid "Allow"
msgstr "允许" msgstr "允许"
@ -81,7 +81,7 @@ msgstr "登录IP"
#: acls/models/login_acl.py:24 acls/models/login_asset_acl.py:26 #: 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 #: 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 #: authentication/templates/authentication/_access_key_modal.html:34
#: tickets/models/ticket.py:43 users/templates/users/_granted_assets.html:29 #: tickets/models/ticket.py:43 users/templates/users/_granted_assets.html:29
#: users/templates/users/user_asset_permission.html:44 #: 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 #: authentication/models.py:97 orgs/models.py:18 orgs/models.py:418
#: perms/models/base.py:50 templates/index.html:78 #: perms/models/base.py:50 templates/index.html:78
#: terminal/backends/command/models.py:18 #: 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 #: tickets/models/comment.py:17 users/models/user.py:164
#: users/models/user.py:712 users/serializers/group.py:20 #: users/models/user.py:712 users/serializers/group.py:20
#: users/templates/users/user_asset_permission.html:38 #: users/templates/users/user_asset_permission.html:38
@ -124,7 +124,7 @@ msgstr "系统用户"
#: assets/serializers/system_user.py:192 audits/models.py:38 #: assets/serializers/system_user.py:192 audits/models.py:38
#: perms/models/asset_permission.py:99 templates/index.html:82 #: perms/models/asset_permission.py:99 templates/index.html:82
#: terminal/backends/command/models.py:19 #: 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:40
#: users/templates/users/user_asset_permission.html:70 #: users/templates/users/user_asset_permission.html:70
#: xpack/plugins/change_auth_plan/models.py:282 #: xpack/plugins/change_auth_plan/models.py:282
@ -132,7 +132,8 @@ msgstr "系统用户"
msgid "Asset" msgid "Asset"
msgstr "资产" 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" msgid "Reviewers"
msgstr "审批人" msgstr "审批人"
@ -250,7 +251,7 @@ msgstr "自定义"
msgid "Category" msgid "Category"
msgstr "类别" 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/models/application_permission.py:23
#: perms/serializers/application/permission.py:17 #: perms/serializers/application/permission.py:17
#: perms/serializers/application/user_permission.py:34 #: 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_otp_check_password.html:13
#: users/templates/users/user_password_update.html:43 #: users/templates/users/user_password_update.html:43
#: users/templates/users/user_password_verify.html:18 #: 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:68
#: xpack/plugins/change_auth_plan/models.py:190 #: xpack/plugins/change_auth_plan/models.py:190
#: xpack/plugins/change_auth_plan/models.py:285 #: 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/asset.py:221 assets/models/base.py:258
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26 #: 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 #: 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 #: 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 #: users/serializers/group.py:35 xpack/plugins/change_auth_plan/models.py:81
@ -585,30 +585,38 @@ msgid "Regex"
msgstr "正则表达式" msgstr "正则表达式"
#: assets/models/cmd_filter.py:41 ops/models/command.py:25 #: 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" msgid "Command"
msgstr "命令" msgstr "命令"
#: assets/models/cmd_filter.py:46 #: assets/models/cmd_filter.py:47
msgid "Deny" msgid "Deny"
msgstr "拒绝" msgstr "拒绝"
#: assets/models/cmd_filter.py:51 #: assets/models/cmd_filter.py:49
msgid "Reconfirm"
msgstr "复核"
#: assets/models/cmd_filter.py:52
msgid "Filter" msgid "Filter"
msgstr "过滤器" 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" msgid "Content"
msgstr "内容" msgstr "内容"
#: assets/models/cmd_filter.py:55 #: assets/models/cmd_filter.py:56
msgid "One line one command" msgid "One line one command"
msgstr "每行一个命令" msgstr "每行一个命令"
#: assets/models/cmd_filter.py:64 #: assets/models/cmd_filter.py:71
msgid "Command filter rule" msgid "Command filter rule"
msgstr "命令过滤规则" msgstr "命令过滤规则"
#: assets/models/cmd_filter.py:111 tickets/const.py:13
msgid "Command confirm"
msgstr "命令复核"
#: assets/models/domain.py:64 #: assets/models/domain.py:64
msgid "Gateway" msgid "Gateway"
msgstr "网关" msgstr "网关"
@ -735,7 +743,7 @@ msgstr "用户组"
#: perms/models/application_permission.py:31 #: perms/models/application_permission.py:31
#: perms/models/asset_permission.py:101 templates/_nav.html:45 #: perms/models/asset_permission.py:101 templates/_nav.html:45
#: terminal/backends/command/models.py:20 #: 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/_granted_assets.html:27
#: users/templates/users/user_asset_permission.html:42 #: users/templates/users/user_asset_permission.html:42
#: users/templates/users/user_asset_permission.html:76 #: users/templates/users/user_asset_permission.html:76
@ -1025,7 +1033,7 @@ msgid "Symlink"
msgstr "建立软链接" msgstr "建立软链接"
#: audits/models.py:37 audits/models.py:60 audits/models.py:71 #: 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" msgid "Remote addr"
msgstr "远端地址" msgstr "远端地址"
@ -1042,7 +1050,7 @@ msgid "Success"
msgstr "成功" msgstr "成功"
#: audits/models.py:43 ops/models/command.py:30 perms/models/base.py:53 #: 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:43
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:74 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:74
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:40 #: 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/_access_key_modal.html:155
#: authentication/templates/authentication/_mfa_confirm_modal.html:53 #: 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" msgid "Close"
msgstr "关闭" msgstr "关闭"
@ -2944,13 +2952,12 @@ msgstr "较高"
#: terminal/const.py:33 users/templates/users/reset_password.html:50 #: terminal/const.py:33 users/templates/users/reset_password.html:50
#: users/templates/users/user_password_update.html:104 #: users/templates/users/user_password_update.html:104
#: users/templates/users/user_update.html:57
msgid "Normal" msgid "Normal"
msgstr "正常" msgstr "正常"
#: terminal/const.py:34 #: terminal/const.py:34
msgid "Offline" msgid "Offline"
msgstr "" msgstr "离线"
#: terminal/exceptions.py:8 #: terminal/exceptions.py:8
msgid "Bulk create not support" msgid "Bulk create not support"
@ -2960,15 +2967,15 @@ msgstr "不支持批量创建"
msgid "Storage is invalid" msgid "Storage is invalid"
msgstr "存储无效" msgstr "存储无效"
#: terminal/models/session.py:43 #: terminal/models/session.py:44
msgid "Login from" msgid "Login from"
msgstr "登录来源" msgstr "登录来源"
#: terminal/models/session.py:47 #: terminal/models/session.py:48
msgid "Replay" msgid "Replay"
msgstr "回放" msgstr "回放"
#: terminal/models/session.py:52 #: terminal/models/session.py:53
msgid "Date end" msgid "Date end"
msgstr "结束日期" msgstr "结束日期"
@ -3115,7 +3122,7 @@ msgstr "文档类型"
#: terminal/serializers/storage.py:185 #: terminal/serializers/storage.py:185
msgid "Ignore Certificate Verification" msgid "Ignore Certificate Verification"
msgstr "" msgstr "忽略证书认证"
#: terminal/serializers/terminal.py:66 terminal/serializers/terminal.py:74 #: terminal/serializers/terminal.py:66 terminal/serializers/terminal.py:74
msgid "Not found" msgid "Not found"
@ -3205,15 +3212,15 @@ msgstr "申请资产"
msgid "Apply for application" msgid "Apply for application"
msgstr "申请应用" msgstr "申请应用"
#: tickets/const.py:16 tickets/const.py:23 #: tickets/const.py:17 tickets/const.py:24
msgid "Open" msgid "Open"
msgstr "打开" msgstr "打开"
#: tickets/const.py:17 #: tickets/const.py:18
msgid "Approve" msgid "Approve"
msgstr "同意" msgstr "同意"
#: tickets/const.py:24 #: tickets/const.py:25
msgid "Closed" msgid "Closed"
msgstr "关闭" msgstr "关闭"
@ -3328,6 +3335,36 @@ msgstr "工单申请信息"
msgid "Ticket approved info" msgid "Ticket approved info"
msgstr "工单批准信息" 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 #: tickets/handler/login_asset_confirm.py:16
msgid "Applied login user" msgid "Applied login user"
msgstr "申请登录的用户" msgstr "申请登录的用户"
@ -3465,6 +3502,36 @@ msgstr "批准的资产"
msgid "No `Asset` are found under Organization `{}`" msgid "No `Asset` are found under Organization `{}`"
msgstr "在组织 `{}` 下没有发现 `资产`" 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 #: tickets/serializers/ticket/meta/ticket_type/common.py:11
msgid "Created by ticket ({}-{})" msgid "Created by ticket ({}-{})"
msgstr "通过工单创建 ({}-{})" msgstr "通过工单创建 ({}-{})"
@ -3585,7 +3652,7 @@ msgstr "原来密码错误"
msgid "Automatically configure and download the SSH key" msgid "Automatically configure and download the SSH key"
msgstr "自动配置并下载SSH密钥" msgstr "自动配置并下载SSH密钥"
#: users/forms/profile.py:130 users/templates/users/user_update.html:30 #: users/forms/profile.py:130
msgid "ssh public key" msgid "ssh public key"
msgstr "SSH公钥" msgstr "SSH公钥"
@ -3821,43 +3888,36 @@ msgstr "重置密码"
#: users/templates/users/reset_password.html:23 #: users/templates/users/reset_password.html:23
#: users/templates/users/user_password_update.html:64 #: users/templates/users/user_password_update.html:64
#: users/templates/users/user_update.html:13
msgid "Your password must satisfy" msgid "Your password must satisfy"
msgstr "您的密码必须满足:" msgstr "您的密码必须满足:"
#: users/templates/users/reset_password.html:24 #: users/templates/users/reset_password.html:24
#: users/templates/users/user_password_update.html:65 #: users/templates/users/user_password_update.html:65
#: users/templates/users/user_update.html:14
msgid "Password strength" msgid "Password strength"
msgstr "密码强度:" msgstr "密码强度:"
#: users/templates/users/reset_password.html:48 #: users/templates/users/reset_password.html:48
#: users/templates/users/user_password_update.html:102 #: users/templates/users/user_password_update.html:102
#: users/templates/users/user_update.html:55
msgid "Very weak" msgid "Very weak"
msgstr "很弱" msgstr "很弱"
#: users/templates/users/reset_password.html:49 #: users/templates/users/reset_password.html:49
#: users/templates/users/user_password_update.html:103 #: users/templates/users/user_password_update.html:103
#: users/templates/users/user_update.html:56
msgid "Weak" msgid "Weak"
msgstr "弱" msgstr "弱"
#: users/templates/users/reset_password.html:51 #: users/templates/users/reset_password.html:51
#: users/templates/users/user_password_update.html:105 #: users/templates/users/user_password_update.html:105
#: users/templates/users/user_update.html:58
msgid "Medium" msgid "Medium"
msgstr "一般" msgstr "一般"
#: users/templates/users/reset_password.html:52 #: users/templates/users/reset_password.html:52
#: users/templates/users/user_password_update.html:106 #: users/templates/users/user_password_update.html:106
#: users/templates/users/user_update.html:59
msgid "Strong" msgid "Strong"
msgstr "强" msgstr "强"
#: users/templates/users/reset_password.html:53 #: users/templates/users/reset_password.html:53
#: users/templates/users/user_password_update.html:107 #: users/templates/users/user_password_update.html:107
#: users/templates/users/user_update.html:60
msgid "Very strong" msgid "Very strong"
msgstr "很强" msgstr "很强"
@ -3933,18 +3993,6 @@ msgstr "重置"
msgid "Verify password" msgid "Verify password"
msgstr "校验密码" 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 #: users/templates/users/user_verify_mfa.html:11
msgid "" msgid ""
"The account protection has been opened, please complete the following " "The account protection has been opened, please complete the following "
@ -4313,6 +4361,10 @@ msgstr "重置密码成功,返回到登录页面"
msgid "Token invalid or expired" msgid "Token invalid or expired"
msgstr "Token错误或失效" 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/meta.py:9
#: xpack/plugins/change_auth_plan/models.py:89 #: xpack/plugins/change_auth_plan/models.py:89
#: xpack/plugins/change_auth_plan/models.py:184 #: xpack/plugins/change_auth_plan/models.py:184
@ -4985,6 +5037,12 @@ msgstr "社区版"
#~ "corresponding private key." #~ "corresponding private key."
#~ msgstr "新的公钥已设置成功,请下载对应的私钥" #~ msgstr "新的公钥已设置成功,请下载对应的私钥"
#~ msgid "Update user"
#~ msgstr "更新用户"
#~ msgid "User auth from {}, ssh key login is not supported"
#~ msgstr "用户认证源来自 {}, 不支持使用 SSH Key 登录"
#~ msgid "(Domain name support)" #~ msgid "(Domain name support)"
#~ msgstr "(支持域名)" #~ msgstr "(支持域名)"

View File

@ -11,6 +11,7 @@ from django.core.files.storage import default_storage
from django.core.cache import cache from django.core.cache import cache
from assets.models import Asset from assets.models import Asset
from users.models import User
from orgs.mixins.models import OrgModelMixin from orgs.mixins.models import OrgModelMixin
from common.db.models import ChoiceSet from common.db.models import ChoiceSet
from ..backends import get_multi_command_storage from ..backends import get_multi_command_storage
@ -79,6 +80,10 @@ class Session(OrgModelMixin):
def asset_obj(self): def asset_obj(self):
return Asset.objects.get(id=self.asset_id) return Asset.objects.get(id=self.asset_id)
@property
def user_obj(self):
return User.objects.get(id=self.user_id)
@property @property
def _date_start_first_has_replay_rdp_session(self): def _date_start_first_has_replay_rdp_session(self):
if self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None: if self.__class__._DATE_START_FIRST_HAS_REPLAY_RDP_SESSION is None:

View File

@ -3,3 +3,4 @@
from .ticket import * from .ticket import *
from .assignee import * from .assignee import *
from .comment 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_asset = 'apply_asset', _('Apply for asset')
apply_application = 'apply_application', _('Apply for application') apply_application = 'apply_application', _('Apply for application')
login_asset_confirm = 'login_asset_confirm', _('Login asset confirm') login_asset_confirm = 'login_asset_confirm', _('Login asset confirm')
command_confirm = 'command_confirm', _('Command confirm')
class TicketActionChoices(TextChoices): 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 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__ = [ __all__ = [
'type_serializer_classes_mapping', 'type_serializer_classes_mapping',
@ -35,5 +37,10 @@ type_serializer_classes_mapping = {
'default': login_asset_confirm.LoginAssetConfirmSerializer, 'default': login_asset_confirm.LoginAssetConfirmSerializer,
action_open: login_asset_confirm.ApplySerializer, action_open: login_asset_confirm.ApplySerializer,
action_approve: login_asset_confirm.LoginAssetConfirmSerializer(read_only=True), 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) # 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(): def perform_db_migrate():
logging.info("Check database structure change ...") logging.info("Check database structure change ...")
os.chdir(os.path.join(BASE_DIR, 'apps')) os.chdir(os.path.join(BASE_DIR, 'apps'))
@ -116,6 +124,7 @@ def prepare():
check_database_connection() check_database_connection()
check_migrations() check_migrations()
upgrade_db() upgrade_db()
expire_caches()
def check_pid(pid): def check_pid(pid):