Merge branch 'dev' of github.com:jumpserver/jumpserver into pr@dev@feat_account_backend_support_aws

pull/14515/head
jiangweidong 2024-11-22 10:01:43 +08:00
commit a6fa1dab2a
35 changed files with 326 additions and 178 deletions

View File

@ -24,6 +24,7 @@ ENV LANG=en_US.UTF-8 \
PATH=/opt/py3/bin:$PATH PATH=/opt/py3/bin:$PATH
ARG DEPENDENCIES=" \ ARG DEPENDENCIES=" \
libldap2-dev \
libx11-dev" libx11-dev"
ARG TOOLS=" \ ARG TOOLS=" \

View File

@ -30,6 +30,6 @@
login_user: "{{ account.username }}" login_user: "{{ account.username }}"
login_password: "{{ account.secret }}" login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}" login_secret_type: "{{ account.secret_type }}"
gateway_args: "{{ jms_gateway | default(None) }}" gateway_args: "{{ jms_gateway | default({}) }}"
when: account.secret_type == "password" when: account.secret_type == "password"
delegate_to: localhost delegate_to: localhost

View File

@ -30,6 +30,6 @@
login_user: "{{ account.username }}" login_user: "{{ account.username }}"
login_password: "{{ account.secret }}" login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}" login_secret_type: "{{ account.secret_type }}"
gateway_args: "{{ jms_gateway | default(None) }}" gateway_args: "{{ jms_gateway | default({}) }}"
when: account.secret_type == "password" when: account.secret_type == "password"
delegate_to: localhost delegate_to: localhost

View File

@ -1,18 +1,21 @@
from importlib import import_module from importlib import import_module
from django.utils.functional import LazyObject from django.utils.functional import LazyObject, empty
from common.utils import get_logger from common.utils import get_logger
from ..const import VaultTypeChoices from ..const import VaultTypeChoices
__all__ = ['vault_client', 'get_vault_client'] __all__ = ['vault_client', 'get_vault_client', 'refresh_vault_client']
logger = get_logger(__file__) logger = get_logger(__file__)
def get_vault_client(raise_exception=False, **kwargs): def get_vault_client(raise_exception=False, **kwargs):
tp = kwargs.get('VAULT_BACKEND') if kwargs.get('VAULT_ENABLED') else VaultTypeChoices.local tp = kwargs.get('VAULT_BACKEND') if kwargs.get('VAULT_ENABLED') else VaultTypeChoices.local
# TODO: Temporary processing, subsequent deletion
tp = VaultTypeChoices.local if tp == VaultTypeChoices.azure else tp
try: try:
module_path = f'apps.accounts.backends.{tp}.main' module_path = f'apps.accounts.backends.{tp}.main'
client = import_module(module_path).Vault(**kwargs) client = import_module(module_path).Vault(**kwargs)
@ -38,3 +41,7 @@ class VaultClient(LazyObject):
""" 为了安全, 页面修改配置, 重启服务后才会重新初始化 vault_client """ """ 为了安全, 页面修改配置, 重启服务后才会重新初始化 vault_client """
vault_client = VaultClient() vault_client = VaultClient()
def refresh_vault_client():
vault_client._wrapped = empty

View File

@ -49,9 +49,9 @@ class SecretStrategy(models.TextChoices):
class SSHKeyStrategy(models.TextChoices): class SSHKeyStrategy(models.TextChoices):
add = 'add', _('Append SSH KEY')
set = 'set', _('Empty and append SSH KEY')
set_jms = 'set_jms', _('Replace (Replace only keys pushed by JumpServer) ') set_jms = 'set_jms', _('Replace (Replace only keys pushed by JumpServer) ')
set = 'set', _('Empty and append SSH KEY')
add = 'add', _('Append SSH KEY')
class TriggerChoice(models.TextChoices, TreeChoices): class TriggerChoice(models.TextChoices, TreeChoices):

View File

