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.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'])
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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')),
|
(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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 = [
|
||||||
|
|
|
@ -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:
|
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.
|
@ -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 "(支持域名)"
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 *
|
||||||
|
|
|
@ -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_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):
|
||||||
|
|
|
@ -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 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
# 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):
|
||||||
|
|
Loading…
Reference in New Issue