feat: 命令过滤器支持多维度绑定

feat: 修改命令过滤规则校验

feat: 修改系统用户命令过滤器迁移文件
pull/7345/head^2
Michael Bai 2021-12-09 17:18:16 +08:00 committed by 老广
parent 3ea4a9fbe6
commit bbf2aff96c
9 changed files with 263 additions and 113 deletions

View File

@ -29,7 +29,7 @@ class CommandFilterViewSet(OrgBulkModelViewSet):
class CommandFilterRuleViewSet(OrgBulkModelViewSet): class CommandFilterRuleViewSet(OrgBulkModelViewSet):
model = CommandFilterRule model = CommandFilterRule
filterset_fields = ("content",) filterset_fields = ('content',)
search_fields = filterset_fields search_fields = filterset_fields
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)
serializer_class = serializers.CommandFilterRuleSerializer serializer_class = serializers.CommandFilterRuleSerializer

View File

@ -1,15 +1,18 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework.response import Response from rest_framework.response import Response
from django.db.models import Q
from common.utils import get_logger from common.utils import get_logger, get_object_or_none
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsValidUser from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser, IsValidUser
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from orgs.mixins import generics from orgs.mixins import generics
from common.mixins.api import SuggestionMixin from common.mixins.api import SuggestionMixin
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org
from rest_framework.decorators import action from rest_framework.decorators import action
from ..models import SystemUser, Asset from users.models import User, UserGroup
from applications.models import Application
from ..models import SystemUser, Asset, CommandFilter, CommandFilterRule
from .. import serializers from .. import serializers
from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer
from ..tasks import ( from ..tasks import (
@ -192,9 +195,42 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView):
return CommandFilterRuleSerializer return CommandFilterRuleSerializer
def get_queryset(self): def get_queryset(self):
pk = self.kwargs.get('pk', None) user_groups = []
system_user = get_object_or_404(SystemUser, pk=pk) user_id = self.request.query_params.get('user_id')
return system_user.cmd_filter_rules user = get_object_or_none(User, pk=user_id)
if user:
user_groups.extend(list(user.groups.all()))
user_group_id = self.request.query_params.get('user_group_id')
user_group = get_object_or_none(UserGroup, pk=user_group_id)
if user_group:
user_groups.append(user_group)
system_user_id = self.kwargs.get('pk', None)
system_user = get_object_or_none(SystemUser, pk=system_user_id)
if not system_user:
system_user_id = self.request.query_params.get('system_user_id')
system_user = get_object_or_none(SystemUser, pk=system_user_id)
asset_id = self.request.query_params.get('asset_id')
asset = get_object_or_none(Asset, pk=asset_id)
application_id = self.request.query_params.get('application_id')
application = get_object_or_none(Application, pk=application_id)
q = Q()
if user:
q |= Q(users=user)
if user_group:
q |= Q(user_groups__in=set(user_groups))
if system_user:
q |= Q(system_users=system_user)
if asset:
q |= Q(assets=asset)
if application:
q |= Q(applications=application)
if q:
cmd_filters = CommandFilter.objects.filter(q).filter(is_active=True)
rule_ids = cmd_filters.values_list('rules', flat=True)
rules = CommandFilterRule.objects.filter(id__in=rule_ids)
else:
rules = CommandFilterRule.objects.none()
return rules
class SystemUserAssetsListView(generics.ListAPIView): class SystemUserAssetsListView(generics.ListAPIView):

View File

@ -0,0 +1,74 @@
# Generated by Django 3.1.13 on 2021-12-09 06:40
from django.conf import settings
from django.db import migrations, models
def migrate_system_users_cmd_filters(apps, schema_editor):
system_user_model = apps.get_model("assets", "SystemUser")
cmd_filter_model = apps.get_model("assets", "CommandFilter")
su_through = system_user_model.cmd_filters.through
cf_through = cmd_filter_model.system_users.through
su_relation_objects = su_through.objects.all()
cf_relation_objects = [
cf_through(**{
'id': su_relation.id,
'systemuser_id': su_relation.systemuser_id,
'commandfilter_id': su_relation.commandfilter_id
})
for su_relation in su_relation_objects
]
cf_through.objects.bulk_create(cf_relation_objects)
class Migration(migrations.Migration):
dependencies = [
('applications', '0014_auto_20211105_1605'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('assets', '0081_auto_20211105_1605'),
]
operations = [
migrations.AddField(
model_name='commandfilter',
name='applications',
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to='applications.Application', verbose_name='Application'),
),
migrations.AddField(
model_name='commandfilter',
name='assets',
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to='assets.Asset', verbose_name='Asset'),
),
migrations.AddField(
model_name='commandfilter',
name='system_users',
field=models.ManyToManyField(blank=True, related_name='cmd_filters_pre', to='assets.SystemUser', verbose_name='System user'),
),
migrations.AddField(
model_name='commandfilter',
name='users',
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to=settings.AUTH_USER_MODEL, verbose_name='User'),
),
migrations.AddField(
model_name='commandfilter',
name='user_groups',
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to='users.UserGroup', verbose_name='User group'),
),
migrations.AlterField(
model_name='systemuser',
name='cmd_filters',
field=models.ManyToManyField(blank=True, related_name='system_users_bak', to='assets.CommandFilter', verbose_name='Command filter'),
),
migrations.RunPython(migrate_system_users_cmd_filters),
migrations.RemoveField(
model_name='systemuser',
name='cmd_filters',
),
migrations.AlterField(
model_name='commandfilter',
name='system_users',
field=models.ManyToManyField(blank=True, related_name='cmd_filters', to='assets.SystemUser', verbose_name='System user'),
),
]