@ -50,7 +50,7 @@ class Migration(migrations.Migration):
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')), ('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')), ('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')), ('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('ssh_key_change_strategy', models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy')), ('ssh_key_change_strategy', models.CharField(choices=[('set_jms', 'Replace (Replace only keys pushed by JumpServer) '), ('set', 'Empty and append SSH KEY'), ('add', 'Append SSH KEY')], default='set_jms', max_length=16, verbose_name='SSH key change strategy')),
], ],
options={ options={
'verbose_name': 'Change secret automation', 'verbose_name': 'Change secret automation',
@ -76,7 +76,7 @@ class Migration(migrations.Migration):
('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')), ('secret', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Secret')),
('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')), ('secret_strategy', models.CharField(choices=[('specific', 'Specific secret'), ('random', 'Random generate')], default='specific', max_length=16, verbose_name='Secret strategy')),
('password_rules', models.JSONField(default=dict, verbose_name='Password rules')), ('password_rules', models.JSONField(default=dict, verbose_name='Password rules')),
('ssh_key_change_strategy', models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy')), ('ssh_key_change_strategy', models.CharField(choices=[('set_jms', 'Replace (Replace only keys pushed by JumpServer) '), ('set', 'Empty and append SSH KEY'), ('add', 'Append SSH KEY')], default='set_jms', max_length=16, verbose_name='SSH key change strategy')),
('triggers', models.JSONField(default=list, max_length=16, verbose_name='Triggers')), ('triggers', models.JSONField(default=list, max_length=16, verbose_name='Triggers')),
('username', models.CharField(max_length=128, verbose_name='Username')), ('username', models.CharField(max_length=128, verbose_name='Username')),
('action', models.CharField(max_length=16, verbose_name='Action')), ('action', models.CharField(max_length=16, verbose_name='Action')),

View File

@ -51,7 +51,7 @@ class AutomationExecution(AssetAutomationExecution):
class ChangeSecretMixin(SecretWithRandomMixin): class ChangeSecretMixin(SecretWithRandomMixin):
ssh_key_change_strategy = models.CharField( ssh_key_change_strategy = models.CharField(
choices=SSHKeyStrategy.choices, max_length=16, choices=SSHKeyStrategy.choices, max_length=16,
default=SSHKeyStrategy.add, verbose_name=_('SSH key change strategy') default=SSHKeyStrategy.set_jms, verbose_name=_('SSH key change strategy')
) )
get_all_assets: callable # get all assets get_all_assets: callable # get all assets

View File

@ -3,14 +3,17 @@ from collections import defaultdict
from django.db.models.signals import post_delete from django.db.models.signals import post_delete
from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.functional import LazyObject
from django.utils.translation import gettext_noop from django.utils.translation import gettext_noop
from accounts.backends import vault_client from accounts.backends import vault_client, refresh_vault_client
from accounts.const import Source from accounts.const import Source
from audits.const import ActivityChoices from audits.const import ActivityChoices
from audits.signal_handlers import create_activities from audits.signal_handlers import create_activities
from common.decorators import merge_delay_run from common.decorators import merge_delay_run
from common.signals import django_ready
from common.utils import get_logger, i18n_fmt from common.utils import get_logger, i18n_fmt
from common.utils.connection import RedisPubSub
from .models import Account, AccountTemplate from .models import Account, AccountTemplate
from .tasks.push_account import push_accounts_to_assets_task from .tasks.push_account import push_accounts_to_assets_task
@ -91,3 +94,18 @@ class VaultSignalHandler(object):
for model in (Account, AccountTemplate, Account.history.model): for model in (Account, AccountTemplate, Account.history.model):
post_save.connect(VaultSignalHandler.save_to_vault, sender=model) post_save.connect(VaultSignalHandler.save_to_vault, sender=model)
post_delete.connect(VaultSignalHandler.delete_to_vault, sender=model) post_delete.connect(VaultSignalHandler.delete_to_vault, sender=model)
class VaultPubSub(LazyObject):
def _setup(self):
self._wrapped = RedisPubSub('refresh_vault')
vault_pub_sub = VaultPubSub()
@receiver(django_ready)
def subscribe_vault_change(sender, **kwargs):
logger.debug("Start subscribe vault change")
vault_pub_sub.subscribe(lambda name: refresh_vault_client())

View File

@ -7,6 +7,7 @@ from django.db.models import F, Value, CharField, Q
from django.db.models.functions import Cast from django.db.models.functions import Cast
from django.http import HttpResponse, FileResponse from django.http import HttpResponse, FileResponse
from django.utils.encoding import escape_uri_path from django.utils.encoding import escape_uri_path
from django_celery_beat.models import PeriodicTask
from rest_framework import generics from rest_framework import generics
from rest_framework import status from rest_framework import status
from rest_framework import viewsets from rest_framework import viewsets
@ -74,6 +75,21 @@ class JobsAuditViewSet(OrgModelViewSet):
queryset = queryset.exclude(type=Types.upload_file).filter(instant=False) queryset = queryset.exclude(type=Types.upload_file).filter(instant=False)
return queryset return queryset
def perform_update(self, serializer):
job = self.get_object()
is_periodic = serializer.validated_data.get('is_periodic')
if job.is_periodic != is_periodic:
job.is_periodic = is_periodic
job.save()
name, task, args, kwargs = job.get_register_task()
task_obj = PeriodicTask.objects.filter(name=name).first()
if task_obj:
is_periodic = job.is_periodic
if task_obj.enabled != is_periodic:
task_obj.enabled = is_periodic
task_obj.save()
return super().perform_update(serializer)
class FTPLogViewSet(OrgModelViewSet): class FTPLogViewSet(OrgModelViewSet):
model = FTPLog model = FTPLog

View File

@ -38,10 +38,12 @@ class JobsAuditSerializer(JobSerializer):
material = serializers.ReadOnlyField(label=_("Command")) material = serializers.ReadOnlyField(label=_("Command"))
summary = serializers.ReadOnlyField(label=_("Summary")) summary = serializers.ReadOnlyField(label=_("Summary"))
crontab = serializers.ReadOnlyField(label=_("Execution cycle")) crontab = serializers.ReadOnlyField(label=_("Execution cycle"))
is_periodic_display = serializers.BooleanField(read_only=True, source='is_periodic')
class Meta(JobSerializer.Meta): class Meta(JobSerializer.Meta):
read_only_fields = [ read_only_fields = [
"id", 'name', 'args', 'material', 'type', 'crontab', 'interval', 'date_last_run', 'summary', 'created_by' "id", 'name', 'args', 'material', 'type', 'crontab', 'interval', 'date_last_run', 'summary', 'created_by',
'is_periodic_display'
] ]
fields = read_only_fields + ['is_periodic'] fields = read_only_fields + ['is_periodic']

View File

@ -5,6 +5,7 @@ from django.shortcuts import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from authentication.mixins import MFAFaceMixin from authentication.mixins import MFAFaceMixin
from common.const import LicenseEditionChoices
from settings.api import settings from settings.api import settings
@ -32,7 +33,10 @@ class MFAFace(BaseMFA, MFAFaceMixin):
@staticmethod @staticmethod
def global_enabled(): def global_enabled():
return settings.XPACK_LICENSE_IS_VALID and settings.FACE_RECOGNITION_ENABLED return settings.XPACK_LICENSE_IS_VALID \
and LicenseEditionChoices.ULTIMATE == \
LicenseEditionChoices.from_key(settings.XPACK_LICENSE_EDITION) \
and settings.FACE_RECOGNITION_ENABLED
def get_enable_url(self) -> str: def get_enable_url(self) -> str:
return reverse('authentication:user-face-enable') return reverse('authentication:user-face-enable')

View File

@ -35,7 +35,7 @@ class MFAMiddleware:
# 这个是 mfa 登录页需要的请求, 也得放出来, 用户其实已经在 CAS/OIDC 中完成登录了 # 这个是 mfa 登录页需要的请求, 也得放出来, 用户其实已经在 CAS/OIDC 中完成登录了
white_urls = [ white_urls = [
'login/mfa', 'mfa/select', 'jsi18n/', '/static/', 'login/mfa', 'mfa/select', 'mfa/face','jsi18n/', '/static/',
'/profile/otp', '/logout/', '/profile/otp', '/logout/',
] ]
for url in white_urls: for url in white_urls:

View File

@ -238,7 +238,7 @@ class MFAFaceMixin:
if not self.is_context_success(context): if not self.is_context_success(context):
msg = context.get('error_message', '') msg = context.get('error_message', '')
raise RuntimeError("Face recognition failed,{}".format(msg)) raise RuntimeError(msg)
face_code = context.get('face_code') face_code = context.get('face_code')
if not face_code: if not face_code:

View File

@ -2,28 +2,18 @@
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
{% block content %} {% block content %}
<style>
.ibox-content {
padding: 0;
}
h2 {
display: none;
}
.ibox-context-margin {
margin: 0;
}
</style>
{% if 'code' in form.errors %} {% if 'code' in form.errors %}
<p class="red-fonts">{{ form.code.errors.as_text }}</p> <div class="alert alert-danger" id="messages">
<p class="red-fonts">{{ form.code.errors.as_text }}</p>
</div>
{% endif %} {% endif %}
<div id="retry_container" style="text-align: center; margin-top: 20px; display: none;">
<button id="retry_button" class="btn btn-primary">{% trans 'Retry' %}</button>
</div>
<form class="m-t" role="form" method="post" action="" style="display: none"> <form class="m-t" role="form" method="post" action="" style="display: none">
{% csrf_token %} {% csrf_token %}
<button id="submit_button" type="submit" style="display: none"></button> <button id="submit_button" type="submit" style="display: none"></button>
@ -40,9 +30,7 @@
</iframe> </iframe>
</div> </div>
<div id="retry_container" style="text-align: center; margin-top: 20px; display: none;">
<button id="retry_button" class="btn btn-primary">{% trans 'Retry' %}</button>
</div>
<script> <script>
$(document).ready(function () { $(document).ready(function () {

View File

@ -76,3 +76,32 @@ class Language(models.TextChoices):
COUNTRY_CALLING_CODES = get_country_phone_choices() COUNTRY_CALLING_CODES = get_country_phone_choices()
class LicenseEditionChoices(models.TextChoices):
COMMUNITY = 'community', _('Community edition')
BASIC = 'basic', _('Basic edition')
STANDARD = 'standard', _('Standard edition')
PROFESSIONAL = 'professional', _('Professional edition')
ULTIMATE = 'ultimate', _('Ultimate edition')
@staticmethod
def from_key(key: str):
for choice in LicenseEditionChoices:
if choice == key:
return choice
return LicenseEditionChoices.COMMUNITY
@staticmethod
def parse_license_edition(info):
count = info.get('license', {}).get('count', 0)
if 50 >= count > 0:
return LicenseEditionChoices.BASIC
elif count <= 500:
return LicenseEditionChoices.STANDARD
elif count < 5000:
return LicenseEditionChoices.PROFESSIONAL
elif count >= 5000:
return LicenseEditionChoices.ULTIMATE
else:
return LicenseEditionChoices.COMMUNITY

View File

@ -73,6 +73,7 @@ known_unauth_urls = [
"/api/v1/authentication/password/reset-code/", "/api/v1/authentication/password/reset-code/",
"/api/v1/authentication/login-confirm-ticket/status/", "/api/v1/authentication/login-confirm-ticket/status/",
"/api/v1/authentication/mfa/select/", "/api/v1/authentication/mfa/select/",
"/api/v1/authentication/mfa/face/context/",
"/api/v1/authentication/mfa/send-code/", "/api/v1/authentication/mfa/send-code/",
"/api/v1/authentication/sso/login/", "/api/v1/authentication/sso/login/",
"/api/v1/authentication/user-session/", "/api/v1/authentication/user-session/",

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: 2024-11-19 18:55+0800\n" "POT-Creation-Date: 2024-11-20 19:32+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"
@ -35,7 +35,7 @@ msgstr "生成资产或应用相关备份信息文件"
#: accounts/automations/backup_account/handlers.py:156 #: accounts/automations/backup_account/handlers.py:156
#: accounts/automations/backup_account/handlers.py:295 #: accounts/automations/backup_account/handlers.py:295
#: accounts/automations/backup_account/manager.py:40 ops/serializers/job.py:82 #: accounts/automations/backup_account/manager.py:40 ops/serializers/job.py:94
#: settings/templates/ldap/_msg_import_ldap_user.html:7 #: settings/templates/ldap/_msg_import_ldap_user.html:7
msgid "Time cost" msgid "Time cost"
msgstr "花费时间" msgstr "花费时间"
@ -57,7 +57,7 @@ msgid "The backup file will be sent to"
msgstr "备份文件将被发送至" msgstr "备份文件将被发送至"
#: accounts/automations/backup_account/handlers.py:213 #: accounts/automations/backup_account/handlers.py:213
#: users/forms/profile.py:75 #: users/forms/profile.py:76
msgid "Finish" msgid "Finish"
msgstr "完成" msgstr "完成"
@ -130,7 +130,7 @@ msgstr ">>> 开始执行测试网关账号可连接性任务"
#: settings/serializers/auth/ldap.py:26 settings/serializers/auth/ldap.py:52 #: settings/serializers/auth/ldap.py:26 settings/serializers/auth/ldap.py:52
#: settings/serializers/auth/ldap_ha.py:34 settings/serializers/msg.py:37 #: settings/serializers/auth/ldap_ha.py:34 settings/serializers/msg.py:37
#: settings/serializers/terminal.py:28 terminal/serializers/storage.py:123 #: settings/serializers/terminal.py:28 terminal/serializers/storage.py:123
#: terminal/serializers/storage.py:142 users/forms/profile.py:21 #: terminal/serializers/storage.py:142 users/forms/profile.py:22
#: users/serializers/user.py:144 #: users/serializers/user.py:144
#: users/templates/users/_msg_user_created.html:13 #: users/templates/users/_msg_user_created.html:13
#: users/templates/users/user_password_verify.html:18 #: users/templates/users/user_password_verify.html:18
@ -149,7 +149,7 @@ msgid "Access key"
msgstr "Access key" msgstr "Access key"
#: accounts/const/account.py:9 authentication/backends/passkey/models.py:16 #: accounts/const/account.py:9 authentication/backends/passkey/models.py:16
#: authentication/models/sso_token.py:14 settings/serializers/feature.py:74 #: authentication/models/sso_token.py:14 settings/serializers/feature.py:83
msgid "Token" msgid "Token"
msgstr "令牌" msgstr "令牌"
@ -233,17 +233,17 @@ msgstr "指定"
msgid "Random generate" msgid "Random generate"
msgstr "随机生成" msgstr "随机生成"
#: accounts/const/automation.py:52 ops/const.py:13 #: accounts/const/automation.py:52 ops/const.py:15
msgid "Append SSH KEY" msgid "Replace (Replace only keys pushed by JumpServer) "
msgstr "追加" msgstr "替换 (只替换由 JumpServer 推送的密钥)"
#: accounts/const/automation.py:53 ops/const.py:14 #: accounts/const/automation.py:53 ops/const.py:14
msgid "Empty and append SSH KEY" msgid "Empty and append SSH KEY"
msgstr "清空所有并添加" msgstr "清空所有并添加"
#: accounts/const/automation.py:54 ops/const.py:15 #: accounts/const/automation.py:54 ops/const.py:13
msgid "Replace (Replace only keys pushed by JumpServer) " msgid "Append SSH KEY"
msgstr "替换 (只替换由 JumpServer 推送的密钥)" msgstr "追加"
#: accounts/const/automation.py:59 #: accounts/const/automation.py:59
msgid "On asset create" msgid "On asset create"
@ -297,8 +297,8 @@ msgstr "仅创建"
#: authentication/serializers/password_mfa.py:17 #: authentication/serializers/password_mfa.py:17
#: authentication/serializers/password_mfa.py:25 #: authentication/serializers/password_mfa.py:25
#: notifications/backends/__init__.py:10 settings/serializers/msg.py:21 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:21
#: settings/serializers/msg.py:61 users/forms/profile.py:100 #: settings/serializers/msg.py:61 users/forms/profile.py:101
#: users/forms/profile.py:108 users/models/user/__init__.py:65 #: users/forms/profile.py:111 users/models/user/__init__.py:65
#: users/templates/users/forgot_password.html:162 #: users/templates/users/forgot_password.html:162
#: users/views/profile/reset.py:94 #: users/views/profile/reset.py:94
msgid "Email" msgid "Email"
@ -319,11 +319,11 @@ msgstr "待定的"
msgid "Database" msgid "Database"
msgstr "数据库" msgstr "数据库"
#: accounts/const/vault.py:9 settings/serializers/feature.py:69 #: accounts/const/vault.py:9 settings/serializers/feature.py:78
msgid "HCP Vault" msgid "HCP Vault"
msgstr "HashiCorp Vault" msgstr "HashiCorp Vault"
#: accounts/const/vault.py:10 settings/serializers/feature.py:82 #: accounts/const/vault.py:10 settings/serializers/feature.py:91
msgid "Azure Key Vault" msgid "Azure Key Vault"
msgstr "Azure Key Vault" msgstr "Azure Key Vault"
@ -510,7 +510,7 @@ msgstr "原因"
#: accounts/models/automations/backup_account.py:136 #: accounts/models/automations/backup_account.py:136
#: accounts/serializers/automations/change_secret.py:117 #: accounts/serializers/automations/change_secret.py:117
#: accounts/serializers/automations/change_secret.py:152 #: accounts/serializers/automations/change_secret.py:152
#: ops/serializers/job.py:80 terminal/serializers/session.py:54 #: ops/serializers/job.py:92 terminal/serializers/session.py:54
msgid "Is success" msgid "Is success"
msgstr "是否成功" msgstr "是否成功"
@ -593,7 +593,7 @@ msgstr "结束日期"
#: accounts/models/automations/change_secret.py:44 #: accounts/models/automations/change_secret.py:44
#: assets/models/automations/base.py:113 #: assets/models/automations/base.py:113
#: assets/serializers/automations/base.py:39 audits/models.py:208 #: assets/serializers/automations/base.py:39 audits/models.py:208
#: audits/serializers.py:76 ops/models/base.py:49 ops/models/job.py:235 #: audits/serializers.py:78 ops/models/base.py:49 ops/models/job.py:235
#: terminal/models/applet/applet.py:331 terminal/models/applet/host.py:140 #: terminal/models/applet/applet.py:331 terminal/models/applet/host.py:140
#: terminal/models/component/status.py:30 #: terminal/models/component/status.py:30
#: terminal/models/virtualapp/virtualapp.py:99 #: terminal/models/virtualapp/virtualapp.py:99
@ -635,8 +635,8 @@ msgstr "最后登录日期"
#: authentication/forms.py:23 authentication/models/temp_token.py:9 #: authentication/forms.py:23 authentication/models/temp_token.py:9
#: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_different_city.html:9
#: authentication/templates/authentication/_msg_oauth_bind.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9
#: terminal/serializers/storage.py:136 users/forms/profile.py:31 #: terminal/serializers/storage.py:136 users/forms/profile.py:32
#: users/forms/profile.py:114 users/models/user/__init__.py:63 #: users/forms/profile.py:117 users/models/user/__init__.py:63
#: users/templates/users/_msg_user_created.html:12 #: users/templates/users/_msg_user_created.html:12
#: xpack/plugins/cloud/serializers/account_attrs.py:26 #: xpack/plugins/cloud/serializers/account_attrs.py:26
msgid "Username" msgid "Username"
@ -665,7 +665,7 @@ msgstr "触发方式"
#: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: accounts/models/automations/push_account.py:16 acls/models/base.py:41
#: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81
#: audits/models.py:92 audits/serializers.py:106 #: audits/models.py:92 audits/serializers.py:108
#: authentication/serializers/connect_token_secret.py:119 #: authentication/serializers/connect_token_secret.py:119
#: authentication/templates/authentication/_access_key_modal.html:34 #: authentication/templates/authentication/_access_key_modal.html:34
#: perms/serializers/permission.py:52 perms/serializers/permission.py:74 #: perms/serializers/permission.py:52 perms/serializers/permission.py:74
@ -734,7 +734,7 @@ msgstr "密码规则"
#: terminal/models/component/terminal.py:85 #: terminal/models/component/terminal.py:85
#: terminal/models/virtualapp/provider.py:10 #: terminal/models/virtualapp/provider.py:10
#: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87
#: users/forms/profile.py:32 users/models/group.py:13 #: users/forms/profile.py:33 users/models/group.py:13
#: users/models/preference.py:11 users/models/user/__init__.py:64 #: users/models/preference.py:11 users/models/user/__init__.py:64
#: xpack/plugins/cloud/models.py:34 xpack/plugins/cloud/models.py:310 #: xpack/plugins/cloud/models.py:34 xpack/plugins/cloud/models.py:310
#: xpack/plugins/cloud/serializers/task.py:75 #: xpack/plugins/cloud/serializers/task.py:75
@ -885,8 +885,8 @@ msgstr "类别"
#: acls/serializers/command_acl.py:19 assets/models/automations/base.py:20 #: acls/serializers/command_acl.py:19 assets/models/automations/base.py:20
#: assets/models/cmd_filter.py:74 assets/models/platform.py:96 #: assets/models/cmd_filter.py:74 assets/models/platform.py:96
#: assets/serializers/asset/common.py:146 assets/serializers/platform.py:159 #: assets/serializers/asset/common.py:146 assets/serializers/platform.py:159
#: assets/serializers/platform.py:171 audits/serializers.py:75 #: assets/serializers/platform.py:171 audits/serializers.py:77
#: audits/serializers.py:192 #: audits/serializers.py:194
#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:153 #: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:153
#: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:40 #: perms/serializers/user_permission.py:27 terminal/models/applet/applet.py:40
#: terminal/models/component/storage.py:58 #: terminal/models/component/storage.py:58
@ -959,7 +959,7 @@ msgstr "ID"
#: acls/templates/acls/user_login_reminder.html:8 #: acls/templates/acls/user_login_reminder.html:8
#: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54
#: audits/models.py:90 audits/models.py:172 audits/models.py:271 #: audits/models.py:90 audits/models.py:172 audits/models.py:271
#: audits/serializers.py:193 authentication/models/connection_token.py:32 #: audits/serializers.py:195 authentication/models/connection_token.py:32
#: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16 #: authentication/models/ssh_key.py:22 authentication/models/sso_token.py:16
#: notifications/models/notification.py:12 #: notifications/models/notification.py:12
#: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 #: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63
@ -1136,17 +1136,17 @@ msgstr "参数设置,目前只对 AIX LINUX UNIX 类型的资产有效。"
msgid "Automation task execution" msgid "Automation task execution"
msgstr "自动化任务执行历史" msgstr "自动化任务执行历史"
#: accounts/signal_handlers.py:48 #: accounts/signal_handlers.py:51
#, python-format #, python-format
msgid "Push related accounts to assets: %s, by system" msgid "Push related accounts to assets: %s, by system"
msgstr "推送账号到资产: %s, 由系统执行" msgstr "推送账号到资产: %s, 由系统执行"
#: accounts/signal_handlers.py:57 #: accounts/signal_handlers.py:60
#, python-format #, python-format
msgid "Add account: %s" msgid "Add account: %s"
msgstr "添加账号: %s" msgstr "添加账号: %s"
#: accounts/signal_handlers.py:59 #: accounts/signal_handlers.py:62
#, python-format #, python-format
msgid "Delete account: %s" msgid "Delete account: %s"
msgstr "删除账号: %s" msgstr "删除账号: %s"
@ -1269,11 +1269,11 @@ msgid ""
" Accounts' this task will be executed" " Accounts' this task will be executed"
msgstr "当在控制台-账号模板-账号-同步更新账号信息点击同步时,执行该任务" msgstr "当在控制台-账号模板-账号-同步更新账号信息点击同步时,执行该任务"
#: accounts/tasks/vault.py:32 #: accounts/tasks/vault.py:33
msgid "Sync secret to vault" msgid "Sync secret to vault"
msgstr "同步密文到 vault" msgstr "同步密文到 vault"
#: accounts/tasks/vault.py:34 #: accounts/tasks/vault.py:35
msgid "" msgid ""
"When clicking 'Sync' in 'System Settings - Features - Account Storage' this " "When clicking 'Sync' in 'System Settings - Features - Account Storage' this "
"task will be executed" "task will be executed"
@ -1388,7 +1388,7 @@ msgstr "激活中"
#: acls/models/base.py:81 perms/serializers/permission.py:42 #: acls/models/base.py:81 perms/serializers/permission.py:42
#: tickets/models/flow.py:23 users/models/preference.py:16 #: tickets/models/flow.py:23 users/models/preference.py:16
#: users/serializers/group.py:21 users/serializers/user.py:424 #: users/serializers/group.py:21 users/serializers/user.py:432
msgid "Users" msgid "Users"
msgstr "用户" msgstr "用户"
@ -1400,7 +1400,7 @@ msgid "Accounts"
msgstr "账号" msgstr "账号"
#: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60
#: audits/serializers.py:38 ops/serializers/job.py:79 terminal/const.py:88 #: audits/serializers.py:38 ops/serializers/job.py:91 terminal/const.py:88
#: terminal/models/session/session.py:43 terminal/serializers/command.py:18 #: terminal/models/session/session.py:43 terminal/serializers/command.py:18
#: 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
@ -1588,7 +1588,7 @@ msgid "Login city"
msgstr "登录城市" msgstr "登录城市"
#: acls/templates/acls/user_login_reminder.html:11 audits/models.py:197 #: acls/templates/acls/user_login_reminder.html:11 audits/models.py:197
#: audits/models.py:266 audits/serializers.py:90 #: audits/models.py:266 audits/serializers.py:92
msgid "User agent" msgid "User agent"
msgstr "用户代理" msgstr "用户代理"
@ -1722,7 +1722,7 @@ msgstr "脚本"
#: assets/const/category.py:10 assets/models/asset/host.py:8 #: assets/const/category.py:10 assets/models/asset/host.py:8
#: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:76 #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:76
#: settings/serializers/feature.py:71 settings/serializers/feature.py:84 #: settings/serializers/feature.py:80 settings/serializers/feature.py:93
#: settings/serializers/msg.py:30 terminal/models/component/endpoint.py:14 #: settings/serializers/msg.py:30 terminal/models/component/endpoint.py:14
#: terminal/serializers/applet.py:17 xpack/plugins/cloud/manager.py:89 #: terminal/serializers/applet.py:17 xpack/plugins/cloud/manager.py:89
#: xpack/plugins/cloud/serializers/account_attrs.py:72 #: xpack/plugins/cloud/serializers/account_attrs.py:72
@ -2031,7 +2031,7 @@ msgstr "忽略证书校验"
msgid "Postgresql SSL mode" msgid "Postgresql SSL mode"
msgstr "PostgreSQL SSL 模式" msgstr "PostgreSQL SSL 模式"
#: assets/models/asset/gpt.py:8 settings/serializers/feature.py:114 #: assets/models/asset/gpt.py:8 settings/serializers/feature.py:123
msgid "Proxy" msgid "Proxy"
msgstr "代理" msgstr "代理"
@ -2858,7 +2858,7 @@ msgstr "是"
msgid "No" msgid "No"
msgstr "否" msgstr "否"
#: audits/models.py:47 rbac/tree.py:67 #: audits/models.py:47
msgid "Job audit log" msgid "Job audit log"
msgstr "作业审计日志" msgstr "作业审计日志"
@ -2867,7 +2867,7 @@ msgstr "作业审计日志"
msgid "Remote addr" msgid "Remote addr"
msgstr "远端地址" msgstr "远端地址"
#: audits/models.py:61 audits/serializers.py:60 #: audits/models.py:61 audits/serializers.py:62
msgid "Operate" msgid "Operate"
msgstr "操作" msgstr "操作"
@ -2892,12 +2892,12 @@ msgstr "会话"
msgid "File transfer log" msgid "File transfer log"
msgstr "文件传输" msgstr "文件传输"
#: audits/models.py:94 audits/serializers.py:108 #: audits/models.py:94 audits/serializers.py:110
msgid "Resource Type" msgid "Resource Type"
msgstr "资源类型" msgstr "资源类型"
#: audits/models.py:95 audits/models.py:98 audits/models.py:144 #: audits/models.py:95 audits/models.py:98 audits/models.py:144
#: audits/serializers.py:107 labels/serializers.py:46 #: audits/serializers.py:109 labels/serializers.py:46
msgid "Resource" msgid "Resource"
msgstr "资源" msgstr "资源"
@ -2939,9 +2939,9 @@ msgstr "登录方式"
msgid "Login IP" msgid "Login IP"
msgstr "登录 IP" msgstr "登录 IP"
#: audits/models.py:200 audits/serializers.py:74 #: audits/models.py:200 audits/serializers.py:76
#: authentication/templates/authentication/_mfa_confirm_modal.html:14 #: authentication/templates/authentication/_mfa_confirm_modal.html:14
#: users/forms/profile.py:63 users/models/user/__init__.py:86 #: users/forms/profile.py:64 users/models/user/__init__.py:86
#: users/serializers/profile.py:70 #: users/serializers/profile.py:70
msgid "MFA" msgid "MFA"
msgstr "MFA" msgstr "MFA"
@ -2992,20 +2992,20 @@ msgstr "汇总"
msgid "Execution cycle" msgid "Execution cycle"
msgstr "周期执行" msgstr "周期执行"
#: audits/serializers.py:91 #: audits/serializers.py:93
msgid "Reason display" msgid "Reason display"
msgstr "原因描述" msgstr "原因描述"
#: audits/serializers.py:92 audits/serializers.py:206 #: audits/serializers.py:94 audits/serializers.py:208
msgid "Auth backend display" msgid "Auth backend display"
msgstr "认证方式" msgstr "认证方式"
#: audits/serializers.py:156 #: audits/serializers.py:158
#, python-format #, python-format
msgid "%s %s this resource" msgid "%s %s this resource"
msgstr "用户 %s %s 了当前资源" msgstr "用户 %s %s 了当前资源"
#: audits/serializers.py:194 authentication/models/connection_token.py:47 #: audits/serializers.py:196 authentication/models/connection_token.py:47
#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80
#: tickets/models/ticket/apply_application.py:31 #: tickets/models/ticket/apply_application.py:31
#: tickets/models/ticket/apply_asset.py:20 users/models/user/__init__.py:105 #: tickets/models/ticket/apply_asset.py:20 users/models/user/__init__.py:105
@ -3150,22 +3150,22 @@ msgstr "ACL 动作是复核"
msgid "Current user not support mfa type: {}" msgid "Current user not support mfa type: {}"
msgstr "当前用户不支持 MFA 类型: {}" msgstr "当前用户不支持 MFA 类型: {}"
#: authentication/api/password.py:33 terminal/api/session/session.py:334 #: authentication/api/password.py:34 terminal/api/session/session.py:334
#: users/views/profile/reset.py:63 #: users/views/profile/reset.py:63
msgid "User does not exist: {}" msgid "User does not exist: {}"
msgstr "用户不存在: {}" msgstr "用户不存在: {}"
#: authentication/api/password.py:33 users/views/profile/reset.py:166 #: authentication/api/password.py:34 users/views/profile/reset.py:166
msgid "No user matched" msgid "No user matched"
msgstr "没有匹配到用户" msgstr "没有匹配到用户"
#: authentication/api/password.py:37 #: authentication/api/password.py:38
msgid "" msgid ""
"The user is from {}, please go to the corresponding system to change the " "The user is from {}, please go to the corresponding system to change the "
"password" "password"
msgstr "用户来自 {} 请去相应系统修改密码" msgstr "用户来自 {} 请去相应系统修改密码"
#: authentication/api/password.py:65 #: authentication/api/password.py:69
#: authentication/templates/authentication/login.html:393 #: authentication/templates/authentication/login.html:393
#: users/templates/users/forgot_password.html:41 #: users/templates/users/forgot_password.html:41
#: users/templates/users/forgot_password.html:42 #: users/templates/users/forgot_password.html:42
@ -3404,15 +3404,15 @@ msgstr "您的密码无效"
msgid "Please wait for %s seconds before retry" msgid "Please wait for %s seconds before retry"
msgstr "请在 %s 秒后重试" msgstr "请在 %s 秒后重试"
#: authentication/errors/redirect.py:85 authentication/mixins.py:365 #: authentication/errors/redirect.py:85 authentication/mixins.py:373
msgid "Your password is too simple, please change it for security" msgid "Your password is too simple, please change it for security"
msgstr "你的密码过于简单,为了安全,请修改" msgstr "你的密码过于简单,为了安全,请修改"
#: authentication/errors/redirect.py:93 authentication/mixins.py:374 #: authentication/errors/redirect.py:93 authentication/mixins.py:382
msgid "You should to change your password before login" msgid "You should to change your password before login"
msgstr "登录完成前,请先修改密码" msgstr "登录完成前,请先修改密码"
#: authentication/errors/redirect.py:101 authentication/mixins.py:383 #: authentication/errors/redirect.py:101 authentication/mixins.py:391
msgid "Your password has expired, please reset before logging in" msgid "Your password has expired, please reset before logging in"
msgstr "您的密码已过期,先修改再登录" msgstr "您的密码已过期,先修改再登录"
@ -3433,7 +3433,7 @@ msgstr "MFA 类型"
msgid "Captcha" msgid "Captcha"
msgstr "验证码" msgstr "验证码"
#: authentication/forms.py:66 users/forms/profile.py:27 #: authentication/forms.py:66 users/forms/profile.py:28
msgid "MFA code" msgid "MFA code"
msgstr "MFA 验证码" msgstr "MFA 验证码"
@ -3497,28 +3497,28 @@ msgstr "Radius 动态安全码"
msgid "Radius global enabled, cannot disable" msgid "Radius global enabled, cannot disable"
msgstr "Radius MFA 全局开启,无法被禁用" msgstr "Radius MFA 全局开启,无法被禁用"
#: authentication/mfa/sms.py:7 #: authentication/mfa/sms.py:8
msgid "SMS verify code invalid" msgid "SMS verify code invalid"
msgstr "短信验证码校验失败" msgstr "短信验证码校验失败"
#: authentication/mfa/sms.py:12 authentication/serializers/password_mfa.py:17 #: authentication/mfa/sms.py:13 authentication/serializers/password_mfa.py:17
#: authentication/serializers/password_mfa.py:25 #: authentication/serializers/password_mfa.py:25
#: settings/serializers/auth/sms.py:18 settings/serializers/auth/sms.py:36 #: settings/serializers/auth/sms.py:18 settings/serializers/auth/sms.py:36
#: users/forms/profile.py:103 users/forms/profile.py:108 #: users/forms/profile.py:104 users/forms/profile.py:111
#: users/templates/users/forgot_password.html:157 #: users/templates/users/forgot_password.html:157
#: users/views/profile/reset.py:100 #: users/views/profile/reset.py:100
msgid "SMS" msgid "SMS"
msgstr "短信" msgstr "短信"
#: authentication/mfa/sms.py:13 #: authentication/mfa/sms.py:14
msgid "SMS verification code" msgid "SMS verification code"
msgstr "短信验证码" msgstr "短信验证码"
#: authentication/mfa/sms.py:57 #: authentication/mfa/sms.py:63
msgid "Set phone number to enable" msgid "Set phone number to enable"
msgstr "设置手机号码启用" msgstr "设置手机号码启用"
#: authentication/mfa/sms.py:61 #: authentication/mfa/sms.py:67
msgid "Clear phone number to disable" msgid "Clear phone number to disable"
msgstr "清空手机号码禁用" msgstr "清空手机号码禁用"
@ -3536,11 +3536,11 @@ msgid ""
" The current user source is {}. Please contact the administrator." " The current user source is {}. Please contact the administrator."
msgstr "管理员已开启'仅允许从用户来源登录',当前用户来源为{},请联系管理员。" msgstr "管理员已开启'仅允许从用户来源登录',当前用户来源为{},请联系管理员。"
#: authentication/mixins.py:311 #: authentication/mixins.py:319
msgid "The MFA type ({}) is not enabled" msgid "The MFA type ({}) is not enabled"
msgstr "该 MFA ({}) 方式没有启用" msgstr "该 MFA ({}) 方式没有启用"
#: authentication/mixins.py:353 #: authentication/mixins.py:361
msgid "Please change your password" msgid "Please change your password"
msgstr "请修改密码" msgstr "请修改密码"
@ -3641,7 +3641,7 @@ msgid "Private key"
msgstr "ssh私钥" msgstr "ssh私钥"
#: authentication/models/ssh_key.py:18 settings/serializers/terminal.py:34 #: authentication/models/ssh_key.py:18 settings/serializers/terminal.py:34
#: users/forms/profile.py:172 users/models/user/__init__.py:96 #: users/forms/profile.py:175 users/models/user/__init__.py:96
#: xpack/plugins/cloud/serializers/account_attrs.py:210 #: xpack/plugins/cloud/serializers/account_attrs.py:210
msgid "Public key" msgid "Public key"
msgstr "SSH公钥" msgstr "SSH公钥"
@ -3752,7 +3752,7 @@ msgid ""
"downloaded once" "downloaded once"
msgstr "创建完成后请下载私钥,每个私钥只有一次下载机会" msgstr "创建完成后请下载私钥,每个私钥只有一次下载机会"
#: authentication/serializers/ssh_key.py:57 users/forms/profile.py:161 #: authentication/serializers/ssh_key.py:57 users/forms/profile.py:164
#: users/serializers/profile.py:133 users/serializers/profile.py:160 #: users/serializers/profile.py:133 users/serializers/profile.py:160
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "SSH密钥不合法" msgstr "SSH密钥不合法"
@ -3886,7 +3886,7 @@ msgstr "重新申请"
#: authentication/templates/authentication/_msg_reset_password_code.html:12 #: authentication/templates/authentication/_msg_reset_password_code.html:12
#: terminal/models/session/sharing.py:27 terminal/models/session/sharing.py:97 #: terminal/models/session/sharing.py:27 terminal/models/session/sharing.py:97
#: terminal/templates/terminal/_msg_session_sharing.html:12 #: terminal/templates/terminal/_msg_session_sharing.html:12
#: users/forms/profile.py:106 users/templates/users/forgot_password.html:98 #: users/forms/profile.py:108 users/templates/users/forgot_password.html:98
msgid "Verify code" msgid "Verify code"
msgstr "验证码" msgstr "验证码"
@ -3934,7 +3934,7 @@ msgstr "如果这次公钥更新不是由你发起的,那么你的账号可能
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: authentication/templates/authentication/face_capture.html:44 #: authentication/templates/authentication/face_capture.html:14
msgid "Retry" msgid "Retry"
msgstr "重试" msgstr "重试"
@ -4706,6 +4706,10 @@ msgstr "Ansible 已禁用"
msgid "Skip hosts below:" msgid "Skip hosts below:"
msgstr "跳过以下主机: " msgstr "跳过以下主机: "
#: ops/api/adhoc.py:32
msgid "Deleting other people's script is not allowed"
msgstr "不允许删除别人的脚本"
#: ops/api/celery.py:66 ops/api/celery.py:81 #: ops/api/celery.py:66 ops/api/celery.py:81
msgid "Waiting task start" msgid "Waiting task start"
msgstr "等待任务开始" msgstr "等待任务开始"
@ -4750,6 +4754,10 @@ msgid ""
"The task is being created and cannot be interrupted. Please try again later." "The task is being created and cannot be interrupted. Please try again later."
msgstr "正在创建任务,无法中断,请稍后重试。" msgstr "正在创建任务,无法中断,请稍后重试。"
#: ops/api/playbook.py:49
msgid "Deleting other people's playbook is not allowed"
msgstr "不允许删除别人的playbook"
#: ops/api/playbook.py:55 #: ops/api/playbook.py:55
msgid "Currently playbook is being used in a job" msgid "Currently playbook is being used in a job"
msgstr "当前 playbook 正在作业中使用" msgstr "当前 playbook 正在作业中使用"
@ -4815,7 +4823,7 @@ msgid "VCS"
msgstr "VCS" msgstr "VCS"
#: ops/const.py:38 ops/models/adhoc.py:44 ops/models/variable.py:26 #: ops/const.py:38 ops/models/adhoc.py:44 ops/models/variable.py:26
#: settings/serializers/feature.py:145 #: settings/serializers/feature.py:154
msgid "Adhoc" msgid "Adhoc"
msgstr "命令" msgstr "命令"
@ -5024,7 +5032,7 @@ msgstr "运行用户"
msgid "Run as policy" msgid "Run as policy"
msgstr "用户策略" msgstr "用户策略"
#: ops/models/job.py:223 ops/models/variable.py:28 ops/serializers/job.py:98 #: ops/models/job.py:223 ops/models/variable.py:28 ops/serializers/job.py:110
#: terminal/notifications.py:182 #: terminal/notifications.py:182
msgid "Job" msgid "Job"
msgstr "作业" msgstr "作业"
@ -5115,23 +5123,23 @@ msgstr "下次执行时间"
msgid "Execute after saving" msgid "Execute after saving"
msgstr "保存后执行" msgstr "保存后执行"
#: ops/serializers/job.py:58 terminal/serializers/session.py:49 #: ops/serializers/job.py:70 terminal/serializers/session.py:49
msgid "Duration" msgid "Duration"
msgstr "时长" msgstr "时长"
#: ops/serializers/job.py:78 #: ops/serializers/job.py:90
msgid "Job type" msgid "Job type"
msgstr "任务类型" msgstr "任务类型"
#: ops/serializers/job.py:81 terminal/serializers/session.py:58 #: ops/serializers/job.py:93 terminal/serializers/session.py:58
msgid "Is finished" msgid "Is finished"
msgstr "是否完成" msgstr "是否完成"
#: ops/serializers/job.py:95 #: ops/serializers/job.py:107
msgid "Task id" msgid "Task id"
msgstr "任务 ID" msgstr "任务 ID"
#: ops/serializers/job.py:104 #: ops/serializers/job.py:116
msgid "You do not have permission for the current job." msgid "You do not have permission for the current job."
msgstr "你没有当前作业的权限。" msgstr "你没有当前作业的权限。"
@ -5441,7 +5449,7 @@ msgid "today"
msgstr "今天" msgstr "今天"
#: perms/notifications.py:12 perms/notifications.py:44 #: perms/notifications.py:12 perms/notifications.py:44
#: settings/serializers/feature.py:136 #: settings/serializers/feature.py:145
msgid "day" msgid "day"
msgstr "天" msgstr "天"
@ -5696,7 +5704,7 @@ msgstr "账号改密"
msgid "App ops" msgid "App ops"
msgstr "作业中心" msgstr "作业中心"
#: rbac/tree.py:57 settings/serializers/feature.py:142 #: rbac/tree.py:57 settings/serializers/feature.py:151
msgid "Feature" msgid "Feature"
msgstr "功能" msgstr "功能"
@ -5719,10 +5727,14 @@ msgid "Appearance"
msgstr "界面" msgstr "界面"
#: rbac/tree.py:65 xpack/plugins/license/meta.py:10 #: rbac/tree.py:65 xpack/plugins/license/meta.py:10
#: xpack/plugins/license/models.py:155 #: xpack/plugins/license/models.py:154
msgid "License" msgid "License"
msgstr "许可证" msgstr "许可证"
#: rbac/tree.py:67
msgid "Job audit"
msgstr "作业审计"
#: rbac/tree.py:159 #: rbac/tree.py:159
msgid "App organizations" msgid "App organizations"
msgstr "组织管理" msgstr "组织管理"
@ -5731,8 +5743,8 @@ msgstr "组织管理"
msgid "Ticket comment" msgid "Ticket comment"
msgstr "工单评论" msgstr "工单评论"
#: rbac/tree.py:161 settings/serializers/feature.py:123 #: rbac/tree.py:161 settings/serializers/feature.py:132
#: settings/serializers/feature.py:125 tickets/models/ticket/general.py:308 #: settings/serializers/feature.py:134 tickets/models/ticket/general.py:308
msgid "Ticket" msgid "Ticket"
msgstr "工单" msgstr "工单"
@ -6143,13 +6155,13 @@ msgstr "图标"
msgid "Service provider" msgid "Service provider"
msgstr "服务提供商" msgstr "服务提供商"
#: settings/serializers/auth/oauth2.py:31 settings/serializers/feature.py:87 #: settings/serializers/auth/oauth2.py:31 settings/serializers/feature.py:96
#: xpack/plugins/cloud/serializers/account_attrs.py:35 #: xpack/plugins/cloud/serializers/account_attrs.py:35
msgid "Client ID" msgid "Client ID"
msgstr "客户端 ID" msgstr "客户端 ID"
#: settings/serializers/auth/oauth2.py:34 settings/serializers/auth/oidc.py:24 #: settings/serializers/auth/oauth2.py:34 settings/serializers/auth/oidc.py:24
#: settings/serializers/feature.py:90 #: settings/serializers/feature.py:99
#: xpack/plugins/cloud/serializers/account_attrs.py:38 #: xpack/plugins/cloud/serializers/account_attrs.py:38
msgid "Client Secret" msgid "Client Secret"
msgstr "客户端密钥" msgstr "客户端密钥"
@ -6590,19 +6602,19 @@ msgstr "结束日期"
msgid "Announcement" msgid "Announcement"
msgstr "公告" msgstr "公告"
#: settings/serializers/feature.py:47 settings/serializers/feature.py:50 #: settings/serializers/feature.py:56 settings/serializers/feature.py:59
msgid "Vault" msgid "Vault"
msgstr "启用 Vault" msgstr "启用 Vault"
#: settings/serializers/feature.py:53 #: settings/serializers/feature.py:62
msgid "Vault provider" msgid "Vault provider"
msgstr "保管库服务商" msgstr "保管库服务商"
#: settings/serializers/feature.py:57 #: settings/serializers/feature.py:66
msgid "Record limit" msgid "Record limit"
msgstr "记录限制" msgstr "记录限制"
#: settings/serializers/feature.py:59 #: settings/serializers/feature.py:68
msgid "" msgid ""
"If the specific value is less than 999 (default), the system will " "If the specific value is less than 999 (default), the system will "
"automatically perform a task every night: check and delete historical " "automatically perform a task every night: check and delete historical "
@ -6612,83 +6624,83 @@ msgstr ""
"若特定数值小于999系统将在每日晚间自动执行任务检查并删除超出预定数量的历史" "若特定数值小于999系统将在每日晚间自动执行任务检查并删除超出预定数量的历史"
"账号。如果该数值达到或超过999则不进行任何历史账号的删除操作。" "账号。如果该数值达到或超过999则不进行任何历史账号的删除操作。"
#: settings/serializers/feature.py:77 #: settings/serializers/feature.py:86
msgid "Mount Point" msgid "Mount Point"
msgstr "挂载点" msgstr "挂载点"
#: settings/serializers/feature.py:93 #: settings/serializers/feature.py:102
#: xpack/plugins/cloud/serializers/account_attrs.py:41 #: xpack/plugins/cloud/serializers/account_attrs.py:41
msgid "Tenant ID" msgid "Tenant ID"
msgstr "租户 ID" msgstr "租户 ID"
#: settings/serializers/feature.py:98 settings/serializers/feature.py:104 #: settings/serializers/feature.py:107 settings/serializers/feature.py:113
msgid "Chat AI" msgid "Chat AI"
msgstr "聊天 AI" msgstr "聊天 AI"
#: settings/serializers/feature.py:107 #: settings/serializers/feature.py:116
msgid "GPT Base URL" msgid "GPT Base URL"
msgstr "GPT 地址" msgstr "GPT 地址"
#: settings/serializers/feature.py:108 #: settings/serializers/feature.py:117
msgid "The base URL of the GPT service. For example: https://api.openai.com/v1" msgid "The base URL of the GPT service. For example: https://api.openai.com/v1"
msgstr "GPT 服务的基本 URL。例如https://api.openai.com/v1" msgstr "GPT 服务的基本 URL。例如https://api.openai.com/v1"
#: settings/serializers/feature.py:111 templates/_header_bar.html:96 #: settings/serializers/feature.py:120 templates/_header_bar.html:96
msgid "API Key" msgid "API Key"
msgstr "API Key" msgstr "API Key"
#: settings/serializers/feature.py:115 #: settings/serializers/feature.py:124
msgid "" msgid ""
"The proxy server address of the GPT service. For example: http://ip:port" "The proxy server address of the GPT service. For example: http://ip:port"
msgstr "GPT 服务的代理服务器地址。例如http://ip:port" msgstr "GPT 服务的代理服务器地址。例如http://ip:port"
#: settings/serializers/feature.py:118 #: settings/serializers/feature.py:127
msgid "GPT Model" msgid "GPT Model"
msgstr "GPT 模型" msgstr "GPT 模型"
#: settings/serializers/feature.py:127 #: settings/serializers/feature.py:136
msgid "Approval without login" msgid "Approval without login"
msgstr "免登录审批" msgstr "免登录审批"
#: settings/serializers/feature.py:128 #: settings/serializers/feature.py:137
msgid "Allow direct approval ticket without login" msgid "Allow direct approval ticket without login"
msgstr "允许无需登录直接批准工单" msgstr "允许无需登录直接批准工单"
#: settings/serializers/feature.py:132 #: settings/serializers/feature.py:141
msgid "Period" msgid "Period"
msgstr "时段" msgstr "时段"
#: settings/serializers/feature.py:133 #: settings/serializers/feature.py:142
msgid "" msgid ""
"The default authorization time period when applying for assets via a ticket" "The default authorization time period when applying for assets via a ticket"
msgstr "工单申请资产的默认授权时间段" msgstr "工单申请资产的默认授权时间段"
#: settings/serializers/feature.py:136 #: settings/serializers/feature.py:145
msgid "hour" msgid "hour"
msgstr "时" msgstr "时"
#: settings/serializers/feature.py:137 #: settings/serializers/feature.py:146
msgid "Unit" msgid "Unit"
msgstr "单位" msgstr "单位"
#: settings/serializers/feature.py:137 #: settings/serializers/feature.py:146
msgid "The unit of period" msgid "The unit of period"
msgstr "执行周期" msgstr "执行周期"
#: settings/serializers/feature.py:146 #: settings/serializers/feature.py:155
msgid "" msgid ""
"Allow users to execute batch commands in the Workbench - Job Center - Adhoc" "Allow users to execute batch commands in the Workbench - Job Center - Adhoc"
msgstr "允许用户在工作台 - 作业中心 - Adhoc 中执行批量命令" msgstr "允许用户在工作台 - 作业中心 - Adhoc 中执行批量命令"
#: settings/serializers/feature.py:150 #: settings/serializers/feature.py:159
msgid "Command blacklist" msgid "Command blacklist"
msgstr "作业中心命令黑名单" msgstr "作业中心命令黑名单"
#: settings/serializers/feature.py:151 #: settings/serializers/feature.py:160
msgid "Command blacklist in Adhoc" msgid "Command blacklist in Adhoc"
msgstr "作业中心命令黑名单" msgstr "作业中心命令黑名单"
#: settings/serializers/feature.py:156 #: settings/serializers/feature.py:165
#: terminal/models/virtualapp/provider.py:17 #: terminal/models/virtualapp/provider.py:17
#: terminal/models/virtualapp/virtualapp.py:36 #: terminal/models/virtualapp/virtualapp.py:36
#: terminal/models/virtualapp/virtualapp.py:97 #: terminal/models/virtualapp/virtualapp.py:97
@ -6696,11 +6708,11 @@ msgstr "作业中心命令黑名单"
msgid "Virtual app" msgid "Virtual app"
msgstr "虚拟应用" msgstr "虚拟应用"
#: settings/serializers/feature.py:159 #: settings/serializers/feature.py:168
msgid "Virtual App" msgid "Virtual App"
msgstr "虚拟应用" msgstr "虚拟应用"
#: settings/serializers/feature.py:161 #: settings/serializers/feature.py:170
msgid "" msgid ""
"Virtual applications, you can use the Linux operating system as an " "Virtual applications, you can use the Linux operating system as an "
"application server in remote applications." "application server in remote applications."
@ -7351,7 +7363,7 @@ msgstr "文档"
msgid "Commercial support" msgid "Commercial support"
msgstr "商业支持" msgstr "商业支持"
#: templates/_header_bar.html:85 users/forms/profile.py:43 #: templates/_header_bar.html:85 users/forms/profile.py:44
msgid "Profile" msgid "Profile"
msgstr "个人信息" msgstr "个人信息"
@ -9051,7 +9063,7 @@ msgstr "无法删除全部用户"
msgid "Create failed. The number of SSH keys has reached the limit" msgid "Create failed. The number of SSH keys has reached the limit"
msgstr "创建失败SSH密钥数量已达到上限" msgstr "创建失败SSH密钥数量已达到上限"
#: users/forms/profile.py:48 #: users/forms/profile.py:49
msgid "" msgid ""
"When enabled, you will enter the MFA binding process the next time you log " "When enabled, you will enter the MFA binding process the next time you log "
"in. you can also directly bind in \"personal information -> quick " "in. you can also directly bind in \"personal information -> quick "
@ -9060,11 +9072,11 @@ msgstr ""
"启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在 (个人信息->快速" "启用之后您将会在下次登录时进入多因子认证绑定流程;您也可以在 (个人信息->快速"
"修改->设置 MFA 多因子认证)中直接绑定!" "修改->设置 MFA 多因子认证)中直接绑定!"
#: users/forms/profile.py:59 #: users/forms/profile.py:60
msgid "* Enable MFA to make the account more secure." msgid "* Enable MFA to make the account more secure."
msgstr "* 启用 MFA 多因子认证,使账号更加安全。" msgstr "* 启用 MFA 多因子认证,使账号更加安全。"
#: users/forms/profile.py:68 #: users/forms/profile.py:69
msgid "" msgid ""
"In order to protect you and your company, please keep your account, password " "In order to protect you and your company, please keep your account, password "
"and key sensitive information properly. (for example: setting complex " "and key sensitive information properly. (for example: setting complex "
@ -9073,47 +9085,47 @@ msgstr ""
"为了保护您和公司的安全,请妥善保管您的账号、密码和密钥等重要敏感信息; (如:" "为了保护您和公司的安全,请妥善保管您的账号、密码和密钥等重要敏感信息; (如:"
"设置复杂密码,并启用 MFA 多因子认证)" "设置复杂密码,并启用 MFA 多因子认证)"
#: users/forms/profile.py:82 users/serializers/preference/lina.py:21 #: users/forms/profile.py:83 users/serializers/preference/lina.py:21
msgid "New password" msgid "New password"
msgstr "新密码" msgstr "新密码"
#: users/forms/profile.py:87 users/serializers/preference/lina.py:26 #: users/forms/profile.py:88 users/serializers/preference/lina.py:26
msgid "Confirm password" msgid "Confirm password"
msgstr "确认密码" msgstr "确认密码"
#: users/forms/profile.py:95 #: users/forms/profile.py:96
msgid "Password does not match" msgid "Password does not match"
msgstr "密码不一致" msgstr "密码不一致"
#: users/forms/profile.py:104 #: users/forms/profile.py:105
msgid "The phone number must contain an area code, for example, +86" msgid "The phone number must contain an area code, for example, +86"
msgstr "手机号码必须包含区号,例如 +86" msgstr "手机号码必须包含区号,例如 +86"
#: users/forms/profile.py:120 #: users/forms/profile.py:123
msgid "Old password" msgid "Old password"
msgstr "原来密码" msgstr "原来密码"
#: users/forms/profile.py:130 #: users/forms/profile.py:133
msgid "Old password error" msgid "Old password error"
msgstr "原来密码错误" msgstr "原来密码错误"
#: users/forms/profile.py:140 #: users/forms/profile.py:143
msgid "Automatically configure and download the SSH key" msgid "Automatically configure and download the SSH key"
msgstr "自动配置并下载SSH密钥" msgstr "自动配置并下载SSH密钥"
#: users/forms/profile.py:142 #: users/forms/profile.py:145
msgid "ssh public key" msgid "ssh public key"
msgstr "SSH公钥" msgstr "SSH公钥"
#: users/forms/profile.py:143 #: users/forms/profile.py:146
msgid "ssh-rsa AAAA..." msgid "ssh-rsa AAAA..."
msgstr "ssh-rsa AAAA..." msgstr "ssh-rsa AAAA..."
#: users/forms/profile.py:144 #: users/forms/profile.py:147
msgid "Paste your id_rsa.pub here." msgid "Paste your id_rsa.pub here."
msgstr "复制你的公钥到这里" msgstr "复制你的公钥到这里"
#: users/forms/profile.py:157 #: users/forms/profile.py:160
msgid "Public key should not be the same as your old one." msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同" msgstr "不能和原来的密钥相同"
@ -9422,12 +9434,12 @@ msgstr "MFA"
msgid "Multi-Factor Authentication" msgid "Multi-Factor Authentication"
msgstr "认证" msgstr "认证"
#: users/serializers/user.py:426 #: users/serializers/user.py:434
msgid "" msgid ""
"* For security, only a partial of users is displayed. You can search for more" "* For security, only a partial of users is displayed. You can search for more"
msgstr "* 为安全起见,只显示部分用户。您可以搜索更多" msgstr "* 为安全起见,只显示部分用户。您可以搜索更多"
#: users/serializers/user.py:461 #: users/serializers/user.py:469
msgid "name not unique" msgid "name not unique"
msgstr "名称重复" msgstr "名称重复"
@ -10608,7 +10620,7 @@ msgstr "企业专业版"
msgid "Ultimate edition" msgid "Ultimate edition"
msgstr "企业旗舰版" msgstr "企业旗舰版"
#: xpack/plugins/license/models.py:101 #: xpack/plugins/license/models.py:100
msgid "FIT2CLOUD" msgid "FIT2CLOUD"
msgstr "飞致云" msgstr "飞致云"

View File

@ -69,5 +69,7 @@
"VerifyCode": "Verify Code", "VerifyCode": "Verify Code",
"WaitFileTransfer": "Wait file transfer to finish", "WaitFileTransfer": "Wait file transfer to finish",
"WebSocketClosed": "WebSocket closed", "WebSocketClosed": "WebSocket closed",
"Writable": "Writable" "Writable": "Writable",
"UploadStart": "Upload start",
"UploadEnd": "Upload completed, please wait for further processing"
} }

View File

@ -69,5 +69,7 @@
"VerifyCode": "認証コード", "VerifyCode": "認証コード",
"WaitFileTransfer": "ファイル転送終了待ち", "WaitFileTransfer": "ファイル転送終了待ち",
"WebSocketClosed": "WebSocket 閉店", "WebSocketClosed": "WebSocket 閉店",
"Writable": "書き込み可能" "Writable": "書き込み可能",
"UploadStart": "アップロード開始",
"UploadEnd": "アップロードが完了しました。後の処理をお待ちください"
} }

View File

@ -65,6 +65,8 @@
"UploadSuccess": "上传成功", "UploadSuccess": "上传成功",
"UploadTips": "将文件拖到此处,或点击上传", "UploadTips": "将文件拖到此处,或点击上传",
"UploadTitle": "上传文件", "UploadTitle": "上传文件",
"UploadStart": "上传开始",
"UploadEnd": "上传已完成,请等待后续处理",
"User": "用户", "User": "用户",
"VerifyCode": "验证码", "VerifyCode": "验证码",
"WaitFileTransfer": "等待文件传输结束", "WaitFileTransfer": "等待文件传输结束",

View File

@ -69,5 +69,7 @@
"VerifyCode": "驗證碼", "VerifyCode": "驗證碼",
"WaitFileTransfer": "等待文件傳輸結束", "WaitFileTransfer": "等待文件傳輸結束",
"WebSocketClosed": "WebSocket 已關閉", "WebSocketClosed": "WebSocket 已關閉",
"Writable": "讀寫" "Writable": "讀寫",
"UploadStart": "上傳開始",
"UploadEnd": "上傳已完成,請等待後續處理"
} }

View File

@ -177,6 +177,8 @@
"AwaitingMyApproval": "Assigned", "AwaitingMyApproval": "Assigned",
"Azure": "Azure (China)", "Azure": "Azure (China)",
"Azure_Int": "Azure (International)", "Azure_Int": "Azure (International)",
"AzureKeyVault": "Azure vault",
"HashicorpVault": "HCP vault",
"Backup": "Backup", "Backup": "Backup",
"BackupAccountsHelpText": "Backup account information externally. it can be stored in an external system or sent via email, supporting segmented delivery.", "BackupAccountsHelpText": "Backup account information externally. it can be stored in an external system or sent via email, supporting segmented delivery.",
"BadConflictErrorMsg": "Refreshing, please try again later", "BadConflictErrorMsg": "Refreshing, please try again later",
@ -651,7 +653,7 @@
"JobCenter": "Job center", "JobCenter": "Job center",
"JobCreate": "Create job", "JobCreate": "Create job",
"JobDetail": "Job details", "JobDetail": "Job details",
"JobExecutionLog": "Execution record", "JobExecutionLog": "Executions",
"JobManagement": "Job List", "JobManagement": "Job List",
"JobUpdate": "Update the job", "JobUpdate": "Update the job",
"KingSoftCloud": "KingSoft cloud", "KingSoftCloud": "KingSoft cloud",
@ -1411,7 +1413,7 @@
"forceEnableMFAHelpText": "If force enable, user can not disable by themselves", "forceEnableMFAHelpText": "If force enable, user can not disable by themselves",
"removeWarningMsg": "Are you sure you want to remove", "removeWarningMsg": "Are you sure you want to remove",
"setVariable": "Set variable", "setVariable": "Set variable",
"JobsAudit": "Jobs audit", "JobsAudit": "Job audits",
"JobList": "Job List", "JobList": "Job List",
"StopJobMsg": "Stop job successfully", "StopJobMsg": "Stop job successfully",
"ExtraArgsFormatError": "Format error, please enter according to the requirements", "ExtraArgsFormatError": "Format error, please enter according to the requirements",

View File

@ -177,6 +177,8 @@
"AwaitingMyApproval": "私の承認待ち", "AwaitingMyApproval": "私の承認待ち",
"Azure": "Azure(中国)", "Azure": "Azure(中国)",
"Azure_Int": "アジュール(インターナショナル)", "Azure_Int": "アジュール(インターナショナル)",
"AzureKeyVault": "Azure vault",
"HashicorpVault": "HCP vault",
"Backup": "バックアップ", "Backup": "バックアップ",
"BackupAccountsHelpText": "アカウント情報を外部にバックアップする。外部システムに保存するかメールを送信することもできます、セクション方式をサポートしています", "BackupAccountsHelpText": "アカウント情報を外部にバックアップする。外部システムに保存するかメールを送信することもできます、セクション方式をサポートしています",
"BadConflictErrorMsg": "更新中です、しばらくお待ちください", "BadConflictErrorMsg": "更新中です、しばらくお待ちください",

View File

@ -177,6 +177,8 @@
"AwaitingMyApproval": "待我审批", "AwaitingMyApproval": "待我审批",
"Azure": "Azure (中国)", "Azure": "Azure (中国)",
"Azure_Int": "Azure (国际)", "Azure_Int": "Azure (国际)",
"AzureKeyVault": "Azure vault",
"HashicorpVault": "HCP vault",
"Backup": "备份", "Backup": "备份",
"BackupAccountsHelpText": "备份账号信息到外部。可以存储到外部系统或发送邮件,支持分段方式", "BackupAccountsHelpText": "备份账号信息到外部。可以存储到外部系统或发送邮件,支持分段方式",
"BadConflictErrorMsg": "正在刷新中,请稍后再试", "BadConflictErrorMsg": "正在刷新中,请稍后再试",

View File

@ -238,6 +238,8 @@
"AwaitingMyApproval": "待我審批", "AwaitingMyApproval": "待我審批",
"Azure": "Azure (中國)", "Azure": "Azure (中國)",
"Azure_Int": "Azure (國際)", "Azure_Int": "Azure (國際)",
"AzureKeyVault": "Azure vault",
"HashicorpVault": "HCP vault",
"Backup": "備份", "Backup": "備份",
"BackupAccountsHelpText": "備份帳號資訊至外部。可以儲存到外部系統或寄送郵件,支援分段方式", "BackupAccountsHelpText": "備份帳號資訊至外部。可以儲存到外部系統或寄送郵件,支援分段方式",
"BadConflictErrorMsg": "正在刷新中,請稍後再試", "BadConflictErrorMsg": "正在刷新中,請稍後再試",

View File

@ -18,11 +18,12 @@ if not XPACK_DISABLED:
XPACK_TEMPLATES_DIR = [] XPACK_TEMPLATES_DIR = []
XPACK_CONTEXT_PROCESSOR = [] XPACK_CONTEXT_PROCESSOR = []
XPACK_LICENSE_IS_VALID = False XPACK_LICENSE_IS_VALID = False
XPACK_LICENSE_EDITION = ""
XPACK_LICENSE_INFO = { XPACK_LICENSE_INFO = {
'corporation': corporation, 'corporation': corporation,
} }
XPACK_LICENSE_CONTENT = '' XPACK_LICENSE_CONTENT = 'community'
if XPACK_ENABLED: if XPACK_ENABLED:
from xpack.utils import get_xpack_templates_dir, get_xpack_context_processor from xpack.utils import get_xpack_templates_dir, get_xpack_context_processor

View File

@ -101,7 +101,7 @@ class ResourceDownload(TemplateView):
MRD_VERSION=10.6.7 MRD_VERSION=10.6.7
OPENSSH_VERSION=v9.4.0.0 OPENSSH_VERSION=v9.4.0.0
TINKER_VERSION=v0.1.6 TINKER_VERSION=v0.1.6
VIDEO_PLAYER_VERSION=0.1.9 VIDEO_PLAYER_VERSION=0.2.0
CLIENT_VERSION=v2.1.3 CLIENT_VERSION=v2.1.3
""" """

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.db.models import Q from django.db.models import Q
from django.utils.translation import gettext_lazy as _
from common.api.generic import JMSBulkModelViewSet from common.api.generic import JMSBulkModelViewSet
from common.utils.http import is_true from common.utils.http import is_true
@ -28,7 +29,7 @@ class AdHocViewSet(JMSBulkModelViewSet):
def check_object_permissions(self, request, obj): def check_object_permissions(self, request, obj):
if request.method != 'GET' and obj.creator != request.user: if request.method != 'GET' and obj.creator != request.user:
self.permission_denied( self.permission_denied(
request, message={"detail": "Deleting other people's script is not allowed"} request, message={"detail": _("Deleting other people's script is not allowed")}
) )
return super().check_object_permissions(request, obj) return super().check_object_permissions(request, obj)

View File

@ -105,6 +105,7 @@ class JobViewSet(OrgBulkModelViewSet):
def perform_update(self, serializer): def perform_update(self, serializer):
run_after_save = serializer.validated_data.pop('run_after_save', False) run_after_save = serializer.validated_data.pop('run_after_save', False)
self._parameters = serializer.validated_data.pop('parameters', None)
instance = serializer.save() instance = serializer.save()
if run_after_save: if run_after_save:
self.run_job(instance, serializer) self.run_job(instance, serializer)
@ -227,9 +228,9 @@ class JobExecutionViewSet(OrgBulkModelViewSet):
try: try:
user = request.user user = request.user
if user.has_perm("audits.view_joblog"): if user.has_perm("audits.view_joblog"):
instance = get_object_or_404(JobExecution, pk=task_id) instance = get_object_or_404(JobExecution, task_id=task_id)
else: else:
instance = get_object_or_404(JobExecution, pk=task_id, creator=request.user) instance = get_object_or_404(JobExecution, task_id=task_id, creator=request.user)
except Http404: except Http404:
return Response( return Response(
{'error': _('The task is being created and cannot be interrupted. Please try again later.')}, {'error': _('The task is being created and cannot be interrupted. Please try again later.')},

View File

@ -46,7 +46,7 @@ class PlaybookViewSet(JMSBulkModelViewSet):
def check_object_permissions(self, request, obj): def check_object_permissions(self, request, obj):
if request.method != 'GET' and obj.creator != request.user: if request.method != 'GET' and obj.creator != request.user:
self.permission_denied( self.permission_denied(
request, message={"detail": "Deleting other people's playbook is not allowed"} request, message={"detail": _("Deleting other people's playbook is not allowed")}
) )
return super().check_object_permissions(request, obj) return super().check_object_permissions(request, obj)

View File

@ -38,6 +38,18 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin, W
user = request.user if request else None user = request.user if request else None
return user return user
def get_periodic_variable(self, variables):
periodic_variable = {}
for variable in variables:
periodic_variable[variable['var_name']] = variable['default_value']
return periodic_variable
def validate(self, attrs):
attrs = super().validate(attrs)
if attrs.get('is_periodic') is True:
attrs['periodic_variable'] = self.get_periodic_variable(attrs.get('variable', []))
return attrs
class Meta: class Meta:
model = Job model = Job
read_only_fields = [ read_only_fields = [

View File

@ -4,9 +4,12 @@ import os
import aiofiles import aiofiles
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer from channels.generic.websocket import AsyncJsonWebsocketConsumer
from http.cookies import SimpleCookie
from common.db.utils import close_old_connections from common.db.utils import close_old_connections
from common.utils import get_logger from common.utils import get_logger
from orgs.models import Organization
from orgs.utils import tmp_to_org, current_org
from rbac.builtin import BuiltinRole from rbac.builtin import BuiltinRole
from .ansible.utils import get_ansible_task_log_path from .ansible.utils import get_ansible_task_log_path
from .celery.utils import get_celery_task_log_path from .celery.utils import get_celery_task_log_path
@ -18,6 +21,8 @@ logger = get_logger(__name__)
class TaskLogWebsocket(AsyncJsonWebsocketConsumer): class TaskLogWebsocket(AsyncJsonWebsocketConsumer):
disconnected = False disconnected = False
cookie = None
org = None
user_tasks = ( user_tasks = (
'ops.tasks.run_ops_job', 'ops.tasks.run_ops_job',
'ops.tasks.run_ops_job_execution', 'ops.tasks.run_ops_job_execution',
@ -28,10 +33,25 @@ class TaskLogWebsocket(AsyncJsonWebsocketConsumer):
'ansible': get_ansible_task_log_path 'ansible': get_ansible_task_log_path
} }
def get_cookie(self):
try:
headers = self.scope['headers']
headers_dict = {key.decode('utf-8'): value.decode('utf-8') for key, value in headers}
cookie = SimpleCookie(headers_dict.get('cookie', ''))
except Exception as e:
cookie = SimpleCookie()
return cookie
def get_current_org(self):
oid = self.cookie.get('X-JMS-ORG')
return oid.value if oid else None
async def connect(self): async def connect(self):
user = self.scope["user"] user = self.scope["user"]
if user.is_authenticated: if user.is_authenticated:
await self.accept() await self.accept()
self.cookie = self.get_cookie()
self.org = self.get_current_org()
else: else:
await self.close() await self.close()
@ -51,10 +71,18 @@ class TaskLogWebsocket(AsyncJsonWebsocketConsumer):
@sync_to_async @sync_to_async
def get_current_user_role_ids(self, user): def get_current_user_role_ids(self, user):
roles = user.system_roles.all() | user.org_roles.all() with tmp_to_org(self.org):
org_roles = user.org_roles.all()
system_roles = user.system_roles.all()
roles = system_roles | org_roles
user_role_ids = set(map(str, roles.values_list('id', flat=True))) user_role_ids = set(map(str, roles.values_list('id', flat=True)))
return user_role_ids return user_role_ids
@sync_to_async
def has_perms(self, user, perms):
with tmp_to_org(self.org):
return user.has_perms(perms)
async def receive_json(self, content, **kwargs): async def receive_json(self, content, **kwargs):
task_id = content.get('task') task_id = content.get('task')
task = await self.get_task(task_id) task = await self.get_task(task_id)
@ -71,8 +99,10 @@ class TaskLogWebsocket(AsyncJsonWebsocketConsumer):
user = self.scope['user'] user = self.scope['user']
user_role_ids = await self.get_current_user_role_ids(user) user_role_ids = await self.get_current_user_role_ids(user)
has_admin_auditor_role = bool(admin_auditor_role_ids & user_role_ids) has_admin_auditor_role = bool(admin_auditor_role_ids & user_role_ids)
has_perms = await self.has_perms(user, ['audits.view_joblog'])
if not has_admin_auditor_role and task.name in self.user_tasks and task.creator != user: user_can_view = task.name in self.user_tasks and (task.creator == user or has_perms)
# (有管理员或审计员角色) 或者 (任务是用户自己创建的 或者 有查看任务日志权限), 其他情况没有权限
if not (has_admin_auditor_role or user_can_view):
await self.send_json({'message': 'No permission', 'task': task_id}) await self.send_json({'message': 'No permission', 'task': task_id})
return return

View File

@ -64,7 +64,7 @@ extra_nodes_data = [
{'id': "tasks", "name": _("Task"), "pId": "view_setting"}, {'id': "tasks", "name": _("Task"), "pId": "view_setting"},
{'id': "license", "name": _("License"), "pId": "view_setting"}, {'id': "license", "name": _("License"), "pId": "view_setting"},
{'id': "other", "name": _("Other"), "pId": "view_setting"}, {'id': "other", "name": _("Other"), "pId": "view_setting"},
{'id': "job_audit", "name": _("Job audit log"), "pId": "view_audit"}, {'id': "job_audit", "name": _("Job audit"), "pId": "view_audit"},
] ]
# 将 model 放到其它节点下,而不是本来的 app 中 # 将 model 放到其它节点下,而不是本来的 app 中

View File

@ -1,9 +1,9 @@
import uuid import uuid
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from accounts.const import VaultTypeChoices
from assets.const import Protocol from assets.const import Protocol
from common.serializers.fields import EncryptedField from common.serializers.fields import EncryptedField
from common.utils import date_expired_default from common.utils import date_expired_default
@ -43,7 +43,16 @@ class AnnouncementSettingSerializer(serializers.Serializer):
ANNOUNCEMENT = AnnouncementSerializer(label=_("Announcement")) ANNOUNCEMENT = AnnouncementSerializer(label=_("Announcement"))
class VaultSettingSerializer(serializers.Serializer): class BaseVaultSettingSerializer(serializers.Serializer):
def validate(self, data):
from accounts.signal_handlers import vault_pub_sub
data = super().validate(data)
vault_pub_sub.publish('vault')
return data
class VaultSettingSerializer(BaseVaultSettingSerializer, serializers.Serializer):
PREFIX_TITLE = _('Vault') PREFIX_TITLE = _('Vault')
VAULT_ENABLED = serializers.BooleanField( VAULT_ENABLED = serializers.BooleanField(
@ -65,7 +74,7 @@ class VaultSettingSerializer(serializers.Serializer):
) )
class HashicorpKVSerializer(serializers.Serializer): class HashicorpKVSerializer(BaseVaultSettingSerializer, serializers.Serializer):
PREFIX_TITLE = _('HCP Vault') PREFIX_TITLE = _('HCP Vault')
VAULT_HCP_HOST = serializers.CharField( VAULT_HCP_HOST = serializers.CharField(
max_length=256, allow_blank=True, required=False, label=_('Host') max_length=256, allow_blank=True, required=False, label=_('Host')
@ -78,7 +87,7 @@ class HashicorpKVSerializer(serializers.Serializer):
) )
class AzureKVSerializer(serializers.Serializer): class AzureKVSerializer(BaseVaultSettingSerializer, serializers.Serializer):
PREFIX_TITLE = _('Azure Key Vault') PREFIX_TITLE = _('Azure Key Vault')
VAULT_AZURE_HOST = serializers.CharField( VAULT_AZURE_HOST = serializers.CharField(
max_length=256, allow_blank=True, required=False, label=_('Host') max_length=256, allow_blank=True, required=False, label=_('Host')

View File

@ -50,10 +50,10 @@ p {
</div> </div>
<div class="group"> <div class="group">
<h2>JumpServer {% trans 'Offline video player' %} v0.1.9</h2> <h2>JumpServer {% trans 'Offline video player' %} v{{ VIDEO_PLAYER_VERSION }}</h2>
<ul> <ul>
<li><a href="/download/public/JumpServer.Video.Player-{{ VIDEO_PLAYER_VERSION }}.dmg">jumpserver-video-player.dmg</a></li> <li><a href="/download/public/JumpServerVideoPlayer-{{ VIDEO_PLAYER_VERSION }}.dmg">jumpserver-video-player.dmg</a></li>
<li><a href="/download/public/JumpServer.Video.Player.Setup.{{ VIDEO_PLAYER_VERSION }}.exe">jumpserver-video-player.exe</a></li> <li><a href="/download/public/JumpServerVideoPlayer-{{ VIDEO_PLAYER_VERSION }}.exe">jumpserver-video-player.exe</a></li>
</ul> </ul>
</div> </div>
</div> </div>