mirror of https://github.com/jumpserver/jumpserver
commit
776234e8cc
|
@ -87,6 +87,9 @@ class SystemUser(BaseUser):
|
|||
(PROTOCOL_POSTGRESQL, 'postgresql'),
|
||||
(PROTOCOL_K8S, 'k8s'),
|
||||
)
|
||||
|
||||
SUPPORT_PUSH_PROTOCOLS = [PROTOCOL_SSH, PROTOCOL_RDP]
|
||||
|
||||
ASSET_CATEGORY_PROTOCOLS = [
|
||||
PROTOCOL_SSH, PROTOCOL_RDP, PROTOCOL_TELNET, PROTOCOL_VNC
|
||||
]
|
||||
|
@ -151,11 +154,15 @@ class SystemUser(BaseUser):
|
|||
return self.get_login_mode_display()
|
||||
|
||||
def is_need_push(self):
|
||||
if self.auto_push and self.protocol in [self.PROTOCOL_SSH, self.PROTOCOL_RDP]:
|
||||
if self.auto_push and self.is_protocol_support_push:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_protocol_support_push(self):
|
||||
return self.protocol in self.SUPPORT_PUSH_PROTOCOLS
|
||||
|
||||
@property
|
||||
def is_need_cmd_filter(self):
|
||||
return self.protocol not in [self.PROTOCOL_RDP, self.PROTOCOL_VNC]
|
||||
|
|
|
@ -193,7 +193,8 @@ def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs):
|
|||
systemuser_id=system_user_id,
|
||||
asset_id=asset_id
|
||||
))
|
||||
push_system_user_to_assets.delay(system_user_id, asset_ids_to_push)
|
||||
if asset_ids_to_push:
|
||||
push_system_user_to_assets.delay(system_user_id, asset_ids_to_push)
|
||||
m2m_model.objects.bulk_create(to_create)
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.db.models.signals import (
|
|||
m2m_changed
|
||||
)
|
||||
|
||||
from orgs.utils import ensure_in_real_or_default_org
|
||||
from orgs.utils import ensure_in_real_or_default_org, tmp_to_org
|
||||
from common.const.signals import PRE_ADD, POST_REMOVE, PRE_CLEAR
|
||||
from common.utils import get_logger
|
||||
from assets.models import Asset, Node, compute_parent_key
|
||||
|
@ -34,15 +34,16 @@ def on_node_asset_change(sender, action, instance, reverse, pk_set, **kwargs):
|
|||
|
||||
operator = mapper[action]
|
||||
|
||||
if reverse:
|
||||
node: Node = instance
|
||||
asset_pk_set = set(pk_set)
|
||||
NodeAssetsAmountUtils.update_node_assets_amount(node, asset_pk_set, operator)
|
||||
else:
|
||||
asset_pk = instance.id
|
||||
# 与资产直接关联的节点
|
||||
node_keys = set(Node.objects.filter(id__in=pk_set).values_list('key', flat=True))
|
||||
NodeAssetsAmountUtils.update_nodes_asset_amount(node_keys, asset_pk, operator)
|
||||
with tmp_to_org(instance.org):
|
||||
if reverse:
|
||||
node: Node = instance
|
||||
asset_pk_set = set(pk_set)
|
||||
NodeAssetsAmountUtils.update_node_assets_amount(node, asset_pk_set, operator)
|
||||
else:
|
||||
asset_pk = instance.id
|
||||
# 与资产直接关联的节点
|
||||
node_keys = set(Node.objects.filter(id__in=pk_set).values_list('key', flat=True))
|
||||
NodeAssetsAmountUtils.update_nodes_asset_amount(node_keys, asset_pk, operator)
|
||||
|
||||
|
||||
class NodeAssetsAmountUtils:
|
||||
|
|
|
@ -25,10 +25,13 @@ def check_asset_can_run_ansible(asset):
|
|||
|
||||
|
||||
def check_system_user_can_run_ansible(system_user):
|
||||
if not system_user.is_need_push():
|
||||
msg = _("Push system user task skip, auto push not enable or "
|
||||
"protocol is not ssh or rdp: {}").format(system_user.name)
|
||||
logger.info(msg)
|
||||
if not system_user.auto_push:
|
||||
logger.warn(f'Push system user task skip, auto push not enable: system_user={system_user.name}')
|
||||
return False
|
||||
if not system_user.is_protocol_support_push:
|
||||
logger.warn(f'Push system user task skip, protocol not support: '
|
||||
f'system_user={system_user.name} protocol={system_user.protocol} '
|
||||
f'support_protocol={system_user.SUPPORT_PUSH_PROTOCOLS}')
|
||||
return False
|
||||
|
||||
# Push root as system user is dangerous
|
||||
|
@ -37,10 +40,6 @@ def check_system_user_can_run_ansible(system_user):
|
|||
logger.info(msg)
|
||||
return False
|
||||
|
||||
# if system_user.protocol != "ssh":
|
||||
# msg = _("System user protocol not ssh: {}".format(system_user))
|
||||
# logger.info(msg)
|
||||
# return False
|
||||
return True
|
||||
|
||||
|
||||
|
|
|
@ -64,6 +64,9 @@ class SessionAuditSerializer(serializers.ModelSerializer):
|
|||
|
||||
class CommandExecutionSerializer(serializers.ModelSerializer):
|
||||
is_success = serializers.BooleanField(read_only=True, label=_('Is success'))
|
||||
hosts_display = serializers.ListSerializer(
|
||||
child=serializers.CharField(), source='hosts', read_only=True, label=_('Hosts for display')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CommandExecution
|
||||
|
@ -72,7 +75,7 @@ class CommandExecutionSerializer(serializers.ModelSerializer):
|
|||
'run_as', 'command', 'user', 'is_finished',
|
||||
'date_start', 'result', 'is_success', 'org_id'
|
||||
]
|
||||
fields = fields_small + ['hosts', 'run_as_display', 'user_display']
|
||||
fields = fields_small + ['hosts', 'hosts_display', 'run_as_display', 'user_display']
|
||||
extra_kwargs = {
|
||||
'result': {'label': _('Result')}, # model 上的方法,只能在这修改
|
||||
'is_success': {'label': _('Is success')},
|
||||
|
|
|
@ -68,6 +68,7 @@ class LoginConfirmSetting(CommonModelMixin):
|
|||
def create_confirm_ticket(self, request=None):
|
||||
from tickets import const
|
||||
from tickets.models import Ticket
|
||||
from orgs.models import Organization
|
||||
ticket_title = _('Login confirm') + ' {}'.format(self.user)
|
||||
ticket_meta = self.construct_confirm_ticket_meta(request)
|
||||
ticket_assignees = self.reviewers.all()
|
||||
|
@ -75,6 +76,7 @@ class LoginConfirmSetting(CommonModelMixin):
|
|||
'title': ticket_title,
|
||||
'type': const.TicketTypeChoices.login_confirm.value,
|
||||
'meta': ticket_meta,
|
||||
'org_id': Organization.ROOT_ID,
|
||||
}
|
||||
ticket = Ticket.objects.create(**data)
|
||||
ticket.assignees.set(ticket_assignees)
|
||||
|
|
|
@ -170,7 +170,7 @@ class UserLoginWaitConfirmView(TemplateView):
|
|||
if not ticket_id:
|
||||
ticket = None
|
||||
else:
|
||||
ticket = get_object_or_none(Ticket, pk=ticket_id)
|
||||
ticket = Ticket.all().filter(pk=ticket_id).first()
|
||||
context = super().get_context_data(**kwargs)
|
||||
if ticket:
|
||||
timestamp_created = datetime.datetime.timestamp(ticket.date_created)
|
||||
|
|
|
@ -283,7 +283,7 @@ class Config(dict):
|
|||
'SERVER_REPLAY_STORAGE': {},
|
||||
'CONNECTION_TOKEN_ENABLED': False,
|
||||
'ONLY_ALLOW_EXIST_USER_AUTH': False,
|
||||
'ONLY_ALLOW_AUTH_FROM_SOURCE': True,
|
||||
'ONLY_ALLOW_AUTH_FROM_SOURCE': False,
|
||||
'DISK_CHECK_ENABLED': True,
|
||||
'SESSION_SAVE_EVERY_REQUEST': True,
|
||||
'SESSION_EXPIRE_AT_BROWSER_CLOSE_FORCE': False,
|
||||
|
|
|
@ -12,7 +12,7 @@ XPACK_CONTEXT_PROCESSOR = []
|
|||
|
||||
if XPACK_ENABLED:
|
||||
from xpack.utils import get_xpack_templates_dir, get_xpack_context_processor
|
||||
INSTALLED_APPS.append('xpack.apps.XpackConfig')
|
||||
INSTALLED_APPS.insert(0, 'xpack.apps.XpackConfig')
|
||||
XPACK_TEMPLATES_DIR = get_xpack_templates_dir(const.BASE_DIR)
|
||||
XPACK_CONTEXT_PROCESSOR = get_xpack_context_processor()
|
||||
TEMPLATES[0]['DIRS'].extend(XPACK_TEMPLATES_DIR)
|
||||
|
|
|
@ -42,13 +42,13 @@ INSTALLED_APPS = [
|
|||
'perms.apps.PermsConfig',
|
||||
'ops.apps.OpsConfig',
|
||||
'settings.apps.SettingsConfig',
|
||||
'common.apps.CommonConfig',
|
||||
'terminal.apps.TerminalConfig',
|
||||
'audits.apps.AuditsConfig',
|
||||
'authentication.apps.AuthenticationConfig', # authentication
|
||||
'applications.apps.ApplicationsConfig',
|
||||
'tickets.apps.TicketsConfig',
|
||||
'acls.apps.AclsConfig',
|
||||
'common.apps.CommonConfig',
|
||||
'jms_oidc_rp',
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#
|
||||
from functools import partial
|
||||
from werkzeug.local import LocalProxy
|
||||
|
||||
from django.conf import settings
|
||||
from common.local import thread_local
|
||||
|
||||
|
||||
|
@ -17,4 +19,11 @@ def get_current_request():
|
|||
return _find('current_request')
|
||||
|
||||
|
||||
def has_valid_xpack_license():
|
||||
if not settings.XPACK_ENABLED:
|
||||
return False
|
||||
from xpack.plugins.license.models import License
|
||||
return License.has_valid_license()
|
||||
|
||||
|
||||
current_request = LocalProxy(partial(_find, 'current_request'))
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-03-11 17:54+0800\n"
|
||||
"POT-Creation-Date: 2021-03-17 18:17+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -35,7 +35,7 @@ msgstr ""
|
|||
#: assets/models/cmd_filter.py:21 assets/models/domain.py:21
|
||||
#: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
|
||||
#: orgs/models.py:23 perms/models/base.py:49 settings/models.py:29
|
||||
#: terminal/models/storage.py:23 terminal/models/storage.py:75
|
||||
#: terminal/models/storage.py:23 terminal/models/storage.py:81
|
||||
#: terminal/models/task.py:16 terminal/models/terminal.py:139
|
||||
#: users/forms/profile.py:32 users/models/group.py:15 users/models/user.py:530
|
||||
#: users/templates/users/_select_user_modal.html:13
|
||||
|
@ -56,12 +56,12 @@ msgid "Name"
|
|||
msgstr "名称"
|
||||
|
||||
#: acls/models/base.py:27 assets/models/cmd_filter.py:53
|
||||
#: assets/models/user.py:119
|
||||
#: assets/models/user.py:122
|
||||
msgid "Priority"
|
||||
msgstr "优先级"
|
||||
|
||||
#: acls/models/base.py:28 assets/models/cmd_filter.py:53
|
||||
#: assets/models/user.py:119
|
||||
#: assets/models/user.py:122
|
||||
msgid "1-100, the lower the value will be match first"
|
||||
msgstr "优先级可选范围为 1-100 (数值越小越优先)"
|
||||
|
||||
|
@ -82,7 +82,7 @@ msgstr "激活中"
|
|||
#: assets/models/domain.py:22 assets/models/domain.py:56
|
||||
#: assets/models/group.py:23 assets/models/label.py:23 ops/models/adhoc.py:37
|
||||
#: orgs/models.py:26 perms/models/base.py:57 settings/models.py:34
|
||||
#: terminal/models/storage.py:29 terminal/models/storage.py:81
|
||||
#: terminal/models/storage.py:29 terminal/models/storage.py:87
|
||||
#: terminal/models/terminal.py:153 tickets/models/ticket.py:73
|
||||
#: users/models/group.py:16 users/models/user.py:563
|
||||
#: users/templates/users/user_detail.html:115
|
||||
|
@ -125,13 +125,13 @@ msgstr "动作"
|
|||
#: acls/models/login_acl.py:28 acls/models/login_asset_acl.py:20
|
||||
#: acls/serializers/login_acl.py:28 assets/models/label.py:15
|
||||
#: audits/models.py:36 audits/models.py:56 audits/models.py:69
|
||||
#: audits/serializers.py:81 authentication/models.py:44
|
||||
#: authentication/models.py:95 orgs/models.py:18 orgs/models.py:403
|
||||
#: audits/serializers.py:84 authentication/models.py:44
|
||||
#: authentication/models.py:97 orgs/models.py:18 orgs/models.py:417
|
||||
#: perms/models/base.py:50 templates/index.html:78
|
||||
#: terminal/backends/command/models.py:18
|
||||
#: terminal/backends/command/serializers.py:12 terminal/models/session.py:37
|
||||
#: tickets/models/comment.py:17 users/models/user.py:159
|
||||
#: users/models/user.py:677 users/serializers/group.py:20
|
||||
#: users/models/user.py:699 users/serializers/group.py:20
|
||||
#: users/templates/users/user_asset_permission.html:38
|
||||
#: users/templates/users/user_asset_permission.html:64
|
||||
#: users/templates/users/user_database_app_permission.html:37
|
||||
|
@ -144,7 +144,7 @@ msgstr "动作"
|
|||
msgid "User"
|
||||
msgstr "用户"
|
||||
|
||||
#: acls/models/login_asset_acl.py:17 authentication/models.py:71
|
||||
#: acls/models/login_asset_acl.py:17 authentication/models.py:72
|
||||
#: tickets/const.py:9 users/templates/users/user_detail.html:250
|
||||
msgid "Login confirm"
|
||||
msgstr "登录复核"
|
||||
|
@ -156,9 +156,9 @@ msgstr "系统用户"
|
|||
#: acls/models/login_asset_acl.py:22
|
||||
#: applications/serializers/attrs/application_category/remote_app.py:33
|
||||
#: assets/models/asset.py:355 assets/models/authbook.py:26
|
||||
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:29
|
||||
#: assets/models/gathered_user.py:14 assets/serializers/admin_user.py:30
|
||||
#: assets/serializers/asset_user.py:47 assets/serializers/asset_user.py:84
|
||||
#: assets/serializers/system_user.py:191 audits/models.py:38
|
||||
#: assets/serializers/system_user.py:192 audits/models.py:38
|
||||
#: perms/models/asset_permission.py:99 templates/index.html:82
|
||||
#: terminal/backends/command/models.py:19
|
||||
#: terminal/backends/command/serializers.py:13 terminal/models/session.py:39
|
||||
|
@ -192,7 +192,7 @@ msgstr "IP 地址无效: `{}`"
|
|||
msgid "IP"
|
||||
msgstr "IP"
|
||||
|
||||
#: acls/serializers/login_acl.py:41
|
||||
#: acls/serializers/login_acl.py:45
|
||||
msgid "The user `{}` is not in the current organization: `{}`"
|
||||
msgstr "用户 `{}` 不在当前组织: `{}`"
|
||||
|
||||
|
@ -228,7 +228,7 @@ msgid "Hostname"
|
|||
msgstr "主机名"
|
||||
|
||||
#: acls/serializers/login_asset_acl.py:41 assets/models/asset.py:187
|
||||
#: assets/models/domain.py:54 assets/models/user.py:120
|
||||
#: assets/models/domain.py:54 assets/models/user.py:123
|
||||
#: terminal/serializers/session.py:29 terminal/serializers/storage.py:69
|
||||
msgid "Protocol"
|
||||
msgstr "协议"
|
||||
|
@ -241,12 +241,12 @@ msgstr "协议选项: {}"
|
|||
msgid "Unsupported protocols: {}"
|
||||
msgstr "不支持的协议: {}"
|
||||
|
||||
#: acls/serializers/login_asset_acl.py:78
|
||||
#: acls/serializers/login_asset_acl.py:80
|
||||
#: tickets/serializers/ticket/ticket.py:109
|
||||
msgid "The organization `{}` does not exist"
|
||||
msgstr "组织 `{}` 不存在"
|
||||
|
||||
#: acls/serializers/login_asset_acl.py:83
|
||||
#: acls/serializers/login_asset_acl.py:85
|
||||
msgid "None of the reviewers belong to Organization `{}`"
|
||||
msgstr "所有复核人都不属于组织 `{}`"
|
||||
|
||||
|
@ -279,7 +279,7 @@ msgstr "类别"
|
|||
#: perms/models/application_permission.py:23
|
||||
#: perms/serializers/application/permission.py:17
|
||||
#: perms/serializers/application/user_permission.py:34
|
||||
#: terminal/models/storage.py:26 terminal/models/storage.py:78
|
||||
#: terminal/models/storage.py:26 terminal/models/storage.py:84
|
||||
#: tickets/models/ticket.py:38
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:27
|
||||
#: users/templates/users/user_granted_database_app.html:35
|
||||
|
@ -335,7 +335,7 @@ msgstr "目标URL"
|
|||
#: applications/serializers/attrs/application_type/vmware_client.py:30
|
||||
#: assets/models/base.py:252 assets/serializers/asset_user.py:71
|
||||
#: audits/signals_handler.py:46 authentication/forms.py:22
|
||||
#: authentication/templates/authentication/login.html:148
|
||||
#: authentication/templates/authentication/login.html:155
|
||||
#: settings/serializers/settings.py:89 users/forms/profile.py:21
|
||||
#: users/templates/users/user_otp_check_password.html:13
|
||||
#: users/templates/users/user_password_update.html:43
|
||||
|
@ -394,7 +394,8 @@ msgstr "基础"
|
|||
msgid "Charset"
|
||||
msgstr "编码"
|
||||
|
||||
#: assets/models/asset.py:145 tickets/models/ticket.py:40
|
||||
#: assets/models/asset.py:145 assets/serializers/asset.py:171
|
||||
#: tickets/models/ticket.py:40
|
||||
msgid "Meta"
|
||||
msgstr "元数据"
|
||||
|
||||
|
@ -412,7 +413,7 @@ msgstr "系统平台"
|
|||
msgid "Protocols"
|
||||
msgstr "协议组"
|
||||
|
||||
#: assets/models/asset.py:192 assets/models/user.py:115
|
||||
#: assets/models/asset.py:192 assets/models/user.py:118
|
||||
#: perms/models/asset_permission.py:100
|
||||
#: xpack/plugins/change_auth_plan/models.py:56
|
||||
#: xpack/plugins/gathered_user/models.py:24
|
||||
|
@ -503,7 +504,7 @@ msgstr "标签管理"
|
|||
#: assets/models/cluster.py:28 assets/models/cmd_filter.py:26
|
||||
#: assets/models/cmd_filter.py:60 assets/models/group.py:21
|
||||
#: common/db/models.py:70 common/mixins/models.py:49 orgs/models.py:24
|
||||
#: orgs/models.py:407 perms/models/base.py:55 users/models/user.py:571
|
||||
#: orgs/models.py:421 perms/models/base.py:55 users/models/user.py:571
|
||||
#: users/serializers/group.py:35 users/templates/users/user_detail.html:97
|
||||
#: xpack/plugins/change_auth_plan/models.py:81
|
||||
#: xpack/plugins/cloud/models.py:104 xpack/plugins/gathered_user/models.py:30
|
||||
|
@ -517,7 +518,7 @@ msgstr "创建者"
|
|||
#: assets/models/gathered_user.py:19 assets/models/group.py:22
|
||||
#: assets/models/label.py:25 common/db/models.py:72 common/mixins/models.py:50
|
||||
#: ops/models/adhoc.py:38 ops/models/command.py:29 orgs/models.py:25
|
||||
#: orgs/models.py:405 perms/models/base.py:56 users/models/group.py:18
|
||||
#: orgs/models.py:419 perms/models/base.py:56 users/models/group.py:18
|
||||
#: users/templates/users/user_group_detail.html:58
|
||||
#: xpack/plugins/cloud/models.py:107
|
||||
msgid "Date created"
|
||||
|
@ -553,7 +554,7 @@ msgstr "SSH公钥"
|
|||
|
||||
#: assets/models/base.py:257 assets/models/gathered_user.py:20
|
||||
#: common/db/models.py:73 common/mixins/models.py:51 ops/models/adhoc.py:39
|
||||
#: orgs/models.py:406
|
||||
#: orgs/models.py:420
|
||||
msgid "Date updated"
|
||||
msgstr "更新日期"
|
||||
|
||||
|
@ -591,7 +592,7 @@ msgid "Default"
|
|||
msgstr "默认"
|
||||
|
||||
#: assets/models/cluster.py:36 assets/models/label.py:14
|
||||
#: users/models/user.py:689
|
||||
#: users/models/user.py:711
|
||||
msgid "System"
|
||||
msgstr "系统"
|
||||
|
||||
|
@ -599,7 +600,7 @@ msgstr "系统"
|
|||
msgid "Default Cluster"
|
||||
msgstr "默认Cluster"
|
||||
|
||||
#: assets/models/cmd_filter.py:33 assets/models/user.py:125
|
||||
#: assets/models/cmd_filter.py:33 assets/models/user.py:128
|
||||
msgid "Command filter"
|
||||
msgstr "命令过滤器"
|
||||
|
||||
|
@ -664,7 +665,7 @@ msgstr "资产组"
|
|||
msgid "Default asset group"
|
||||
msgstr "默认资产组"
|
||||
|
||||
#: assets/models/label.py:19 assets/models/node.py:575 settings/models.py:30
|
||||
#: assets/models/label.py:19 assets/models/node.py:538 settings/models.py:30
|
||||
msgid "Value"
|
||||
msgstr "值"
|
||||
|
||||
|
@ -672,23 +673,23 @@ msgstr "值"
|
|||
msgid "New node"
|
||||
msgstr "新节点"
|
||||
|
||||
#: assets/models/node.py:467 users/templates/users/_granted_assets.html:130
|
||||
#: assets/models/node.py:466 users/templates/users/_granted_assets.html:130
|
||||
msgid "empty"
|
||||
msgstr "空"
|
||||
|
||||
#: assets/models/node.py:574 perms/models/asset_permission.py:156
|
||||
#: assets/models/node.py:537 perms/models/asset_permission.py:156
|
||||
msgid "Key"
|
||||
msgstr "键"
|
||||
|
||||
#: assets/models/node.py:576
|
||||
#: assets/models/node.py:539
|
||||
msgid "Full value"
|
||||
msgstr "全称"
|
||||
|
||||
#: assets/models/node.py:579 perms/models/asset_permission.py:157
|
||||
#: assets/models/node.py:542 perms/models/asset_permission.py:157
|
||||
msgid "Parent key"
|
||||
msgstr "ssh私钥"
|
||||
|
||||
#: assets/models/node.py:588 assets/serializers/system_user.py:190
|
||||
#: assets/models/node.py:551 assets/serializers/system_user.py:191
|
||||
#: users/templates/users/user_asset_permission.html:41
|
||||
#: users/templates/users/user_asset_permission.html:73
|
||||
#: users/templates/users/user_asset_permission.html:158
|
||||
|
@ -696,66 +697,66 @@ msgstr "ssh私钥"
|
|||
msgid "Node"
|
||||
msgstr "节点"
|
||||
|
||||
#: assets/models/user.py:111
|
||||
#: assets/models/user.py:114
|
||||
msgid "Automatic login"
|
||||
msgstr "自动登录"
|
||||
|
||||
#: assets/models/user.py:112
|
||||
#: assets/models/user.py:115
|
||||
msgid "Manually login"
|
||||
msgstr "手动登录"
|
||||
|
||||
#: assets/models/user.py:114
|
||||
#: assets/models/user.py:117
|
||||
msgid "Username same with user"
|
||||
msgstr "用户名与用户相同"
|
||||
|
||||
#: assets/models/user.py:116 assets/serializers/domain.py:30
|
||||
#: assets/models/user.py:119 assets/serializers/domain.py:30
|
||||
#: templates/_nav.html:39 xpack/plugins/change_auth_plan/models.py:52
|
||||
msgid "Assets"
|
||||
msgstr "资产"
|
||||
|
||||
#: assets/models/user.py:117 templates/_nav.html:17
|
||||
#: assets/models/user.py:120 templates/_nav.html:17
|
||||
#: users/views/profile/password.py:42 users/views/profile/pubkey.py:36
|
||||
msgid "Users"
|
||||
msgstr "用户管理"
|
||||
|
||||
#: assets/models/user.py:118 users/templates/users/user_group_list.html:90
|
||||
#: assets/models/user.py:121 users/templates/users/user_group_list.html:90
|
||||
#: users/templates/users/user_profile.html:124
|
||||
msgid "User groups"
|
||||
msgstr "用户组"
|
||||
|
||||
#: assets/models/user.py:121
|
||||
#: assets/models/user.py:124
|
||||
msgid "Auto push"
|
||||
msgstr "自动推送"
|
||||
|
||||
#: assets/models/user.py:122
|
||||
#: assets/models/user.py:125
|
||||
msgid "Sudo"
|
||||
msgstr "Sudo"
|
||||
|
||||
#: assets/models/user.py:123
|
||||
#: assets/models/user.py:126
|
||||
msgid "Shell"
|
||||
msgstr "Shell"
|
||||
|
||||
#: assets/models/user.py:124
|
||||
#: assets/models/user.py:127
|
||||
msgid "Login mode"
|
||||
msgstr "登录模式"
|
||||
|
||||
#: assets/models/user.py:126
|
||||
#: assets/models/user.py:129
|
||||
msgid "SFTP Root"
|
||||
msgstr "SFTP根路径"
|
||||
|
||||
#: assets/models/user.py:127 authentication/models.py:93
|
||||
#: assets/models/user.py:130 authentication/models.py:95
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
#: assets/models/user.py:128
|
||||
#: assets/models/user.py:131
|
||||
msgid "Home"
|
||||
msgstr "家目录"
|
||||
|
||||
#: assets/models/user.py:129
|
||||
#: assets/models/user.py:132
|
||||
msgid "System groups"
|
||||
msgstr "用户组"
|
||||
|
||||
#: assets/models/user.py:221 audits/models.py:39
|
||||
#: assets/models/user.py:228 audits/models.py:39
|
||||
#: perms/models/application_permission.py:31
|
||||
#: perms/models/asset_permission.py:101 templates/_nav.html:45
|
||||
#: terminal/backends/command/models.py:20
|
||||
|
@ -858,6 +859,17 @@ msgstr "应用数量"
|
|||
msgid "Gateways count"
|
||||
msgstr "网关数量"
|
||||
|
||||
#: assets/serializers/label.py:13 assets/serializers/system_user.py:45
|
||||
#: assets/serializers/system_user.py:166
|
||||
#: perms/serializers/asset/permission.py:66
|
||||
msgid "Assets amount"
|
||||
msgstr "资产数量"
|
||||
|
||||
#: assets/serializers/label.py:14
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:24
|
||||
msgid "Category display"
|
||||
msgstr "类别 (显示名称)"
|
||||
|
||||
#: assets/serializers/node.py:18
|
||||
msgid "value"
|
||||
msgstr "值"
|
||||
|
@ -870,22 +882,17 @@ msgstr "不能包含: /"
|
|||
msgid "The same level node name cannot be the same"
|
||||
msgstr "同级别节点名字不能重复"
|
||||
|
||||
#: assets/serializers/system_user.py:44 assets/serializers/system_user.py:164
|
||||
#: assets/serializers/system_user.py:44 assets/serializers/system_user.py:165
|
||||
#: perms/serializers/asset/permission.py:67
|
||||
msgid "Nodes amount"
|
||||
msgstr "节点数量"
|
||||
|
||||
#: assets/serializers/system_user.py:45 assets/serializers/system_user.py:165
|
||||
#: perms/serializers/asset/permission.py:66
|
||||
msgid "Assets amount"
|
||||
msgstr "资产数量"
|
||||
|
||||
#: assets/serializers/system_user.py:46 assets/serializers/system_user.py:166
|
||||
#: assets/serializers/system_user.py:192
|
||||
#: assets/serializers/system_user.py:46 assets/serializers/system_user.py:167
|
||||
#: assets/serializers/system_user.py:193
|
||||
msgid "Login mode display"
|
||||
msgstr "登录模式(显示名称)"
|
||||
|
||||
#: assets/serializers/system_user.py:48 assets/serializers/system_user.py:168
|
||||
#: assets/serializers/system_user.py:48 assets/serializers/system_user.py:169
|
||||
msgid "Ad domain"
|
||||
msgstr "Ad 网域"
|
||||
|
||||
|
@ -1008,17 +1015,11 @@ msgstr "资产已经被禁用, 跳过: {}"
|
|||
msgid "Asset may not be support ansible, skipped: {}"
|
||||
msgstr "资产或许不支持ansible, 跳过: {}"
|
||||
|
||||
#: assets/tasks/utils.py:29
|
||||
msgid ""
|
||||
"Push system user task skip, auto push not enable or protocol is not ssh or "
|
||||
"rdp: {}"
|
||||
msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}"
|
||||
|
||||
#: assets/tasks/utils.py:36
|
||||
#: assets/tasks/utils.py:39
|
||||
msgid "For security, do not push user {}"
|
||||
msgstr "为了安全,禁止推送用户 {}"
|
||||
|
||||
#: assets/tasks/utils.py:56
|
||||
#: assets/tasks/utils.py:55
|
||||
msgid "No assets matched, stop task"
|
||||
msgstr "没有匹配到资产,结束任务"
|
||||
|
||||
|
@ -1165,7 +1166,7 @@ msgstr "用户代理"
|
|||
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
||||
#: authentication/templates/authentication/login_otp.html:6
|
||||
#: users/forms/profile.py:64 users/models/user.py:552
|
||||
#: users/serializers/profile.py:102 users/templates/users/user_detail.html:77
|
||||
#: users/serializers/profile.py:99 users/templates/users/user_detail.html:77
|
||||
#: users/templates/users/user_profile.html:87
|
||||
msgid "MFA"
|
||||
msgstr "多因子认证"
|
||||
|
@ -1204,29 +1205,33 @@ msgstr "状态(显示名称)"
|
|||
msgid "MFA for display"
|
||||
msgstr "多因子认证状态(显示名称)"
|
||||
|
||||
#: audits/serializers.py:66 audits/serializers.py:78 ops/models/adhoc.py:247
|
||||
#: audits/serializers.py:66 audits/serializers.py:81 ops/models/adhoc.py:247
|
||||
#: terminal/serializers/session.py:34
|
||||
msgid "Is success"
|
||||
msgstr "是否成功"
|
||||
|
||||
#: audits/serializers.py:77 ops/models/command.py:26
|
||||
#: audits/serializers.py:68
|
||||
msgid "Hosts for display"
|
||||
msgstr "主机 (显示名称)"
|
||||
|
||||
#: audits/serializers.py:80 ops/models/command.py:26
|
||||
#: xpack/plugins/cloud/models.py:155
|
||||
msgid "Result"
|
||||
msgstr "结果"
|
||||
|
||||
#: audits/serializers.py:79 terminal/serializers/storage.py:178
|
||||
#: audits/serializers.py:82 terminal/serializers/storage.py:178
|
||||
msgid "Hosts"
|
||||
msgstr "主机"
|
||||
|
||||
#: audits/serializers.py:80
|
||||
#: audits/serializers.py:83
|
||||
msgid "Run as"
|
||||
msgstr "运行用户"
|
||||
|
||||
#: audits/serializers.py:82
|
||||
#: audits/serializers.py:85
|
||||
msgid "Run as for display"
|
||||
msgstr "运行用户(显示名称)"
|
||||
|
||||
#: audits/serializers.py:83
|
||||
#: audits/serializers.py:86
|
||||
msgid "User for display"
|
||||
msgstr "用户(显示名称)"
|
||||
|
||||
|
@ -1349,8 +1354,8 @@ msgid ""
|
|||
"after {} minutes)"
|
||||
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
|
||||
|
||||
#: authentication/errors.py:55 users/views/profile/otp.py:110
|
||||
#: users/views/profile/otp.py:149 users/views/profile/otp.py:169
|
||||
#: authentication/errors.py:55 users/views/profile/otp.py:107
|
||||
#: users/views/profile/otp.py:146 users/views/profile/otp.py:166
|
||||
msgid "MFA code invalid, or ntp sync server time"
|
||||
msgstr "MFA验证码不正确,或者服务器端时间不对"
|
||||
|
||||
|
@ -1399,7 +1404,7 @@ msgstr "多因子认证验证码"
|
|||
msgid "Private Token"
|
||||
msgstr "SSH密钥"
|
||||
|
||||
#: authentication/models.py:94
|
||||
#: authentication/models.py:96
|
||||
msgid "Expired"
|
||||
msgstr "过期时间"
|
||||
|
||||
|
@ -1429,7 +1434,7 @@ msgid "Show"
|
|||
msgstr "显示"
|
||||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:66
|
||||
#: users/models/user.py:445 users/serializers/profile.py:99
|
||||
#: users/models/user.py:445 users/serializers/profile.py:96
|
||||
#: users/templates/users/user_profile.html:94
|
||||
#: users/templates/users/user_profile.html:163
|
||||
#: users/templates/users/user_profile.html:166
|
||||
|
@ -1438,7 +1443,7 @@ msgid "Disable"
|
|||
msgstr "禁用"
|
||||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:67
|
||||
#: users/models/user.py:446 users/serializers/profile.py:100
|
||||
#: users/models/user.py:446 users/serializers/profile.py:97
|
||||
#: users/templates/users/user_profile.html:92
|
||||
#: users/templates/users/user_profile.html:170
|
||||
msgid "Enable"
|
||||
|
@ -1487,30 +1492,30 @@ msgstr "确认"
|
|||
msgid "Code error"
|
||||
msgstr "代码错误"
|
||||
|
||||
#: authentication/templates/authentication/login.html:141
|
||||
#: authentication/templates/authentication/login.html:148
|
||||
msgid "Welcome back, please enter username and password to login"
|
||||
msgstr "欢迎回来,请输入用户名和密码登录"
|
||||
|
||||
#: authentication/templates/authentication/login.html:167
|
||||
#: authentication/templates/authentication/login.html:174
|
||||
#: users/templates/users/forgot_password.html:15
|
||||
#: users/templates/users/forgot_password.html:16
|
||||
msgid "Forgot password"
|
||||
msgstr "忘记密码"
|
||||
|
||||
#: authentication/templates/authentication/login.html:174
|
||||
#: authentication/templates/authentication/login.html:181
|
||||
#: templates/_header_bar.html:83
|
||||
msgid "Login"
|
||||
msgstr "登录"
|
||||
|
||||
#: authentication/templates/authentication/login.html:181
|
||||
#: authentication/templates/authentication/login.html:188
|
||||
msgid "More login options"
|
||||
msgstr "更多登录方式"
|
||||
|
||||
#: authentication/templates/authentication/login.html:184
|
||||
#: authentication/templates/authentication/login.html:191
|
||||
msgid "OpenID"
|
||||
msgstr "OpenID"
|
||||
|
||||
#: authentication/templates/authentication/login.html:189
|
||||
#: authentication/templates/authentication/login.html:196
|
||||
msgid "CAS"
|
||||
msgstr ""
|
||||
|
||||
|
@ -1833,15 +1838,15 @@ msgstr "{} 任务结束"
|
|||
msgid "Date finished"
|
||||
msgstr "结束日期"
|
||||
|
||||
#: ops/models/command.py:74
|
||||
#: ops/models/command.py:78
|
||||
msgid "Task start"
|
||||
msgstr "任务开始"
|
||||
|
||||
#: ops/models/command.py:96
|
||||
#: ops/models/command.py:100
|
||||
msgid "Command `{}` is forbidden ........"
|
||||
msgstr "命令 `{}` 不允许被执行 ......."
|
||||
|
||||
#: ops/models/command.py:109
|
||||
#: ops/models/command.py:113
|
||||
msgid "Task end"
|
||||
msgstr "任务结束"
|
||||
|
||||
|
@ -1865,16 +1870,21 @@ msgstr "更新任务内容: {}"
|
|||
msgid "Disk used more than 80%: {} => {}"
|
||||
msgstr "磁盘使用率超过 80%: {} => {}"
|
||||
|
||||
#: orgs/api.py:64
|
||||
msgid "Organization contains undeleted resources"
|
||||
msgstr "组织包含未删除的资源"
|
||||
#: orgs/api.py:76
|
||||
#, python-brace-format
|
||||
msgid "Have `{model._meta.verbose_name}` exists, Please delete"
|
||||
msgstr "`{model._meta.verbose_name}` 存在数据, 请先删除"
|
||||
|
||||
#: orgs/api.py:68
|
||||
#: orgs/api.py:80
|
||||
msgid "The current organization cannot be deleted"
|
||||
msgstr "当前组织不能被删除"
|
||||
|
||||
#: orgs/mixins/api.py:46
|
||||
msgid "Root organization only allow view and delete"
|
||||
msgstr "全局组织仅支持 查看和删除"
|
||||
|
||||
#: orgs/mixins/models.py:45 orgs/mixins/serializers.py:25 orgs/models.py:36
|
||||
#: orgs/models.py:402 orgs/serializers.py:101
|
||||
#: orgs/models.py:416 orgs/serializers.py:101
|
||||
#: tickets/serializers/ticket/ticket.py:81
|
||||
msgid "Organization"
|
||||
msgstr "组织"
|
||||
|
@ -1891,7 +1901,7 @@ msgstr "组织审计员"
|
|||
msgid "GLOBAL"
|
||||
msgstr "全局组织"
|
||||
|
||||
#: orgs/models.py:404 users/models/user.py:540
|
||||
#: orgs/models.py:418 users/models/user.py:540
|
||||
#: users/templates/users/_select_user_modal.html:15
|
||||
#: users/templates/users/user_detail.html:73
|
||||
#: users/templates/users/user_list.html:16
|
||||
|
@ -1899,14 +1909,6 @@ msgstr "全局组织"
|
|||
msgid "Role"
|
||||
msgstr "角色"
|
||||
|
||||
#: perms/const.py:7 perms/models/asset_permission.py:189
|
||||
msgid "Ungrouped"
|
||||
msgstr "未分组"
|
||||
|
||||
#: perms/const.py:10
|
||||
msgid "Empty"
|
||||
msgstr "空"
|
||||
|
||||
#: perms/exceptions.py:9
|
||||
msgid "The administrator is modifying permissions. Please wait"
|
||||
msgstr "管理员正在修改授权,请稍等"
|
||||
|
@ -1965,6 +1967,10 @@ msgstr "动作"
|
|||
msgid "Asset permission"
|
||||
msgstr "资产授权"
|
||||
|
||||
#: perms/models/asset_permission.py:189
|
||||
msgid "Ungrouped"
|
||||
msgstr "未分组"
|
||||
|
||||
#: perms/models/asset_permission.py:191
|
||||
msgid "Favorite"
|
||||
msgstr "收藏夹"
|
||||
|
@ -1998,11 +2004,13 @@ msgid ""
|
|||
"permission type. ({})"
|
||||
msgstr "应用列表中包含与授权类型不同的应用。({})"
|
||||
|
||||
#: perms/serializers/asset/permission.py:61 users/serializers/user.py:67
|
||||
#: perms/serializers/asset/permission.py:43
|
||||
#: perms/serializers/asset/permission.py:61 users/serializers/user.py:34
|
||||
#: users/serializers/user.py:69
|
||||
msgid "Is expired"
|
||||
msgstr "是否过期"
|
||||
|
||||
#: perms/serializers/asset/permission.py:62 users/serializers/user.py:66
|
||||
#: perms/serializers/asset/permission.py:62 users/serializers/user.py:68
|
||||
msgid "Is valid"
|
||||
msgstr "账户是否有效"
|
||||
|
||||
|
@ -2018,11 +2026,11 @@ msgstr "用户组数量"
|
|||
msgid "System users amount"
|
||||
msgstr "系统用户数量"
|
||||
|
||||
#: settings/api/common.py:24
|
||||
#: settings/api/common.py:25
|
||||
msgid "Test mail sent to {}, please check"
|
||||
msgstr "邮件已经发送{}, 请检查"
|
||||
|
||||
#: settings/api/common.py:110 xpack/plugins/interface/api.py:18
|
||||
#: settings/api/common.py:100 xpack/plugins/interface/api.py:18
|
||||
#: xpack/plugins/interface/models.py:36
|
||||
msgid "Welcome to the JumpServer open source Bastion Host"
|
||||
msgstr "欢迎使用JumpServer开源堡垒机"
|
||||
|
@ -2910,19 +2918,23 @@ msgstr "不允许删除默认存储配置"
|
|||
msgid "Cannot delete storage that is being used"
|
||||
msgstr "不允许删除正在使用的存储配置"
|
||||
|
||||
#: terminal/api/storage.py:66 terminal/api/storage.py:67
|
||||
#: terminal/api/storage.py:72 terminal/api/storage.py:73
|
||||
msgid "Command storages"
|
||||
msgstr "命令存储"
|
||||
|
||||
#: terminal/api/storage.py:104
|
||||
#: terminal/api/storage.py:79
|
||||
msgid "Invalid"
|
||||
msgstr "无效"
|
||||
|
||||
#: terminal/api/storage.py:122
|
||||
msgid "Test failure: {}"
|
||||
msgstr "测试失败: {}"
|
||||
|
||||
#: terminal/api/storage.py:107
|
||||
#: terminal/api/storage.py:125
|
||||
msgid "Test successful"
|
||||
msgstr "测试成功"
|
||||
|
||||
#: terminal/api/storage.py:109
|
||||
#: terminal/api/storage.py:127
|
||||
msgid "Test failure: Account invalid"
|
||||
msgstr "测试失败: 账户无效"
|
||||
|
||||
|
@ -2983,6 +2995,10 @@ msgstr "正常"
|
|||
msgid "Bulk create not support"
|
||||
msgstr "不支持批量创建"
|
||||
|
||||
#: terminal/exceptions.py:13
|
||||
msgid "Storage is invalid"
|
||||
msgstr "存储无效"
|
||||
|
||||
#: terminal/models/session.py:43
|
||||
msgid "Login from"
|
||||
msgstr "登录来源"
|
||||
|
@ -3423,10 +3439,6 @@ msgstr "受理人"
|
|||
msgid "Assignees display"
|
||||
msgstr "受理人 (显示名称)"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:24
|
||||
msgid "Category display"
|
||||
msgstr "类别 (显示名称)"
|
||||
|
||||
#: tickets/serializers/ticket/meta/ticket_type/apply_application.py:31
|
||||
#: tickets/serializers/ticket/ticket.py:19
|
||||
msgid "Type display"
|
||||
|
@ -3558,7 +3570,7 @@ msgstr "工单已处理 - {} ({})"
|
|||
msgid "Your ticket has been processed, processor - {}"
|
||||
msgstr "你的工单已被处理, 处理人 - {}"
|
||||
|
||||
#: users/api/user.py:200
|
||||
#: users/api/user.py:212
|
||||
msgid "Could not reset self otp, use profile reset instead"
|
||||
msgstr "不能在该页面重置多因子认证, 请去个人信息页面重置"
|
||||
|
||||
|
@ -3638,8 +3650,8 @@ msgstr "复制你的公钥到这里"
|
|||
msgid "Public key should not be the same as your old one."
|
||||
msgstr "不能和原来的密钥相同"
|
||||
|
||||
#: users/forms/profile.py:149 users/serializers/profile.py:74
|
||||
#: users/serializers/profile.py:147 users/serializers/profile.py:160
|
||||
#: users/forms/profile.py:149 users/serializers/profile.py:71
|
||||
#: users/serializers/profile.py:144 users/serializers/profile.py:157
|
||||
msgid "Not a valid ssh public key"
|
||||
msgstr "SSH密钥不合法"
|
||||
|
||||
|
@ -3677,27 +3689,27 @@ msgstr "用户来源"
|
|||
msgid "Date password last updated"
|
||||
msgstr "最后更新密码日期"
|
||||
|
||||
#: users/models/user.py:685
|
||||
#: users/models/user.py:707
|
||||
msgid "Administrator"
|
||||
msgstr "管理员"
|
||||
|
||||
#: users/models/user.py:688
|
||||
#: users/models/user.py:710
|
||||
msgid "Administrator is the super user of system"
|
||||
msgstr "Administrator是初始的超级管理员"
|
||||
|
||||
#: users/serializers/profile.py:32
|
||||
#: users/serializers/profile.py:29
|
||||
msgid "The old password is incorrect"
|
||||
msgstr "旧密码错误"
|
||||
|
||||
#: users/serializers/profile.py:40 users/serializers/user.py:112
|
||||
#: users/serializers/profile.py:37 users/serializers/user.py:112
|
||||
msgid "Password does not match security rules"
|
||||
msgstr "密码不满足安全规则"
|
||||
|
||||
#: users/serializers/profile.py:46
|
||||
#: users/serializers/profile.py:43
|
||||
msgid "The newly set password is inconsistent"
|
||||
msgstr "两次密码不一致"
|
||||
|
||||
#: users/serializers/profile.py:118 users/serializers/user.py:65
|
||||
#: users/serializers/profile.py:115 users/serializers/user.py:67
|
||||
msgid "Is first login"
|
||||
msgstr "首次登录"
|
||||
|
||||
|
@ -3715,58 +3727,58 @@ msgid "Password strategy"
|
|||
msgstr "密码策略"
|
||||
|
||||
#: users/serializers/user.py:30
|
||||
msgid "MFA level for display"
|
||||
msgstr "多因子认证等级(显示名称)"
|
||||
|
||||
#: users/serializers/user.py:31
|
||||
msgid "Login blocked"
|
||||
msgstr "登录被阻塞"
|
||||
|
||||
#: users/serializers/user.py:32
|
||||
msgid "Can update"
|
||||
msgstr "是否可更新"
|
||||
|
||||
#: users/serializers/user.py:33
|
||||
msgid "Can delete"
|
||||
msgstr "是否可删除"
|
||||
|
||||
#: users/serializers/user.py:35 users/serializers/user.py:72
|
||||
msgid "Organization role name"
|
||||
msgstr "组织角色名称"
|
||||
|
||||
#: users/serializers/user.py:68
|
||||
msgid "Avatar url"
|
||||
msgstr "头像路径"
|
||||
|
||||
#: users/serializers/user.py:70
|
||||
msgid "Groups name"
|
||||
msgstr "用户组名"
|
||||
|
||||
#: users/serializers/user.py:71
|
||||
msgid "Source name"
|
||||
msgstr "用户来源名"
|
||||
|
||||
#: users/serializers/user.py:73
|
||||
msgid "Super role name"
|
||||
msgstr "超级角色名称"
|
||||
|
||||
#: users/serializers/user.py:74
|
||||
msgid "Total role name"
|
||||
msgstr "汇总角色名称"
|
||||
|
||||
#: users/serializers/user.py:75
|
||||
msgid "MFA enabled"
|
||||
msgstr "是否开启多因子认证"
|
||||
|
||||
#: users/serializers/user.py:76
|
||||
#: users/serializers/user.py:31
|
||||
msgid "MFA force enabled"
|
||||
msgstr "强制启用多因子认证"
|
||||
|
||||
#: users/serializers/user.py:32
|
||||
msgid "MFA level for display"
|
||||
msgstr "多因子认证等级(显示名称)"
|
||||
|
||||
#: users/serializers/user.py:33
|
||||
msgid "Login blocked"
|
||||
msgstr "登录被阻塞"
|
||||
|
||||
#: users/serializers/user.py:35
|
||||
msgid "Can update"
|
||||
msgstr "是否可更新"
|
||||
|
||||
#: users/serializers/user.py:36
|
||||
msgid "Can delete"
|
||||
msgstr "是否可删除"
|
||||
|
||||
#: users/serializers/user.py:38 users/serializers/user.py:74
|
||||
msgid "Organization role name"
|
||||
msgstr "组织角色名称"
|
||||
|
||||
#: users/serializers/user.py:70
|
||||
msgid "Avatar url"
|
||||
msgstr "头像路径"
|
||||
|
||||
#: users/serializers/user.py:72
|
||||
msgid "Groups name"
|
||||
msgstr "用户组名"
|
||||
|
||||
#: users/serializers/user.py:73
|
||||
msgid "Source name"
|
||||
msgstr "用户来源名"
|
||||
|
||||
#: users/serializers/user.py:75
|
||||
msgid "Super role name"
|
||||
msgstr "超级角色名称"
|
||||
|
||||
#: users/serializers/user.py:76
|
||||
msgid "Total role name"
|
||||
msgstr "汇总角色名称"
|
||||
|
||||
#: users/serializers/user.py:100
|
||||
msgid "Role limit to {}"
|
||||
msgstr "角色只能为 {}"
|
||||
|
||||
#: users/serializers/user.py:202
|
||||
#: users/serializers/user.py:197
|
||||
msgid "name not unique"
|
||||
msgstr "名称重复"
|
||||
|
||||
|
@ -4591,19 +4603,19 @@ msgstr ""
|
|||
" <br>\n"
|
||||
" "
|
||||
|
||||
#: users/views/profile/otp.py:193
|
||||
#: users/views/profile/otp.py:190
|
||||
msgid "MFA enable success"
|
||||
msgstr "多因子认证启用成功"
|
||||
|
||||
#: users/views/profile/otp.py:194
|
||||
#: users/views/profile/otp.py:191
|
||||
msgid "MFA enable success, return login page"
|
||||
msgstr "多因子认证启用成功,返回到登录页面"
|
||||
|
||||
#: users/views/profile/otp.py:196
|
||||
#: users/views/profile/otp.py:193
|
||||
msgid "MFA disable success"
|
||||
msgstr "多因子认证禁用成功"
|
||||
|
||||
#: users/views/profile/otp.py:197
|
||||
#: users/views/profile/otp.py:194
|
||||
msgid "MFA disable success, return login page"
|
||||
msgstr "多因子认证禁用成功,返回登录页面"
|
||||
|
||||
|
@ -5098,7 +5110,7 @@ msgstr "许可证导入成功"
|
|||
msgid "License is invalid"
|
||||
msgstr "无效的许可证"
|
||||
|
||||
#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:124
|
||||
#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:127
|
||||
msgid "License"
|
||||
msgstr "许可证"
|
||||
|
||||
|
@ -5117,3 +5129,14 @@ msgstr "旗舰版"
|
|||
#: xpack/plugins/license/models.py:77
|
||||
msgid "Community edition"
|
||||
msgstr "社区版"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Push system user task skip, auto push not enable or protocol is not ssh "
|
||||
#~ "or rdp: {}"
|
||||
#~ msgstr "推送系统用户任务跳过,自动推送没有打开,或协议不是ssh或rdp: {}"
|
||||
|
||||
#~ msgid "Empty"
|
||||
#~ msgstr "空"
|
||||
|
||||
#~ msgid "Organization contains undeleted resources"
|
||||
#~ msgstr "组织包含未删除的资源"
|
||||
|
|
|
@ -53,7 +53,7 @@ class AdHocDisplay(Display, metaclass=UnSingleton):
|
|||
# 这里先不 flush,log 文件不需要那么及时。
|
||||
self.log_file.write(msg)
|
||||
|
||||
def display(self, msg, color=None, stderr=False, screen_only=False, log_only=False):
|
||||
def display(self, msg, color=None, stderr=False, screen_only=False, log_only=False, newline=True):
|
||||
if color:
|
||||
msg = stringc(msg, color)
|
||||
|
||||
|
@ -62,5 +62,7 @@ class AdHocDisplay(Display, metaclass=UnSingleton):
|
|||
else:
|
||||
msg2 = msg
|
||||
|
||||
self._write_to_screen(msg2, stderr)
|
||||
self._write_to_log_file(msg2)
|
||||
if log_only:
|
||||
self._write_to_log_file(msg2)
|
||||
else:
|
||||
self._write_to_screen(msg2, stderr)
|
||||
|
|
|
@ -11,7 +11,7 @@ default_id = '00000000-0000-0000-0000-000000000002'
|
|||
|
||||
def add_default_org(apps, schema_editor):
|
||||
org_cls = apps.get_model('orgs', 'Organization')
|
||||
defaults = {'name': 'DEFAULT', 'id': default_id}
|
||||
defaults = {'name': 'Default', 'id': default_id}
|
||||
org_cls.objects.get_or_create(defaults=defaults, id=default_id)
|
||||
|
||||
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#
|
||||
from rest_framework.viewsets import ModelViewSet, GenericViewSet
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
from rest_framework.exceptions import MethodNotAllowed
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.mixins import CommonApiMixin, RelationMixin
|
||||
from orgs.utils import current_org
|
||||
|
||||
|
@ -39,15 +42,29 @@ class OrgQuerySetMixin:
|
|||
return queryset
|
||||
|
||||
|
||||
class OrgModelViewSet(CommonApiMixin, OrgQuerySetMixin, ModelViewSet):
|
||||
class OrgViewSetMixin(OrgQuerySetMixin):
|
||||
root_org_readonly_msg = _("Root organization only allow view and delete")
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
if current_org.is_root():
|
||||
raise MethodNotAllowed('put', self.root_org_readonly_msg)
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
if current_org.is_root():
|
||||
raise MethodNotAllowed('post', self.root_org_readonly_msg)
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
|
||||
class OrgModelViewSet(CommonApiMixin, OrgViewSetMixin, ModelViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class OrgGenericViewSet(CommonApiMixin, OrgQuerySetMixin, GenericViewSet):
|
||||
class OrgGenericViewSet(CommonApiMixin, OrgViewSetMixin, GenericViewSet):
|
||||
pass
|
||||
|
||||
|
||||
class OrgBulkModelViewSet(CommonApiMixin, OrgQuerySetMixin, BulkModelViewSet):
|
||||
class OrgBulkModelViewSet(CommonApiMixin, OrgViewSetMixin, BulkModelViewSet):
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
qs_count = qs.count()
|
||||
filtered_count = filtered.count()
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.dispatch import receiver
|
|||
|
||||
from users.models import User
|
||||
from assets.models import Asset
|
||||
from orgs.utils import current_org
|
||||
from orgs.utils import current_org, tmp_to_org
|
||||
from common.utils import get_logger
|
||||
from common.exceptions import M2MReverseNotAllowed
|
||||
from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR
|
||||
|
@ -18,24 +18,28 @@ logger = get_logger(__file__)
|
|||
|
||||
@receiver(m2m_changed, sender=User.groups.through)
|
||||
def on_user_groups_change(sender, instance, action, reverse, pk_set, **kwargs):
|
||||
if action.startswith('post'):
|
||||
if reverse:
|
||||
group_ids = [instance.id]
|
||||
user_ids = pk_set
|
||||
else:
|
||||
group_ids = pk_set
|
||||
user_ids = [instance.id]
|
||||
if not action.startswith('post'):
|
||||
return
|
||||
if reverse:
|
||||
group_ids = [instance.id]
|
||||
user_ids = pk_set
|
||||
else:
|
||||
group_ids = pk_set
|
||||
user_ids = [instance.id]
|
||||
|
||||
exists = AssetPermission.user_groups.through.objects.filter(usergroup_id__in=group_ids).exists()
|
||||
if exists:
|
||||
org_ids = [current_org.id]
|
||||
UserGrantedTreeRefreshController.add_need_refresh_orgs_for_users(org_ids, user_ids)
|
||||
exists = AssetPermission.user_groups.through.objects.filter(usergroup_id__in=group_ids).exists()
|
||||
if not exists:
|
||||
return
|
||||
with tmp_to_org(instance.org):
|
||||
org_ids = [current_org.id]
|
||||
UserGrantedTreeRefreshController.add_need_refresh_orgs_for_users(org_ids, user_ids)
|
||||
|
||||
|
||||
@receiver([pre_delete], sender=AssetPermission)
|
||||
def on_asset_perm_pre_delete(sender, instance, **kwargs):
|
||||
# 授权删除之前,查出所有相关用户
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
|
||||
|
||||
@receiver([pre_save], sender=AssetPermission)
|
||||
|
@ -44,14 +48,17 @@ def on_asset_perm_pre_save(sender, instance, **kwargs):
|
|||
old = AssetPermission.objects.get(id=instance.id)
|
||||
|
||||
if old.is_valid != instance.is_valid:
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
except AssetPermission.DoesNotExist:
|
||||
pass
|
||||
|
||||
|
||||
@receiver([post_save], sender=AssetPermission)
|
||||
def on_asset_perm_post_save(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
if not created:
|
||||
return
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
|
||||
|
||||
|
@ -64,7 +71,10 @@ def on_permission_nodes_changed(sender, instance, action, reverse, **kwargs):
|
|||
if reverse:
|
||||
raise M2MReverseNotAllowed
|
||||
|
||||
if need_rebuild_mapping_node(action):
|
||||
if not need_rebuild_mapping_node(action):
|
||||
return
|
||||
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
|
||||
|
||||
|
@ -73,28 +83,38 @@ def on_permission_assets_changed(sender, instance, action, reverse, pk_set, mode
|
|||
if reverse:
|
||||
raise M2MReverseNotAllowed
|
||||
|
||||
if need_rebuild_mapping_node(action):
|
||||
if not need_rebuild_mapping_node(action):
|
||||
return
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_by_asset_perm_ids([instance.id])
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=AssetPermission.users.through)
|
||||
def on_asset_permission_users_changed(sender, action, reverse, pk_set, **kwargs):
|
||||
def on_asset_permission_users_changed(sender, action, reverse, instance, pk_set, **kwargs):
|
||||
if reverse:
|
||||
raise M2MReverseNotAllowed
|
||||
|
||||
if need_rebuild_mapping_node(action):
|
||||
if not need_rebuild_mapping_node(action):
|
||||
return
|
||||
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_orgs_for_users(
|
||||
[current_org.id], pk_set
|
||||
)
|
||||
|
||||
|
||||
@receiver(m2m_changed, sender=AssetPermission.user_groups.through)
|
||||
def on_asset_permission_user_groups_changed(sender, action, pk_set, reverse, **kwargs):
|
||||
def on_asset_permission_user_groups_changed(sender, instance, action, pk_set, reverse, **kwargs):
|
||||
if reverse:
|
||||
raise M2MReverseNotAllowed
|
||||
|
||||
if need_rebuild_mapping_node(action):
|
||||
user_ids = User.groups.through.objects.filter(usergroup_id__in=pk_set).distinct().values_list('user_id', flat=True)
|
||||
if not need_rebuild_mapping_node(action):
|
||||
return
|
||||
|
||||
user_ids = User.groups.through.objects.filter(usergroup_id__in=pk_set) \
|
||||
.values_list('user_id', flat=True) \
|
||||
.distinct()
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_orgs_for_users(
|
||||
[current_org.id], user_ids
|
||||
)
|
||||
|
@ -112,4 +132,5 @@ def on_node_asset_change(action, instance, reverse, pk_set, **kwargs):
|
|||
asset_pk_set = [instance.id]
|
||||
node_pk_set = pk_set
|
||||
|
||||
UserGrantedTreeRefreshController.add_need_refresh_on_nodes_assets_relate_change(node_pk_set, asset_pk_set)
|
||||
with tmp_to_org(instance.org):
|
||||
UserGrantedTreeRefreshController.add_need_refresh_on_nodes_assets_relate_change(node_pk_set, asset_pk_set)
|
||||
|
|
|
@ -10,6 +10,7 @@ from django.core.mail import send_mail, get_connection
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.templatetags.static import static
|
||||
|
||||
from jumpserver.utils import has_valid_xpack_license
|
||||
from common.permissions import IsSuperUser
|
||||
from common.utils import get_logger
|
||||
from .. import serializers
|
||||
|
@ -94,14 +95,6 @@ class PublicSettingApi(generics.RetrieveAPIView):
|
|||
logo_urls.update({attr: getattr(obj, attr).url})
|
||||
return logo_urls
|
||||
|
||||
@staticmethod
|
||||
def get_xpack_license_is_valid():
|
||||
if not settings.XPACK_ENABLED:
|
||||
return False
|
||||
|
||||
from xpack.plugins.license.models import License
|
||||
return License.has_valid_license()
|
||||
|
||||
@staticmethod
|
||||
def get_login_title():
|
||||
default_title = _('Welcome to the JumpServer open source Bastion Host')
|
||||
|
@ -121,7 +114,7 @@ class PublicSettingApi(generics.RetrieveAPIView):
|
|||
"SECURITY_MFA_VERIFY_TTL": settings.SECURITY_MFA_VERIFY_TTL,
|
||||
"SECURITY_COMMAND_EXECUTION": settings.SECURITY_COMMAND_EXECUTION,
|
||||
"SECURITY_PASSWORD_EXPIRATION_TIME": settings.SECURITY_PASSWORD_EXPIRATION_TIME,
|
||||
"XPACK_LICENSE_IS_VALID": self.get_xpack_license_is_valid(),
|
||||
"XPACK_LICENSE_IS_VALID": has_valid_xpack_license(),
|
||||
"LOGIN_TITLE": self.get_login_title(),
|
||||
"LOGO_URLS": self.get_logo_urls(),
|
||||
"TICKETS_ENABLED": settings.TICKETS_ENABLED,
|
||||
|
|
|
@ -11,7 +11,7 @@ from rest_framework.response import Response
|
|||
from rest_framework.decorators import action
|
||||
from django.template import loader
|
||||
|
||||
from terminal.models import CommandStorage
|
||||
from terminal.models import CommandStorage, Command
|
||||
from terminal.filters import CommandFilter
|
||||
from orgs.utils import current_org
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor, IsAppUser
|
||||
|
@ -19,6 +19,7 @@ from common.const.http import GET
|
|||
from common.utils import get_logger
|
||||
from terminal.utils import send_command_alert_mail
|
||||
from terminal.serializers import InsecureCommandAlertSerializer
|
||||
from terminal.exceptions import StorageInvalid
|
||||
from ..backends import (
|
||||
get_command_storage, get_multi_command_storage,
|
||||
SessionCommandSerializer,
|
||||
|
@ -116,9 +117,12 @@ class CommandViewSet(viewsets.ModelViewSet):
|
|||
|
||||
storages = CommandStorage.objects.all()
|
||||
for storage in storages:
|
||||
if not storage.is_valid():
|
||||
continue
|
||||
|
||||
qs = storage.get_command_queryset()
|
||||
commands = self.filter_queryset(qs)
|
||||
merged_commands.extend(commands)
|
||||
merged_commands.extend(commands[:]) # ES 默认只取 10 条数据
|
||||
|
||||
merged_commands.sort(key=lambda command: command.timestamp, reverse=True)
|
||||
page = self.paginate_queryset(merged_commands)
|
||||
|
@ -126,7 +130,7 @@ class CommandViewSet(viewsets.ModelViewSet):
|
|||
serializer = self.get_serializer(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
serializer = self.get_serializer(merged_commands, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
@ -141,7 +145,10 @@ class CommandViewSet(viewsets.ModelViewSet):
|
|||
def get_queryset(self):
|
||||
command_storage_id = self.request.query_params.get('command_storage_id')
|
||||
storage = CommandStorage.objects.get(id=command_storage_id)
|
||||
qs = storage.get_command_queryset()
|
||||
if not storage.is_valid():
|
||||
raise StorageInvalid
|
||||
else:
|
||||
qs = storage.get_command_queryset()
|
||||
return qs
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
|
|
@ -46,7 +46,13 @@ class CommandStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
|
|||
def tree(self, request: Request):
|
||||
storage_qs = self.get_queryset().exclude(name='null')
|
||||
storages_with_count = []
|
||||
invalid_storages = []
|
||||
|
||||
for storage in storage_qs:
|
||||
if not storage.is_valid():
|
||||
invalid_storages.append(storage)
|
||||
continue
|
||||
|
||||
command_qs = storage.get_command_queryset()
|
||||
filterset = CommandFilter(
|
||||
data=request.query_params, queryset=command_qs,
|
||||
|
@ -70,6 +76,7 @@ class CommandStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
|
|||
'open': True,
|
||||
}
|
||||
|
||||
invalid = _('Invalid')
|
||||
nodes = [
|
||||
{
|
||||
'id': storage.id,
|
||||
|
@ -78,7 +85,18 @@ class CommandStorageViewSet(BaseStorageViewSetMixin, viewsets.ModelViewSet):
|
|||
'pId': 'root',
|
||||
'isParent': False,
|
||||
'open': False,
|
||||
'valid': True,
|
||||
} for storage, command_count in storages_with_count
|
||||
] + [
|
||||
{
|
||||
'id': storage.id,
|
||||
'name': f'{storage.name}({storage.type}) *{invalid}',
|
||||
'title': f'{storage.name}({storage.type})',
|
||||
'pId': 'root',
|
||||
'isParent': False,
|
||||
'open': False,
|
||||
'valid': False,
|
||||
} for storage in invalid_storages
|
||||
]
|
||||
nodes.append(root)
|
||||
return Response(data=nodes)
|
||||
|
|
|
@ -25,7 +25,7 @@ class CommandStore():
|
|||
kwargs = config.get("OTHER", {})
|
||||
self.index = config.get("INDEX") or 'jumpserver'
|
||||
self.doc_type = config.get("DOC_TYPE") or 'command_store'
|
||||
self.es = Elasticsearch(hosts=hosts, **kwargs)
|
||||
self.es = Elasticsearch(hosts=hosts, max_retries=0, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def make_data(command):
|
||||
|
@ -81,9 +81,9 @@ class CommandStore():
|
|||
"""返回所有数据"""
|
||||
raise NotImplementedError("Not support")
|
||||
|
||||
def ping(self):
|
||||
def ping(self, timeout=None):
|
||||
try:
|
||||
return self.es.ping()
|
||||
return self.es.ping(request_timeout=timeout)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
@ -121,7 +121,11 @@ class CommandStore():
|
|||
org_id = match.get('org_id')
|
||||
|
||||
real_default_org_id = '00000000-0000-0000-0000-000000000002'
|
||||
if org_id in (real_default_org_id, ''):
|
||||
root_org_id = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
if org_id == root_org_id:
|
||||
match.pop('org_id')
|
||||
elif org_id in (real_default_org_id, ''):
|
||||
match.pop('org_id')
|
||||
should.append({
|
||||
'bool':{
|
||||
|
@ -256,7 +260,7 @@ class QuerySet(DJQuerySet):
|
|||
clone = self.__clone()
|
||||
from_ = item.start or 0
|
||||
if item.stop is None:
|
||||
size = 10
|
||||
size = self.max_result_window - from_
|
||||
else:
|
||||
size = item.stop - from_
|
||||
|
||||
|
|
|
@ -6,3 +6,9 @@ from common.exceptions import JMSException
|
|||
class BulkCreateNotSupport(JMSException):
|
||||
default_code = 'bulk_create_not_support'
|
||||
default_detail = _('Bulk create not support')
|
||||
|
||||
|
||||
class StorageInvalid(JMSException):
|
||||
default_code = 'storage_invalid'
|
||||
default_detail = _('Storage is invalid')
|
||||
|
||||
|
|
|
@ -52,8 +52,14 @@ class CommandStorage(CommonModelMixin):
|
|||
def is_valid(self):
|
||||
if self.type_null_or_server:
|
||||
return True
|
||||
storage = jms_storage.get_log_storage(self.config)
|
||||
return storage.ping()
|
||||
|
||||
if self.type not in TYPE_ENGINE_MAPPING:
|
||||
logger.error(f'Command storage `{self.type}` not support')
|
||||
return False
|
||||
|
||||
engine_mod = import_module(TYPE_ENGINE_MAPPING[self.type])
|
||||
store = engine_mod.CommandStore(self.config)
|
||||
return store.ping(timeout=3)
|
||||
|
||||
def is_use(self):
|
||||
return Terminal.objects.filter(command_storage=self.name, is_deleted=False).exists()
|
||||
|
|
Loading…
Reference in New Issue