View File

@ -7,9 +7,11 @@ from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from common.utils import lazyproperty from common.utils import lazyproperty, get_logger
from orgs.mixins.models import OrgModelMixin from orgs.mixins.models import OrgModelMixin
logger = get_logger(__file__)
__all__ = [ __all__ = [
'CommandFilter', 'CommandFilterRule' 'CommandFilter', 'CommandFilterRule'
@ -19,11 +21,32 @@ __all__ = [
class CommandFilter(OrgModelMixin): class CommandFilter(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=64, verbose_name=_("Name")) name = models.CharField(max_length=64, verbose_name=_("Name"))
users = models.ManyToManyField(
'users.User', related_name='cmd_filters', blank=True,
verbose_name=_("User")
)
user_groups = models.ManyToManyField(
'users.UserGroup', related_name='cmd_filters', blank=True,
verbose_name=_("User group"),
)
assets = models.ManyToManyField(
'assets.Asset', related_name='cmd_filters', blank=True,
verbose_name=_("Asset")
)
system_users = models.ManyToManyField(
'assets.SystemUser', related_name='cmd_filters', blank=True,
verbose_name=_("System user"))
applications = models.ManyToManyField(
'applications.Application', related_name='cmd_filters', blank=True,
verbose_name=_("Application")
)
is_active = models.BooleanField(default=True, verbose_name=_('Is active')) is_active = models.BooleanField(default=True, verbose_name=_('Is active'))
comment = models.TextField(blank=True, default='', verbose_name=_("Comment")) comment = models.TextField(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)
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by')) created_by = models.CharField(
max_length=128, blank=True, default='', verbose_name=_('Created by')
)
def __str__(self): def __str__(self):
return self.name return self.name
@ -71,10 +94,18 @@ class CommandFilterRule(OrgModelMixin):
verbose_name = _("Command filter rule") verbose_name = _("Command filter rule")
@lazyproperty @lazyproperty
def _pattern(self): def pattern(self):
if self.type == 'command': if self.type == 'command':
s = self.construct_command_regex(content=self.content)
else:
s = r'{0}'.format(self.content)
return s
@classmethod
def construct_command_regex(cls, content):
regex = [] regex = []
content = self.content.replace('\r\n', '\n') content = content.replace('\r\n', '\n')
for _cmd in content.split('\n'): for _cmd in content.split('\n'):
cmd = re.sub(r'\s+', ' ', _cmd) cmd = re.sub(r'\s+', ' ', _cmd)
cmd = re.escape(cmd) cmd = re.escape(cmd)
@ -90,17 +121,25 @@ class CommandFilterRule(OrgModelMixin):
regex.append(r'\b{0}\b'.format(cmd)) regex.append(r'\b{0}\b'.format(cmd))
else: else:
regex.append(r'\b{0}'.format(cmd)) regex.append(r'\b{0}'.format(cmd))
s = r'{}'.format('|'.join(regex)) s = r'(?i){}'.format('|'.join(regex))
else: return s
s = r'{0}'.format(self.content)
@staticmethod
def compile_regex(regex):
try: try:
_pattern = re.compile(s) pattern = re.compile(regex)
except: except Exception as e:
_pattern = '' error = _('The generated regular expression is incorrect: {}').format(str(e))
return _pattern logger.error(error)
return False, error, None
return True, '', pattern
def match(self, data): def match(self, data):
found = self._pattern.search(data) succeed, error, pattern = self.compile_regex(regex=self.pattern)
if not succeed:
return self.ACTION_UNKNOWN, ''
found = pattern.search(data)
if not found: if not found:
return self.ACTION_UNKNOWN, '' return self.ACTION_UNKNOWN, ''

View File

@ -203,7 +203,6 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo')) sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode')) login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
cmd_filters = models.ManyToManyField('CommandFilter', related_name='system_users', verbose_name=_("Command filter"), blank=True)
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root")) sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
token = models.TextField(default='', verbose_name=_('Token')) token = models.TextField(default='', verbose_name=_('Token'))
home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True) home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True)

View File

@ -3,6 +3,7 @@
import re import re
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..models import CommandFilter, CommandFilterRule from ..models import CommandFilter, CommandFilterRule
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org
@ -22,16 +23,14 @@ class CommandFilterSerializer(BulkOrgResourceModelSerializer):
'comment', 'created_by', 'comment', 'created_by',
] ]
fields_fk = ['rules'] fields_fk = ['rules']
fields_m2m = ['system_users'] fields_m2m = ['users', 'user_groups', 'system_users', 'assets', 'applications']
fields = fields_small + fields_fk + fields_m2m fields = fields_small + fields_fk + fields_m2m
extra_kwargs = { extra_kwargs = {
'rules': {'read_only': True}, 'rules': {'read_only': True}
'system_users': {'required': False},
} }
class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer): class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
invalid_pattern = re.compile(r'[\.\*\+\[\\\?\{\}\^\$\|\(\)\#\<\>]')
type_display = serializers.ReadOnlyField(source='get_type_display') type_display = serializers.ReadOnlyField(source='get_type_display')
action_display = serializers.ReadOnlyField(source='get_action_display') action_display = serializers.ReadOnlyField(source='get_action_display')
@ -39,13 +38,13 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
model = CommandFilterRule model = CommandFilterRule
fields_mini = ['id'] fields_mini = ['id']
fields_small = fields_mini + [ fields_small = fields_mini + [
'type', 'type_display', 'content', 'priority', 'type', 'type_display', 'content', 'pattern', 'priority',
'action', 'action_display', 'reviewers', 'action', 'action_display', 'reviewers',
'date_created', 'date_updated', 'date_created', 'date_updated',
'comment', 'created_by', 'comment', 'created_by',
] ]
fields_fk = ['filter'] fields_fk = ['filter']
fields = '__all__' fields = fields_small + fields_fk
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -61,15 +60,16 @@ class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
choices.pop(CommandFilterRule.ActionChoices.confirm, None) choices.pop(CommandFilterRule.ActionChoices.confirm, None)
action._choices = choices action._choices = choices
# def validate_content(self, content): def validate_content(self, content):
# tp = self.initial_data.get("type") tp = self.initial_data.get("type")
# if tp == CommandFilterRule.TYPE_REGEX: if tp == CommandFilterRule.TYPE_COMMAND:
# return content regex = CommandFilterRule.construct_command_regex(content)
# if self.invalid_pattern.search(content): else:
# invalid_char = self.invalid_pattern.pattern.replace('\\', '') regex = content
# msg = _("Content should not be contain: {}").format(invalid_char) succeed, error, pattern = CommandFilterRule.compile_regex(regex)
# raise serializers.ValidationError(msg) if not succeed:
# return content raise serializers.ValidationError(error)
return content
class CommandConfirmSerializer(serializers.Serializer): class CommandConfirmSerializer(serializers.Serializer):

View File

@ -48,6 +48,7 @@ urlpatterns = [
path('system-users/<uuid:pk>/temp-auth/', api.SystemUserTempAuthInfoApi.as_view(), name='system-user-asset-temp-info'), path('system-users/<uuid:pk>/temp-auth/', api.SystemUserTempAuthInfoApi.as_view(), name='system-user-asset-temp-info'),
path('system-users/<uuid:pk>/tasks/', api.SystemUserTaskApi.as_view(), name='system-user-task-create'), path('system-users/<uuid:pk>/tasks/', api.SystemUserTaskApi.as_view(), name='system-user-task-create'),
path('system-users/<uuid:pk>/cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='system-user-cmd-filter-rule-list'), path('system-users/<uuid:pk>/cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='system-user-cmd-filter-rule-list'),
path('cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='cmd-filter-rules'),
path('accounts/tasks/', api.AccountTaskCreateAPI.as_view(), name='account-task-create'), path('accounts/tasks/', api.AccountTaskCreateAPI.as_view(), name='account-task-create'),

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:d9471737ad3816416dd8389f66a7530ecd874d0ccf69838b5ab0edae390398c9 oid sha256:5a4e8bcfb535a77c85668323bb86f0348d2622f4840ecb42bc0ca9e47fcbef51
size 94354 size 94514

View File

@ -7,7 +7,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-12-08 17:30+0800\n" "POT-Creation-Date: 2021-12-09 17:52+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\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"
@ -20,7 +20,7 @@ msgstr ""
#: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47 #: acls/models/base.py:25 acls/serializers/login_asset_acl.py:47
#: applications/models/application.py:166 assets/models/asset.py:139 #: applications/models/application.py:166 assets/models/asset.py:139
#: assets/models/base.py:175 assets/models/cluster.py:18 #: assets/models/base.py:175 assets/models/cluster.py:18
#: assets/models/cmd_filter.py:21 assets/models/domain.py:24 #: assets/models/cmd_filter.py:23 assets/models/domain.py:24
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24 #: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: orgs/models.py:24 perms/models/base.py:44 settings/models.py:29 #: orgs/models.py:24 perms/models/base.py:44 settings/models.py:29
#: settings/serializers/sms.py:6 terminal/models/storage.py:23 #: settings/serializers/sms.py:6 terminal/models/storage.py:23
@ -34,12 +34,12 @@ msgstr ""
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
#: acls/models/base.py:27 assets/models/cmd_filter.py:54 #: acls/models/base.py:27 assets/models/cmd_filter.py:77
#: assets/models/user.py:200 #: assets/models/user.py:200
msgid "Priority" msgid "Priority"
msgstr "优先级" msgstr "优先级"
#: acls/models/base.py:28 assets/models/cmd_filter.py:54 #: acls/models/base.py:28 assets/models/cmd_filter.py:77
#: assets/models/user.py:200 #: assets/models/user.py:200
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 (数值越小越优先)"
@ -54,7 +54,7 @@ msgstr "激活中"
#: acls/models/base.py:32 applications/models/application.py:179 #: acls/models/base.py:32 applications/models/application.py:179
#: assets/models/asset.py:144 assets/models/asset.py:232 #: assets/models/asset.py:144 assets/models/asset.py:232
#: assets/models/base.py:180 assets/models/cluster.py:29 #: assets/models/base.py:180 assets/models/cluster.py:29
#: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:64 #: assets/models/cmd_filter.py:44 assets/models/cmd_filter.py:87
#: assets/models/domain.py:25 assets/models/domain.py:65 #: assets/models/domain.py:25 assets/models/domain.py:65
#: 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:27 perms/models/base.py:53 settings/models.py:34 #: orgs/models.py:27 perms/models/base.py:53 settings/models.py:34
@ -70,7 +70,7 @@ msgstr "备注"
msgid "Reject" msgid "Reject"
msgstr "拒绝" msgstr "拒绝"
#: acls/models/login_acl.py:19 assets/models/cmd_filter.py:48 #: acls/models/login_acl.py:19 assets/models/cmd_filter.py:71
msgid "Allow" msgid "Allow"
msgstr "允许" msgstr "允许"
@ -80,10 +80,11 @@ msgid "Login confirm"
msgstr "登录复核" msgstr "登录复核"
#: acls/models/login_acl.py:24 acls/models/login_asset_acl.py:20 #: acls/models/login_acl.py:24 acls/models/login_asset_acl.py:20
#: assets/models/label.py:15 audits/models.py:36 audits/models.py:56 #: assets/models/cmd_filter.py:26 assets/models/label.py:15 audits/models.py:36
#: audits/models.py:74 audits/serializers.py:94 authentication/models.py:47 #: audits/models.py:56 audits/models.py:74 audits/serializers.py:94
#: orgs/models.py:19 orgs/models.py:433 perms/models/base.py:45 #: authentication/models.py:47 orgs/models.py:19 orgs/models.py:433
#: templates/index.html:78 terminal/backends/command/models.py:18 #: perms/models/base.py:45 templates/index.html:78
#: terminal/backends/command/models.py:18
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:39 #: terminal/backends/command/serializers.py:12 terminal/models/session.py:39
#: terminal/notifications.py:90 terminal/notifications.py:138 #: terminal/notifications.py:90 terminal/notifications.py:138
#: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:169 #: tickets/models/comment.py:17 users/const.py:14 users/models/user.py:169
@ -102,7 +103,7 @@ msgstr "规则"
#: acls/models/login_acl.py:31 acls/models/login_asset_acl.py:26 #: acls/models/login_acl.py:31 acls/models/login_asset_acl.py:26
#: acls/serializers/login_acl.py:17 acls/serializers/login_asset_acl.py:75 #: acls/serializers/login_acl.py:17 acls/serializers/login_asset_acl.py:75
#: assets/models/cmd_filter.py:57 audits/models.py:57 #: assets/models/cmd_filter.py:80 audits/models.py:57
#: authentication/templates/authentication/_access_key_modal.html:34 #: authentication/templates/authentication/_access_key_modal.html:34
#: users/templates/users/_granted_assets.html:29 #: users/templates/users/_granted_assets.html:29
#: users/templates/users/user_asset_permission.html:44 #: users/templates/users/user_asset_permission.html:44
@ -112,7 +113,7 @@ msgid "Action"
msgstr "动作" msgstr "动作"
#: acls/models/login_acl.py:35 acls/models/login_asset_acl.py:32 #: acls/models/login_acl.py:35 acls/models/login_asset_acl.py:32
#: acls/serializers/login_acl.py:16 assets/models/cmd_filter.py:62 #: acls/serializers/login_acl.py:16 assets/models/cmd_filter.py:85
msgid "Reviewers" msgid "Reviewers"
msgstr "审批人" msgstr "审批人"
@ -129,9 +130,10 @@ msgstr "系统用户"
#: acls/models/login_asset_acl.py:22 #: acls/models/login_asset_acl.py:22
#: applications/serializers/attrs/application_category/remote_app.py:37 #: applications/serializers/attrs/application_category/remote_app.py:37
#: assets/models/asset.py:356 assets/models/authbook.py:18 #: assets/models/asset.py:356 assets/models/authbook.py:18
#: assets/models/gathered_user.py:14 assets/serializers/system_user.py:259 #: assets/models/cmd_filter.py:34 assets/models/gathered_user.py:14
#: audits/models.py:38 perms/models/asset_permission.py:99 #: assets/serializers/system_user.py:259 audits/models.py:38
#: templates/index.html:82 terminal/backends/command/models.py:19 #: perms/models/asset_permission.py:99 templates/index.html:82
#: terminal/backends/command/models.py:19
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:41 #: terminal/backends/command/serializers.py:13 terminal/models/session.py:41
#: terminal/notifications.py:89 #: terminal/notifications.py:89
#: users/templates/users/user_asset_permission.html:40 #: users/templates/users/user_asset_permission.html:40
@ -263,7 +265,7 @@ msgid "Custom"
msgstr "自定义" msgstr "自定义"
#: applications/models/account.py:11 assets/models/authbook.py:19 #: applications/models/account.py:11 assets/models/authbook.py:19
#: assets/models/user.py:292 audits/models.py:39 #: assets/models/cmd_filter.py:38 assets/models/user.py:291 audits/models.py:39
#: perms/models/application_permission.py:32 #: perms/models/application_permission.py:32
#: 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
@ -303,7 +305,7 @@ msgid "Category"
msgstr "类别" msgstr "类别"
#: applications/models/application.py:171 #: applications/models/application.py:171
#: applications/serializers/application.py:90 assets/models/cmd_filter.py:53 #: applications/serializers/application.py:90 assets/models/cmd_filter.py:76
#: assets/models/user.py:199 perms/models/application_permission.py:23 #: assets/models/user.py:199 perms/models/application_permission.py:23
#: perms/serializers/application/user_permission.py:34 #: perms/serializers/application/user_permission.py:34
#: terminal/models/storage.py:55 terminal/models/storage.py:116 #: terminal/models/storage.py:55 terminal/models/storage.py:116
@ -323,7 +325,7 @@ msgstr "网域"
msgid "Attrs" msgid "Attrs"
msgstr "" msgstr ""
#: applications/models/application.py:183 #: applications/models/application.py:183 assets/models/cmd_filter.py:41
#: perms/models/application_permission.py:27 users/models/user.py:170 #: perms/models/application_permission.py:27 users/models/user.py:170
msgid "Application" msgid "Application"
msgstr "应用程序" msgstr "应用程序"
@ -528,13 +530,13 @@ msgstr "协议组"
msgid "Nodes" msgid "Nodes"
msgstr "节点" msgstr "节点"
#: assets/models/asset.py:220 assets/models/cmd_filter.py:22 #: assets/models/asset.py:220 assets/models/cmd_filter.py:43
#: assets/models/domain.py:66 assets/models/label.py:22 #: assets/models/domain.py:66 assets/models/label.py:22
msgid "Is active" msgid "Is active"
msgstr "激活" msgstr "激活"
#: assets/models/asset.py:223 assets/models/cluster.py:19 #: assets/models/asset.py:223 assets/models/cluster.py:19
#: assets/models/user.py:188 assets/models/user.py:341 templates/_nav.html:44 #: assets/models/user.py:188 assets/models/user.py:340 templates/_nav.html:44
msgid "Admin user" msgid "Admin user"
msgstr "特权用户" msgstr "特权用户"
@ -551,8 +553,8 @@ msgid "Labels"
msgstr "标签管理" msgstr "标签管理"
#: assets/models/asset.py:230 assets/models/base.py:183 #: assets/models/asset.py:230 assets/models/base.py:183
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:48
#: assets/models/cmd_filter.py:67 assets/models/group.py:21 #: assets/models/cmd_filter.py:90 assets/models/group.py:21
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25 #: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:25
#: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:591 #: orgs/models.py:437 perms/models/base.py:51 users/models/user.py:591
#: users/serializers/group.py:33 #: users/serializers/group.py:33
@ -656,47 +658,61 @@ msgstr "系统"
msgid "Default Cluster" msgid "Default Cluster"
msgstr "默认Cluster" msgstr "默认Cluster"
#: assets/models/cmd_filter.py:33 assets/models/user.py:206 #: assets/models/cmd_filter.py:30 perms/models/base.py:47
#: templates/_nav.html:21 users/models/group.py:31 users/models/user.py:553
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_asset_permission.html:39
#: users/templates/users/user_asset_permission.html:67
#: users/templates/users/user_database_app_permission.html:38
#: users/templates/users/user_database_app_permission.html:61
msgid "User group"
msgstr "用户组"
#: assets/models/cmd_filter.py:56
msgid "Command filter" msgid "Command filter"
msgstr "命令过滤器" msgstr "命令过滤器"
#: assets/models/cmd_filter.py:40 #: assets/models/cmd_filter.py:63
msgid "Regex" msgid "Regex"
msgstr "正则表达式" msgstr "正则表达式"
#: assets/models/cmd_filter.py:41 ops/models/command.py:25 #: assets/models/cmd_filter.py:64 ops/models/command.py:25
#: terminal/backends/command/serializers.py:15 terminal/models/session.py:50 #: terminal/backends/command/serializers.py:15 terminal/models/session.py:50
#: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_alert.html:12
#: terminal/templates/terminal/_msg_command_execute_alert.html:10 #: terminal/templates/terminal/_msg_command_execute_alert.html:10
msgid "Command" msgid "Command"
msgstr "命令" msgstr "命令"
#: assets/models/cmd_filter.py:47 #: assets/models/cmd_filter.py:70
msgid "Deny" msgid "Deny"
msgstr "拒绝" msgstr "拒绝"
#: assets/models/cmd_filter.py:49 #: assets/models/cmd_filter.py:72
msgid "Reconfirm" msgid "Reconfirm"
msgstr "复核" msgstr "复核"
#: assets/models/cmd_filter.py:52 #: assets/models/cmd_filter.py:75
msgid "Filter" msgid "Filter"
msgstr "过滤器" msgstr "过滤器"
#: assets/models/cmd_filter.py:56 settings/serializers/basic.py:10 #: assets/models/cmd_filter.py:79 settings/serializers/basic.py:10
#: xpack/plugins/license/models.py:29 #: xpack/plugins/license/models.py:29
msgid "Content" msgid "Content"
msgstr "内容" msgstr "内容"
#: assets/models/cmd_filter.py:56 #: assets/models/cmd_filter.py:79
msgid "One line one command" msgid "One line one command"
msgstr "每行一个命令" msgstr "每行一个命令"
#: assets/models/cmd_filter.py:71 #: assets/models/cmd_filter.py:94
msgid "Command filter rule" msgid "Command filter rule"
msgstr "命令过滤规则" msgstr "命令过滤规则"
#: assets/models/cmd_filter.py:119 tickets/const.py:13 #: assets/models/cmd_filter.py:132
msgid "The generated regular expression is incorrect: {}"
msgstr "生成的正则表达式有误"
#: assets/models/cmd_filter.py:158 tickets/const.py:13
msgid "Command confirm" msgid "Command confirm"
msgstr "命令复核" msgstr "命令复核"
@ -821,27 +837,27 @@ msgstr "Shell"
msgid "Login mode" msgid "Login mode"
msgstr "认证方式" msgstr "认证方式"
#: assets/models/user.py:207 #: assets/models/user.py:206
msgid "SFTP Root" msgid "SFTP Root"
msgstr "SFTP根路径" msgstr "SFTP根路径"
#: assets/models/user.py:208 authentication/models.py:45 #: assets/models/user.py:207 authentication/models.py:45
msgid "Token" msgid "Token"
msgstr "" msgstr ""
#: assets/models/user.py:209 #: assets/models/user.py:208
msgid "Home" msgid "Home"
msgstr "家目录" msgstr "家目录"
#: assets/models/user.py:210 #: assets/models/user.py:209
msgid "System groups" msgid "System groups"
msgstr "用户组" msgstr "用户组"
#: assets/models/user.py:213 #: assets/models/user.py:212
msgid "User switch" msgid "User switch"
msgstr "用户切换" msgstr "用户切换"
#: assets/models/user.py:214 #: assets/models/user.py:213
msgid "Switch from" msgid "Switch from"
msgstr "切换自" msgstr "切换自"
@ -1503,7 +1519,7 @@ msgstr "{ApplicationPermission} 添加 {SystemUser}"
msgid "{ApplicationPermission} REMOVE {SystemUser}" msgid "{ApplicationPermission} REMOVE {SystemUser}"
msgstr "{ApplicationPermission} 移除 {SystemUser}" msgstr "{ApplicationPermission} 移除 {SystemUser}"
#: authentication/api/connection_token.py:259 #: authentication/api/connection_token.py:285
msgid "Invalid token" msgid "Invalid token"
msgstr "无效的令牌" msgstr "无效的令牌"
@ -2638,15 +2654,6 @@ msgstr "未分组"
msgid "Favorite" msgid "Favorite"
msgstr "收藏夹" msgstr "收藏夹"
#: perms/models/base.py:47 templates/_nav.html:21 users/models/group.py:31
#: users/models/user.py:553 users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_asset_permission.html:39
#: users/templates/users/user_asset_permission.html:67
#: users/templates/users/user_database_app_permission.html:38
#: users/templates/users/user_database_app_permission.html:61
msgid "User group"
msgstr "用户组"
#: perms/models/base.py:50 #: perms/models/base.py:50
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:58 #: tickets/serializers/ticket/meta/ticket_type/apply_application.py:58
#: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:60 #: tickets/serializers/ticket/meta/ticket_type/apply_asset.py:60
@ -4179,19 +4186,19 @@ msgstr "macOS 需要下载客户端来连接 RDP 资产Windows 系统默认
msgid "Filters" msgid "Filters"
msgstr "过滤" msgstr "过滤"
#: terminal/api/session.py:185 #: terminal/api/session.py:189
msgid "Session does not exist: {}" msgid "Session does not exist: {}"
msgstr "会话不存在: {}" msgstr "会话不存在: {}"
#: terminal/api/session.py:188 #: terminal/api/session.py:192
msgid "Session is finished or the protocol not supported" msgid "Session is finished or the protocol not supported"
msgstr "会话已经完成或协议不支持" msgstr "会话已经完成或协议不支持"
#: terminal/api/session.py:193 #: terminal/api/session.py:197
msgid "User does not exist: {}" msgid "User does not exist: {}"
msgstr "用户不存在: {}" msgstr "用户不存在: {}"
#: terminal/api/session.py:197 #: terminal/api/session.py:201
msgid "User does not have permission" msgid "User does not have permission"
msgstr "用户没有权限" msgstr "用户没有权限"
@ -5780,10 +5787,8 @@ msgid "Qingyun Private Cloud"
msgstr "青云私有云" msgstr "青云私有云"
#: xpack/plugins/cloud/const.py:19 #: xpack/plugins/cloud/const.py:19
#, fuzzy
#| msgid "Tencent Cloud"
msgid "OpenStack Cloud" msgid "OpenStack Cloud"
msgstr "腾讯云" msgstr "OpenStack Cloud"
#: xpack/plugins/cloud/const.py:20 #: xpack/plugins/cloud/const.py:20
msgid "Google Cloud Platform" msgid "Google Cloud Platform"
@ -6063,16 +6068,12 @@ msgid "API Endpoint"
msgstr "API 端点" msgstr "API 端点"
#: xpack/plugins/cloud/serializers/account_attrs.py:101 #: xpack/plugins/cloud/serializers/account_attrs.py:101
#, fuzzy msgid "Auth url"
#| msgid "Target url" msgstr "认证地址"
msgid "auth url"
msgstr "目标URL"
#: xpack/plugins/cloud/serializers/account_attrs.py:103 #: xpack/plugins/cloud/serializers/account_attrs.py:103
#, fuzzy msgid "User domain"
#| msgid "Domain name" msgstr "用户域"
msgid "user domain name"
msgstr "网域名称"
#: xpack/plugins/cloud/serializers/account_attrs.py:110 #: xpack/plugins/cloud/serializers/account_attrs.py:110
msgid "Service account key" msgid "Service account key"