diff --git a/apps/accounts/automations/push_account/custom/ssh/main.yml b/apps/accounts/automations/push_account/custom/ssh/main.yml index 966454bfc..345a8027f 100644 --- a/apps/accounts/automations/push_account/custom/ssh/main.yml +++ b/apps/accounts/automations/push_account/custom/ssh/main.yml @@ -20,10 +20,11 @@ become_private_key_path: "{{ jms_custom_become_private_key_path | default(None) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" + recv_timeout: "{{ params.recv_timeout | default(30) }}" register: ping_info delegate_to: localhost - - name: Change asset password (paramiko) + - name: Push asset password (paramiko) custom_command: login_user: "{{ jms_account.username }}" login_password: "{{ jms_account.secret }}" @@ -39,7 +40,10 @@ name: "{{ account.username }}" password: "{{ account.secret }}" commands: "{{ params.commands }}" - first_conn_delay_time: "{{ first_conn_delay_time | default(0.5) }}" + answers: "{{ params.answers }}" + recv_timeout: "{{ params.recv_timeout | default(30) }}" + delay_time: "{{ params.delay_time | default(2) }}" + prompt: "{{ params.prompt | default('.*') }}" ignore_errors: true when: ping_info is succeeded and check_conn_after_change register: change_info @@ -58,5 +62,6 @@ become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" + recv_timeout: "{{ params.recv_timeout | default(30) }}" delegate_to: localhost when: check_conn_after_change \ No newline at end of file diff --git a/apps/accounts/automations/push_account/custom/ssh/manifest.yml b/apps/accounts/automations/push_account/custom/ssh/manifest.yml index 2330224ca..56ff249b1 100644 --- a/apps/accounts/automations/push_account/custom/ssh/manifest.yml +++ b/apps/accounts/automations/push_account/custom/ssh/manifest.yml @@ -10,10 +10,30 @@ protocol: ssh priority: 50 params: - name: commands - type: list + type: text label: "{{ 'Params commands label' | trans }}" - default: [ '' ] + default: '' help_text: "{{ 'Params commands help text' | trans }}" + - name: recv_timeout + type: int + label: "{{ 'Params recv_timeout label' | trans }}" + default: 30 + help_text: "{{ 'Params recv_timeout help text' | trans }}" + - name: delay_time + type: int + label: "{{ 'Params delay_time label' | trans }}" + default: 2 + help_text: "{{ 'Params delay_time help text' | trans }}" + - name: prompt + type: str + label: "{{ 'Params prompt label' | trans }}" + default: '.*' + help_text: "{{ 'Params prompt help text' | trans }}" + - name: answers + type: text + label: "{{ 'Params answer label' | trans }}" + default: '.*' + help_text: "{{ 'Params answer help text' | trans }}" i18n: SSH account push: @@ -22,11 +42,91 @@ i18n: en: 'Custom push using SSH command line' Params commands help text: - zh: '自定义命令中如需包含账号的 账号、密码、SSH 连接的用户密码 字段,
请使用 {username}、{password}、{login_password}格式,执行任务时会进行替换 。
比如针对 Cisco 主机进行改密,一般需要配置五条命令:
1. enable
2. {login_password}
3. configure terminal
4. username {username} privilege 0 password {password}
5. end' - ja: 'カスタム コマンドに SSH 接続用のアカウント番号、パスワード、ユーザー パスワード フィールドを含める必要がある場合は、
{ユーザー名}、{パスワード}、{login_password& を使用してください。 # 125; 形式。タスクの実行時に置き換えられます。
たとえば、Cisco ホストのパスワードを変更するには、通常、次の 5 つのコマンドを設定する必要があります:
1.enable
2.{login_password}
3 .ターミナルの設定
4. ユーザー名 {ユーザー名} 権限 0 パスワード {パスワード}
5. 終了' - en: 'If the custom command needs to include the account number, password, and user password field for SSH connection,
Please use {username}, {password}, {login_password&# 125; format, which will be replaced when executing the task.
For example, to change the password of a Cisco host, you generally need to configure five commands:
1. enable
2. {login_password}
3. configure terminal
4. username {username} privilege 0 password {password}
5. end' + zh: | + 请将命令中的指定位置改成特殊符号
+ 1. 推送账号 -> {username}
+ 2. 推送密码 -> {password}
+ 3. 登录用户密码 -> {login_password}
+ 多条命令使用换行分割,执行任务时系统会根据特殊符号替换真实数据。
+ 比如针对 Cisco 主机进行推送,一般需要配置五条命令:
+ enable
+ {login_password}
+ configure terminal
+ username {username} privilege 0 password {password}
+ end
+ ja: | + コマンド内の指定された位置を特殊記号に変更してください。
+ 新しいパスワード(アカウント押す) -> {username}
+ 新しいパスワード(パスワード押す) -> {password}
+ ログインユーザーパスワード -> {login_password}
+ 複数のコマンドは改行で区切り、タスクを実行するときにシステムは特殊記号を使用して実際のデータを置き換えます。
+ 例えば、Cisco機器のパスワードを変更する場合、一般的には5つのコマンドを設定する必要があります:
+ enable
+ {login_password}
+ configure terminal
+ username {username} privilege 0 password {password}
+ end
+ en: | + Please change the specified positions in the command to special symbols.
+ Change password account -> {username}
+ Change password -> {password}
+ Login user password -> {login_password}
+ Multiple commands are separated by new lines, and when executing tasks,
+ the system will replace the special symbols with real data.
+ For example, to push the password for a Cisco device, you generally need to configure five commands:
+ enable
+ {login_password}
+ configure terminal
+ username {username} privilege 0 password {password}
+ end
Params commands label: zh: '自定义命令' ja: 'カスタムコマンド' en: 'Custom command' + + Params recv_timeout label: + zh: '超时时间' + ja: 'タイムアウト' + en: 'Timeout' + + Params recv_timeout help text: + zh: '等待命令结果返回的超时时间(秒)' + ja: 'コマンドの結果を待つタイムアウト時間(秒)' + en: 'The timeout for waiting for the command result to return (Seconds)' + + Params delay_time label: + zh: '延迟发送时间' + ja: '遅延送信時間' + en: 'Delayed send time' + + Params delay_time help text: + zh: '每条命令延迟发送的时间间隔(秒)' + ja: '各コマンド送信の遅延間隔(秒)' + en: 'Time interval for each command delay in sending (Seconds)' + + Params prompt label: + zh: '提示符' + ja: 'ヒント' + en: 'Prompt' + + Params prompt help text: + zh: '终端连接后显示的提示符信息(正则表达式)' + ja: 'ターミナル接続後に表示されるプロンプト情報(正規表現)' + en: 'Prompt information displayed after terminal connection (Regular expression)' + + Params answer label: + zh: '命令结果' + ja: 'コマンド結果' + en: 'Command result' + + Params answer help text: + zh: | + 根据结果匹配度决定是否执行下一条命令,输入框的内容和上方 “自定义命令” 内容按行一一对应(正则表达式) + ja: | + 結果の一致度に基づいて次のコマンドを実行するかどうかを決定します。 + 入力欄の内容は、上の「カスタムコマンド」の内容と行ごとに対応しています(せいきひょうげん) + en: | + Decide whether to execute the next command based on the result match. + The input content corresponds line by line with the content + of the `Custom command` above. (Regular expression) diff --git a/apps/accounts/automations/verify_account/host/posix/main.yml b/apps/accounts/automations/verify_account/host/posix/main.yml index 49e84e5ee..d02dd6f4f 100644 --- a/apps/accounts/automations/verify_account/host/posix/main.yml +++ b/apps/accounts/automations/verify_account/host/posix/main.yml @@ -8,6 +8,7 @@ ansible_user: "{{ account.username }}" ansible_password: "{{ account.secret }}" ansible_ssh_private_key_file: "{{ account.private_key_path }}" + ansible_timeout: 30 when: not account.become.ansible_become - name: Verify account connectivity(Switch) @@ -20,4 +21,5 @@ ansible_become_method: "{{ account.become.ansible_become_method }}" ansible_become_user: "{{ account.become.ansible_become_user }}" ansible_become_password: "{{ account.become.ansible_become_password }}" + ansible_timeout: 30 when: account.become.ansible_become diff --git a/apps/accounts/automations/verify_account/host/windows/main.yml b/apps/accounts/automations/verify_account/host/windows/main.yml index a7b49545c..d932b3f6b 100644 --- a/apps/accounts/automations/verify_account/host/windows/main.yml +++ b/apps/accounts/automations/verify_account/host/windows/main.yml @@ -9,3 +9,4 @@ vars: ansible_user: "{{ account.full_username }}" ansible_password: "{{ account.secret }}" + ansible_timeout: 30 diff --git a/apps/assets/automations/ping/host/posix/main.yml b/apps/assets/automations/ping/host/posix/main.yml index 8e5f375dd..8616f497b 100644 --- a/apps/assets/automations/ping/host/posix/main.yml +++ b/apps/assets/automations/ping/host/posix/main.yml @@ -1,5 +1,7 @@ - hosts: demo gather_facts: no + vars: + ansible_timeout: 30 tasks: - name: Posix ping ansible.builtin.ping: diff --git a/apps/assets/automations/ping/host/windows/main.yml b/apps/assets/automations/ping/host/windows/main.yml index 92d8164ad..59c1697b8 100644 --- a/apps/assets/automations/ping/host/windows/main.yml +++ b/apps/assets/automations/ping/host/windows/main.yml @@ -1,5 +1,7 @@ - hosts: windows gather_facts: no + vars: + ansible_timeout: 30 tasks: - name: Refresh connection ansible.builtin.meta: reset_connection diff --git a/apps/assets/serializers/asset/database.py b/apps/assets/serializers/asset/database.py index 84703b74f..8f653ce15 100644 --- a/apps/assets/serializers/asset/database.py +++ b/apps/assets/serializers/asset/database.py @@ -46,7 +46,7 @@ class DatabaseSerializer(AssetSerializer): elif self.context.get('request'): platform_id = self.context['request'].query_params.get('platform') - if not platform and platform_id: + if not platform and platform_id and platform_id.isdigit(): platform = Platform.objects.filter(id=platform_id).first() return platform diff --git a/apps/authentication/tasks.py b/apps/authentication/tasks.py index acd940978..155bf4a3a 100644 --- a/apps/authentication/tasks.py +++ b/apps/authentication/tasks.py @@ -1,12 +1,18 @@ # -*- coding: utf-8 -*- # +import datetime +import logging from celery import shared_task +from django.conf import settings from django.contrib.sessions.models import Session from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from authentication.models import ConnectionToken, TempToken +from common.const.crontab import CRONTAB_AT_AM_TWO from ops.celery.decorator import register_as_period_task +from orgs.utils import tmp_to_root_org @shared_task( @@ -18,3 +24,26 @@ from ops.celery.decorator import register_as_period_task @register_as_period_task(interval=3600 * 24) def clean_django_sessions(): Session.objects.filter(expire_date__lt=timezone.now()).delete() + + +@shared_task( + verbose_name=_('Clean expired temporary, connection tokens'), + description=_( + "When connecting to assets or generating temporary passwords, the system creates corresponding connection " + "tokens or temporary credential records. To maintain security and manage storage, the system automatically " + "deletes expired tokens every day at 2:00 AM based on the retention settings configured under System settings " + "> Security > User password > Token Retention Period" + ) +) +@register_as_period_task(crontab=CRONTAB_AT_AM_TWO) +def clean_expire_token(): + logging.info('Cleaning expired temporary and connection tokens...') + with tmp_to_root_org(): + now = timezone.now() + days = settings.SECURITY_EXPIRED_TOKEN_RECORD_KEEP_DAYS + expired_time = now - datetime.timedelta(days=days) + count = ConnectionToken.objects.filter(date_expired__lt=expired_time).delete() + logging.info('Deleted %d expired connection tokens.', count[0]) + count = TempToken.objects.filter(date_expired__lt=expired_time).delete() + logging.info('Deleted %d temporary tokens.', count[0]) + logging.info('Cleaned expired temporary and connection tokens.') diff --git a/apps/common/api/action.py b/apps/common/api/action.py index 627a1468a..9959adcaf 100644 --- a/apps/common/api/action.py +++ b/apps/common/api/action.py @@ -8,6 +8,7 @@ from rest_framework.request import Request from rest_framework.response import Response from common.const.http import POST, PUT +from orgs.utils import current_org __all__ = ['SuggestionMixin', 'RenderToJsonMixin'] @@ -23,7 +24,11 @@ class SuggestionMixin: @action(methods=['get'], detail=False, url_path='suggestions') def match(self, request, *args, **kwargs): - queryset = self.filter_queryset(self.get_queryset()) + queryset = self.get_queryset() + if not request.user.orgs.filter(id=current_org.id).exists(): + queryset = queryset.none() + + queryset = self.filter_queryset(queryset) queryset = queryset[:self.suggestion_limit] page = self.paginate_queryset(queryset) diff --git a/apps/i18n/core/zh/LC_MESSAGES/django.po b/apps/i18n/core/zh/LC_MESSAGES/django.po index 42cad9d76..ef53ee17a 100644 --- a/apps/i18n/core/zh/LC_MESSAGES/django.po +++ b/apps/i18n/core/zh/LC_MESSAGES/django.po @@ -136,6 +136,8 @@ msgstr ">>> 开始执行测试网关账号可连接性任务" #: users/templates/users/_msg_user_created.html:13 #: users/templates/users/user_password_verify.html:18 #: xpack/plugins/cloud/serializers/account_attrs.py:43 +#: xpack/plugins/cloud/serializers/account_attrs.py:105 +#: xpack/plugins/cloud/serializers/account_attrs.py:112 msgid "Password" msgstr "密码" @@ -192,7 +194,7 @@ msgstr "发现" msgid "Template" msgstr "模板" -#: accounts/const/account.py:32 ops/const.py:46 xpack/plugins/cloud/const.py:73 +#: accounts/const/account.py:32 ops/const.py:46 xpack/plugins/cloud/const.py:74 msgid "Skip" msgstr "跳过" @@ -204,7 +206,7 @@ msgstr "更新" #: accounts/const/account.py:34 accounts/const/automation.py:115 #: accounts/serializers/automations/change_secret.py:169 audits/const.py:66 #: audits/signal_handlers/activity_log.py:34 common/const/choices.py:66 -#: ops/const.py:76 terminal/const.py:81 xpack/plugins/cloud/const.py:52 +#: ops/const.py:76 terminal/const.py:81 xpack/plugins/cloud/const.py:53 msgid "Failed" msgstr "失败" @@ -391,7 +393,7 @@ msgstr "数据库" #: accounts/const/vault.py:8 assets/const/category.py:12 #: assets/models/asset/database.py:10 assets/models/asset/database.py:29 -#: xpack/plugins/cloud/const.py:38 +#: xpack/plugins/cloud/const.py:39 msgid "Database" msgstr "数据库" @@ -855,6 +857,8 @@ msgstr "重复密码" #: users/serializers/profile.py:190 #: users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:41 +#: xpack/plugins/cloud/serializers/account_attrs.py:103 +#: xpack/plugins/cloud/serializers/account_attrs.py:110 msgid "Username" msgstr "用户名" @@ -1156,7 +1160,7 @@ msgstr "账号存在策略" #: settings/models.py:41 tickets/models/ticket/apply_application.py:13 #: users/models/preference.py:12 xpack/plugins/cloud/models.py:41 #: xpack/plugins/cloud/models.py:326 -#: xpack/plugins/cloud/serializers/account.py:80 +#: xpack/plugins/cloud/serializers/account.py:81 msgid "Category" msgstr "类别" @@ -1310,12 +1314,12 @@ msgid "Password length" msgstr "密码长度" #: accounts/serializers/account/template.py:12 -#: settings/serializers/security.py:46 +#: settings/serializers/security.py:51 msgid "Lowercase" msgstr "小写字母" #: accounts/serializers/account/template.py:13 -#: settings/serializers/security.py:43 +#: settings/serializers/security.py:48 msgid "Uppercase" msgstr "大写字母" @@ -1905,8 +1909,8 @@ msgid "" "10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 (Domain name " "support)" msgstr "" -"* 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:" -"db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" +"* 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, " +"2001:db8:2de::e13, 2001:db8:1a:1110::/64 (支持网域)" #: acls/serializers/base.py:41 assets/serializers/asset/host.py:19 msgid "IP/Host" @@ -1934,8 +1938,8 @@ msgid "" "With * indicating a match all. Such as: 192.168.10.1, 192.168.1.0/24, " "10.1.1.1-10.1.1.20, 2001:db8:2de::e13, 2001:db8:1a:1110::/64 " msgstr "" -"* 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, 2001:" -"db8:2de::e13, 2001:db8:1a:1110::/64" +"* 表示匹配所有。例如: 192.168.10.1, 192.168.1.0/24, 10.1.1.1-10.1.1.20, " +"2001:db8:2de::e13, 2001:db8:1a:1110::/64" #: acls/serializers/rules/rules.py:33 #: authentication/templates/authentication/_msg_oauth_bind.html:12 @@ -2164,7 +2168,7 @@ msgstr "脚本" #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:76 #: settings/serializers/feature.py:81 settings/serializers/feature.py:94 #: settings/serializers/msg.py:30 terminal/models/component/endpoint.py:14 -#: terminal/serializers/applet.py:17 xpack/plugins/cloud/const.py:37 +#: terminal/serializers/applet.py:17 xpack/plugins/cloud/const.py:38 #: xpack/plugins/cloud/manager.py:101 #: xpack/plugins/cloud/serializers/account_attrs.py:87 msgid "Host" @@ -2184,7 +2188,8 @@ msgstr "云服务" msgid "Web" msgstr "Web" -#: assets/const/category.py:16 common/sdk/sms/endpoint.py:20 +#: assets/const/category.py:16 common/sdk/sms/custom_file.py:47 +#: common/sdk/sms/endpoint.py:20 msgid "Custom type" msgstr "自定义类型" @@ -2246,7 +2251,7 @@ msgid "ChatGPT" msgstr "ChatGPT" #: assets/const/host.py:13 rbac/tree.py:28 rbac/tree.py:66 -#: xpack/plugins/cloud/const.py:74 +#: xpack/plugins/cloud/const.py:75 msgid "Other" msgstr "其它" @@ -2291,7 +2296,7 @@ msgid "Any" msgstr "任意" #: assets/const/protocol.py:88 rbac/tree.py:62 -#: settings/serializers/security.py:268 +#: settings/serializers/security.py:273 msgid "Security" msgstr "安全" @@ -2890,7 +2895,7 @@ msgstr "端口超出范围 (0-65535)" msgid "Protocol is required: {}" msgstr "协议是必填的: {}" -#: assets/serializers/asset/common.py:353 +#: assets/serializers/asset/common.py:353 labels/api.py:107 msgid "Invalid data" msgstr "无效的数据" @@ -3329,7 +3334,7 @@ msgstr "同意" #: audits/const.py:42 ops/models/celery.py:85 #: terminal/models/session/sharing.py:128 tickets/const.py:25 -#: xpack/plugins/cloud/const.py:72 +#: xpack/plugins/cloud/const.py:73 msgid "Finished" msgstr "结束" @@ -3354,7 +3359,7 @@ msgstr "登录日志" #: audits/const.py:55 rbac/tree.py:64 terminal/models/applet/host.py:144 #: terminal/models/component/task.py:22 -#: xpack/plugins/cloud/serializers/account.py:77 +#: xpack/plugins/cloud/serializers/account.py:78 msgid "Task" msgstr "任务" @@ -4195,13 +4200,13 @@ msgstr "私有令牌" #: authentication/models/ssh_key.py:15 terminal/serializers/storage.py:146 #: users/models/user/__init__.py:94 -#: xpack/plugins/cloud/serializers/account_attrs.py:229 +#: xpack/plugins/cloud/serializers/account_attrs.py:237 msgid "Private key" msgstr "ssh私钥" #: authentication/models/ssh_key.py:18 settings/serializers/terminal.py:38 #: users/forms/profile.py:175 users/models/user/__init__.py:97 -#: xpack/plugins/cloud/serializers/account_attrs.py:226 +#: xpack/plugins/cloud/serializers/account_attrs.py:234 msgid "Public key" msgstr "SSH公钥" @@ -4321,16 +4326,30 @@ msgstr "SSH密钥不合法" msgid "Is valid" msgstr "是否有效" -#: authentication/tasks.py:13 +#: authentication/tasks.py:19 msgid "Clean expired session" msgstr "清除过期会话" -#: authentication/tasks.py:15 +#: authentication/tasks.py:21 msgid "" "Since user logins create sessions, the system will clean up expired sessions " "every 24 hours" msgstr "由于用户登录系统会产生会话,系统会每24小时清理已经过期的会话" +#: authentication/tasks.py:30 +msgid "Clean expired temporary, connection tokens" +msgstr "清理过期的临时密码和连接令牌" + +#: authentication/tasks.py:32 +msgid "" +"When connecting to assets or generating temporary passwords, the system " +"creates corresponding connection tokens or temporary credential records. To " +"maintain security and manage storage, the system automatically deletes " +"expired tokens every day at 2:00 AM based on the retention settings " +"configured under System settings > Security > User password > Token " +"Retention Period" +msgstr "系统在连接资产或创建临时密码时,会生成相应的连接 Token 或临时密码记录。为了保障系统安全并控制数据存储量,系统会根据“安全设置 > 密码安全 > Token 保留时长”中的配置,每天凌晨 2 点自动清理所有已过期的 Token 记录" + #: authentication/templates/authentication/_access_key_modal.html:6 msgid "API key list" msgstr "API Key列表" @@ -4836,7 +4855,7 @@ msgstr "无效的ID,应为列表" #: xpack/plugins/cloud/serializers/account_attrs.py:20 #: xpack/plugins/cloud/serializers/account_attrs.py:71 #: xpack/plugins/cloud/serializers/account_attrs.py:95 -#: xpack/plugins/cloud/serializers/account_attrs.py:166 +#: xpack/plugins/cloud/serializers/account_attrs.py:174 msgid "This field is required." msgstr "该字段是必填项。" @@ -5045,6 +5064,10 @@ msgstr "自定义短信文件无效" msgid "SMS sending failed[%s]: %s" msgstr "短信发送失败[%s]: %s" +#: common/sdk/sms/custom_file.py:47 common/serializers/common.py:98 +msgid "File" +msgstr "文件" + #: common/sdk/sms/endpoint.py:16 msgid "Alibaba cloud" msgstr "阿里云" @@ -5089,10 +5112,6 @@ msgstr "请在 {} 秒后发送" msgid "Children" msgstr "节点" -#: common/serializers/common.py:98 -msgid "File" -msgstr "文件" - #: common/serializers/fields.py:139 msgid "Invalid data type" msgstr "无效的数据" @@ -5112,7 +5131,7 @@ msgid "Invalid data type, should be list" msgstr "错误的数据类型,应该是列表" #: common/serializers/fields.py:237 -#: xpack/plugins/cloud/serializers/account.py:114 +#: xpack/plugins/cloud/serializers/account.py:115 #: xpack/plugins/cloud/serializers/task.py:122 msgid "Invalid choice: {}" msgstr "无效选项: {}" @@ -5215,6 +5234,10 @@ msgstr "你的账号已创建成功" msgid "JumpServer - An open-source PAM" msgstr "JumpServer 开源堡垒机" +#: jumpserver/context_processor.py:28 +msgid "FIT2CLOUD" +msgstr "" + #: jumpserver/views/celery_flower.py:22 msgid "

Flower service unavailable, check it

" msgstr "Flower 服务不可用,请检查" @@ -5246,7 +5269,7 @@ msgstr "" msgid "App Labels" msgstr "标签管理" -#: labels/models.py:15 settings/serializers/security.py:207 +#: labels/models.py:15 settings/serializers/security.py:212 msgid "Color" msgstr "颜色" @@ -7062,7 +7085,7 @@ msgid "SSO auth key TTL" msgstr "令牌有效期" #: settings/serializers/auth/sso.py:20 -#: xpack/plugins/cloud/serializers/account_attrs.py:216 +#: xpack/plugins/cloud/serializers/account_attrs.py:224 msgid "Unit: second" msgstr "单位: 秒" @@ -7219,7 +7242,7 @@ msgstr "租户 ID" #: settings/serializers/feature.py:110 terminal/serializers/storage.py:68 #: xpack/plugins/cloud/manager.py:119 xpack/plugins/cloud/manager.py:124 -#: xpack/plugins/cloud/models.py:292 +#: xpack/plugins/cloud/manager.py:161 xpack/plugins/cloud/models.py:292 msgid "Region" msgstr "地域" @@ -7435,68 +7458,77 @@ msgstr "" "期前5天内由系统 (每天)自动发送给用户" #: settings/serializers/security.py:28 +msgid "User expired tokens record keep days" +msgstr "用户过期 Token 保留天数" + +#: settings/serializers/security.py:29 +msgid "" +"Retention period (in days) for expired user tokens before automatic cleanup." +msgstr "系统在每日清理任务中永久删除用户过期 token 记录之前,保留这些记录的天数" + +#: settings/serializers/security.py:33 msgid "Recent password count" msgstr "不能设置近几次密码" -#: settings/serializers/security.py:30 +#: settings/serializers/security.py:35 msgid "" "Tip: When the user resets the password, it cannot be the previous n " "historical passwords of the user" msgstr "提示:用户重置密码时,不能为该用户前几次使用过的密码" -#: settings/serializers/security.py:36 +#: settings/serializers/security.py:41 msgid "Minimum length (User)" msgstr "密码最小长度" -#: settings/serializers/security.py:40 +#: settings/serializers/security.py:45 msgid "Minimum length (Admin)" msgstr "管理员密码最小长度" -#: settings/serializers/security.py:49 +#: settings/serializers/security.py:54 msgid "Digits" msgstr "必须包含数字" -#: settings/serializers/security.py:52 +#: settings/serializers/security.py:57 msgid "Special characters" msgstr "必须包含特殊字符" -#: settings/serializers/security.py:57 +#: settings/serializers/security.py:62 msgid "" "If the user has failed to log in for a limited number of times, no login is " "allowed during this time interval." msgstr "当用户登录失败次数达到限制后,那么在此间隔内禁止登录" -#: settings/serializers/security.py:65 settings/serializers/security.py:75 +#: settings/serializers/security.py:70 settings/serializers/security.py:80 msgid "Login failures count" msgstr "限制用户登录失败次数" -#: settings/serializers/security.py:69 settings/serializers/security.py:79 +#: settings/serializers/security.py:74 settings/serializers/security.py:84 msgid "Login failure period (minute)" msgstr "禁止用户登录间隔 (分)" -#: settings/serializers/security.py:83 +#: settings/serializers/security.py:88 msgid "Login IP whitelist" msgstr "IP 登录白名单" -#: settings/serializers/security.py:88 +#: settings/serializers/security.py:93 msgid "Login IP blacklist" msgstr "IP 登录黑名单" -#: settings/serializers/security.py:93 +#: settings/serializers/security.py:98 msgid "Only single device login" msgstr "仅一台设备登录" -#: settings/serializers/security.py:94 +#: settings/serializers/security.py:99 msgid "" "After the user logs in on the new device, other logged-in devices will " "automatically log out" msgstr "用户在新设备登录后,其他已登录的设备会自动退出" -#: settings/serializers/security.py:97 +#: settings/serializers/security.py:102 msgid "Only exist user login" msgstr "仅已存在用户登录" -#: settings/serializers/security.py:99 +#: settings/serializers/security.py:104 msgid "" "If enabled, non-existent users will not be allowed to log in; if disabled, " "users of other authentication methods except local authentication methods " @@ -7506,11 +7538,11 @@ msgstr "" "如果开启,不存在的用户将不被允许登录;如果关闭,除本地认证方式外,其他认证方" "式的用户都允许登录并自动创建用户 (如果用户不存在)" -#: settings/serializers/security.py:105 +#: settings/serializers/security.py:110 msgid "Only from source login" msgstr "仅从用户来源登录" -#: settings/serializers/security.py:107 +#: settings/serializers/security.py:112 msgid "" "If it is enabled, the user will only authenticate to the source when logging " "in; if it is disabled, the user will authenticate all the enabled " @@ -7520,78 +7552,78 @@ msgstr "" "如果开启,用户登录时仅会向来源端进行认证;如果关闭,用户登录时会按照一定的顺" "序对所有已开启的认证方式进行顺序认证,只要有一个认证成功就可以直接登录" -#: settings/serializers/security.py:118 +#: settings/serializers/security.py:123 #: users/templates/users/mfa_setting.html:160 msgid "Not enabled" msgstr "未启用" -#: settings/serializers/security.py:119 +#: settings/serializers/security.py:124 msgid "All users" msgstr "所有用户" -#: settings/serializers/security.py:120 +#: settings/serializers/security.py:125 msgid "Only admin users" msgstr "仅管理员" -#: settings/serializers/security.py:122 +#: settings/serializers/security.py:127 msgid "Global MFA" msgstr "全局启用 MFA 认证" -#: settings/serializers/security.py:126 +#: settings/serializers/security.py:131 msgid "Third-party login MFA" msgstr "第三方认证开启 MFA" -#: settings/serializers/security.py:127 +#: settings/serializers/security.py:132 msgid "The third-party login modes include OIDC, CAS, and SAML2" msgstr "第三方登录方式包括: OIDC、CAS、SAML2" -#: settings/serializers/security.py:131 +#: settings/serializers/security.py:136 msgid "MFA via Email" msgstr "邮件验证 MFA" -#: settings/serializers/security.py:132 +#: settings/serializers/security.py:137 msgid "Email as a method for multi-factor authentication" msgstr "将电子邮件作为多因子认证的一种方式" -#: settings/serializers/security.py:135 +#: settings/serializers/security.py:140 msgid "OTP issuer name" msgstr "OTP 扫描后的名称" -#: settings/serializers/security.py:139 +#: settings/serializers/security.py:144 msgid "OTP valid window" msgstr "OTP 延迟有效次数" -#: settings/serializers/security.py:143 +#: settings/serializers/security.py:148 msgid "MFA verify TTL" msgstr "MFA 校验有效期" -#: settings/serializers/security.py:145 +#: settings/serializers/security.py:150 msgid "" "Unit: second, The verification MFA takes effect only when you view the " "account password" msgstr "单位:秒,目前仅在查看账号密码校验 MFA 时生效" -#: settings/serializers/security.py:150 +#: settings/serializers/security.py:155 msgid "MFA in login page" msgstr "MFA 在登录页面输入" -#: settings/serializers/security.py:151 +#: settings/serializers/security.py:156 msgid "Eu security regulations(GDPR) require MFA to be on the login page" msgstr "欧盟数据安全法规(GDPR) 要求 MFA 在登录页面,来确保系统登录安全" -#: settings/serializers/security.py:155 +#: settings/serializers/security.py:160 msgid "Verify code TTL (second)" msgstr "验证码有效时间 (分)" -#: settings/serializers/security.py:156 +#: settings/serializers/security.py:161 msgid "Reset password and send SMS code expiration time" msgstr "重置密码的验证码及发送短信的验证码过期时间" -#: settings/serializers/security.py:160 +#: settings/serializers/security.py:165 msgid "Login dynamic code" msgstr "启用登录附加码" -#: settings/serializers/security.py:161 +#: settings/serializers/security.py:166 msgid "" "The password and additional code are sent to a third party authentication " "system for verification" @@ -7599,19 +7631,19 @@ msgstr "" "密码和附加码一并发送给第三方认证系统进行校验, 如:有的第三方认证系统,需要 密" "码+6位数字 完成认证" -#: settings/serializers/security.py:165 +#: settings/serializers/security.py:170 msgid "Login captcha" msgstr "启用登录验证码" -#: settings/serializers/security.py:166 +#: settings/serializers/security.py:171 msgid "Enable captcha to prevent robot authentication" msgstr "开启验证码,防止机器人登录" -#: settings/serializers/security.py:169 +#: settings/serializers/security.py:174 msgid "Suspicious Login Verification" msgstr "异地登录通知" -#: settings/serializers/security.py:171 +#: settings/serializers/security.py:176 msgid "" "The system determines whether the login IP address belongs to a common login " "city. If the account is logged in from a common login city, the system sends " @@ -7620,101 +7652,101 @@ msgstr "" "根据登录 IP 是否所属常用登录城市进行判断,若账号在非常用城市登录,会发送异地" "登录提醒" -#: settings/serializers/security.py:177 +#: settings/serializers/security.py:182 msgid "Auto Disable Threshold (day)" msgstr "不活跃用户自动禁用 (天)" -#: settings/serializers/security.py:178 +#: settings/serializers/security.py:183 msgid "" "Detect infrequent users daily and disable them if they exceed the " "predetermined time limit" msgstr "每天检测一次,超过预设时间的用户自动禁用" -#: settings/serializers/security.py:198 +#: settings/serializers/security.py:203 msgid "Watermark" msgstr "开启水印" -#: settings/serializers/security.py:201 +#: settings/serializers/security.py:206 msgid "Watermark session content" msgstr "会话水印自定义内容" -#: settings/serializers/security.py:204 +#: settings/serializers/security.py:209 msgid "Watermark console content" msgstr "管理页面水印自定义内容" -#: settings/serializers/security.py:210 +#: settings/serializers/security.py:215 msgid "Watermark font size" msgstr "字体字号" -#: settings/serializers/security.py:213 +#: settings/serializers/security.py:218 msgid "Watermark height" msgstr "单个水印高度" -#: settings/serializers/security.py:216 +#: settings/serializers/security.py:221 msgid "Watermark width" msgstr "单个水印宽度" -#: settings/serializers/security.py:219 +#: settings/serializers/security.py:224 msgid "Watermark rotate" msgstr "水印旋转角度" -#: settings/serializers/security.py:223 +#: settings/serializers/security.py:228 msgid "Max idle time (minute)" msgstr "连接最大空闲时间 (分)" -#: settings/serializers/security.py:224 +#: settings/serializers/security.py:229 msgid "If idle time more than it, disconnect connection." msgstr "提示:如果超过该配置没有操作,连接会被断开" -#: settings/serializers/security.py:227 +#: settings/serializers/security.py:232 msgid "Session expire at browser closed" msgstr "会话在浏览器关闭时过期" -#: settings/serializers/security.py:228 +#: settings/serializers/security.py:233 msgid "Whether to expire the session when the user closes their browser." msgstr "当用户关闭浏览器时是否使会话过期。" -#: settings/serializers/security.py:233 +#: settings/serializers/security.py:238 msgid "Allow users to view asset session information" msgstr "允许用户查看资产在线会话信息" -#: settings/serializers/security.py:235 +#: settings/serializers/security.py:240 msgid "" "When a user connects to an asset, the account selection popup displays the " "number of active sessions for the current asset (RDP protocol only)." msgstr "" "当用户连接资产时,账号选择弹窗中显示当前资产的在线会话数量(仅 rdp 协议)" -#: settings/serializers/security.py:241 +#: settings/serializers/security.py:246 msgid "Max online time (hour)" msgstr "会话连接最大时间 (时)" -#: settings/serializers/security.py:242 +#: settings/serializers/security.py:247 msgid "If session connection time more than it, disconnect connection." msgstr "提示:如果会话连接超过该配置,连接会被断开" -#: settings/serializers/security.py:245 +#: settings/serializers/security.py:250 msgid "Remember manual auth" msgstr "保存手动输入密码" -#: settings/serializers/security.py:248 +#: settings/serializers/security.py:253 #: terminal/templates/terminal/_msg_session_sharing.html:10 msgid "Session share" msgstr "会话分享" -#: settings/serializers/security.py:249 +#: settings/serializers/security.py:254 msgid "Enabled, Allows user active session to be shared with other users" msgstr "开启后允许用户分享已连接的资产会话给他人,协同工作" -#: settings/serializers/security.py:255 +#: settings/serializers/security.py:260 msgid "Insecure command alert" msgstr "危险命令告警" -#: settings/serializers/security.py:258 +#: settings/serializers/security.py:263 msgid "Email recipient" msgstr "邮件收件人" -#: settings/serializers/security.py:259 +#: settings/serializers/security.py:264 msgid "Multiple user using , split" msgstr "多个用户,使用 , 分割" @@ -7974,7 +8006,7 @@ msgid ", disabled {}" msgstr ", 禁用 {}" #: templates/_csv_import_export.html:13 templates/_csv_import_modal.html:5 -#: xpack/plugins/cloud/const.py:70 +#: xpack/plugins/cloud/const.py:71 msgid "Import" msgstr "导入" @@ -8202,7 +8234,7 @@ msgstr "会话不存在: {}" msgid "Session is finished or the protocol not supported" msgstr "会话已经完成或协议不支持" -#: terminal/api/session/session.py:345 +#: terminal/api/session/session.py:345 tickets/api/ticket.py:140 msgid "User does not have permission" msgstr "用户没有权限" @@ -8229,7 +8261,7 @@ msgstr "风险等级" #: terminal/connect_methods.py:29 msgid "SSH Client" -msgstr "启用 SSH Client" +msgstr "SSH Client" #: terminal/connect_methods.py:30 msgid "SSH Guide" @@ -8692,8 +8724,8 @@ msgstr "命令及录像存储" #: terminal/notifications.py:258 terminal/tasks.py:212 #: xpack/plugins/cloud/api.py:182 -#: xpack/plugins/cloud/serializers/account.py:136 -#: xpack/plugins/cloud/serializers/account.py:138 +#: xpack/plugins/cloud/serializers/account.py:137 +#: xpack/plugins/cloud/serializers/account.py:139 msgid "Test failure: Account invalid" msgstr "测试失败: 账号无效" @@ -8750,9 +8782,9 @@ msgid "" "days. Detail" msgstr "" -"如果不存在,RDS 将处于试用模式,试用期为 120 天。详情" +"如果不存在,RDS 将处于试用模式,试用期为 120 天。详情" #: terminal/serializers/applet_host.py:55 msgid "RDS License Server" @@ -8970,8 +9002,8 @@ msgid "" "If there are multiple hosts, use a comma (,) to separate them.
(For " "example: http://www.jumpserver.a.com:9100, http://www.jumpserver.b.com:9100)" msgstr "" -"如果有多个主机,请用逗号 (,) 分隔它们。
(例如:http://www.jumpserver.a." -"com:9100,http://www.jumpserver.b.com:9100)" +"如果有多个主机,请用逗号 (,) 分隔它们。
(例如:http://" +"www.jumpserver.a.com:9100,http://www.jumpserver.b.com:9100)" #: terminal/serializers/storage.py:199 msgid "Index by date" @@ -10467,67 +10499,71 @@ msgstr "OpenStack" msgid "ZStack" msgstr "ZStack" -#: xpack/plugins/cloud/const.py:30 +#: xpack/plugins/cloud/const.py:30 xpack/plugins/cloud/providers/smartx.py:9 +msgid "SmartX CloudTower" +msgstr "" + +#: xpack/plugins/cloud/const.py:31 msgid "Fusion Compute" msgstr "融合计算" -#: xpack/plugins/cloud/const.py:31 +#: xpack/plugins/cloud/const.py:32 msgid "SCP" msgstr "深信服SCP" -#: xpack/plugins/cloud/const.py:32 +#: xpack/plugins/cloud/const.py:33 msgid "Apsara Stack" msgstr "阿里云专有云" -#: xpack/plugins/cloud/const.py:42 +#: xpack/plugins/cloud/const.py:43 msgid "Private IP" msgstr "私有IP" -#: xpack/plugins/cloud/const.py:43 +#: xpack/plugins/cloud/const.py:44 msgid "Public IP" msgstr "公网IP" -#: xpack/plugins/cloud/const.py:47 xpack/plugins/cloud/models.py:372 +#: xpack/plugins/cloud/const.py:48 xpack/plugins/cloud/models.py:372 msgid "Instance name" msgstr "实例名称" -#: xpack/plugins/cloud/const.py:48 +#: xpack/plugins/cloud/const.py:49 msgid "Instance name and Partial IP" msgstr "实例名称和部分IP" -#: xpack/plugins/cloud/const.py:53 +#: xpack/plugins/cloud/const.py:54 msgid "Succeed" msgstr "成功" -#: xpack/plugins/cloud/const.py:57 xpack/plugins/cloud/manager.py:107 +#: xpack/plugins/cloud/const.py:58 xpack/plugins/cloud/manager.py:107 msgid "Unsync" msgstr "未同步" -#: xpack/plugins/cloud/const.py:58 xpack/plugins/cloud/manager.py:106 +#: xpack/plugins/cloud/const.py:59 xpack/plugins/cloud/manager.py:106 msgid "New Sync" msgstr "新同步" -#: xpack/plugins/cloud/const.py:59 xpack/plugins/cloud/manager.py:106 +#: xpack/plugins/cloud/const.py:60 xpack/plugins/cloud/manager.py:106 msgid "Synced" msgstr "已同步" -#: xpack/plugins/cloud/const.py:60 xpack/plugins/cloud/manager.py:108 +#: xpack/plugins/cloud/const.py:61 xpack/plugins/cloud/manager.py:108 msgid "Released" msgstr "已释放" -#: xpack/plugins/cloud/const.py:64 +#: xpack/plugins/cloud/const.py:65 msgid "And" msgstr "与" -#: xpack/plugins/cloud/const.py:65 +#: xpack/plugins/cloud/const.py:66 msgid "Or" msgstr "或" -#: xpack/plugins/cloud/const.py:69 +#: xpack/plugins/cloud/const.py:70 msgid "Sync region" msgstr "已同步组织" -#: xpack/plugins/cloud/const.py:71 +#: xpack/plugins/cloud/const.py:72 msgid "Imported" msgstr "导入" @@ -10569,6 +10605,10 @@ msgstr "同步地区" msgid "Get instances of region \"%s\" error, error: %s" msgstr "获取区域 \"%s\" 的实例错误,错误:%s" +#: xpack/plugins/cloud/manager.py:161 xpack/plugins/cloud/models.py:289 +msgid "Instance" +msgstr "实例" + #: xpack/plugins/cloud/manager.py:187 #, python-format msgid "Failed to synchronize the instance \"%s\"" @@ -10630,7 +10670,7 @@ msgid "Cloud center" msgstr "云管中心" #: xpack/plugins/cloud/models.py:43 -#: xpack/plugins/cloud/serializers/account.py:98 +#: xpack/plugins/cloud/serializers/account.py:99 msgid "Attrs" msgstr "属性" @@ -10647,7 +10687,7 @@ msgid "Test cloud account" msgstr "测试云账号" #: xpack/plugins/cloud/models.py:109 -#: xpack/plugins/cloud/serializers/account.py:76 +#: xpack/plugins/cloud/serializers/account.py:77 #: xpack/plugins/cloud/serializers/task.py:170 #: xpack/plugins/cloud/serializers/task.py:171 msgid "Regions" @@ -10711,10 +10751,6 @@ msgstr "同步任务" msgid "Sync instance task history" msgstr "同步实例任务历史" -#: xpack/plugins/cloud/models.py:289 -msgid "Instance" -msgstr "实例" - #: xpack/plugins/cloud/models.py:306 msgid "Sync instance detail" msgstr "同步实例详情" @@ -10999,11 +11035,11 @@ msgstr "华东-宿迁" msgid "Port \"%(port)s\" of instance IP \"%(ip)s\" is not reachable" msgstr "实例 IP %(ip)s 的端口%(port)s 无法访问" -#: xpack/plugins/cloud/serializers/account.py:99 +#: xpack/plugins/cloud/serializers/account.py:100 msgid "Validity display" msgstr "有效性显示" -#: xpack/plugins/cloud/serializers/account.py:100 +#: xpack/plugins/cloud/serializers/account.py:101 msgid "Provider display" msgstr "服务商显示" @@ -11019,11 +11055,15 @@ msgstr "订阅 ID" msgid "Auto node classification" msgstr "自动节点分类" +#: xpack/plugins/cloud/serializers/account_attrs.py:108 +msgid "domain_name" +msgstr "域名称" + #: xpack/plugins/cloud/serializers/account_attrs.py:114 #: xpack/plugins/cloud/serializers/account_attrs.py:118 -#: xpack/plugins/cloud/serializers/account_attrs.py:142 -#: xpack/plugins/cloud/serializers/account_attrs.py:172 -#: xpack/plugins/cloud/serializers/account_attrs.py:222 +#: xpack/plugins/cloud/serializers/account_attrs.py:150 +#: xpack/plugins/cloud/serializers/account_attrs.py:180 +#: xpack/plugins/cloud/serializers/account_attrs.py:230 msgid "API Endpoint" msgstr "API 端点" @@ -11039,31 +11079,31 @@ msgstr "如: http://openstack.example.com:5000/v3" msgid "User domain" msgstr "用户域" -#: xpack/plugins/cloud/serializers/account_attrs.py:143 +#: xpack/plugins/cloud/serializers/account_attrs.py:151 msgid "Cert File" msgstr "证书文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:144 +#: xpack/plugins/cloud/serializers/account_attrs.py:152 msgid "Key File" msgstr "密钥文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:160 +#: xpack/plugins/cloud/serializers/account_attrs.py:168 msgid "Service account key" msgstr "服务账号密钥" -#: xpack/plugins/cloud/serializers/account_attrs.py:161 +#: xpack/plugins/cloud/serializers/account_attrs.py:169 msgid "The file is in JSON format" msgstr "JSON 格式的文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:179 +#: xpack/plugins/cloud/serializers/account_attrs.py:187 msgid "IP address invalid `{}`, {}" msgstr "IP 地址无效: `{}`, {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:195 +#: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例: 192.168.1.0/24,10.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:198 +#: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -11072,23 +11112,23 @@ msgstr "" "端口用来检测 IP 地址的有效性,在同步任务执行时,只会同步有效的 IP 地址。
" "如果端口为 0,则表示所有 IP 地址均有效。" -#: xpack/plugins/cloud/serializers/account_attrs.py:206 +#: xpack/plugins/cloud/serializers/account_attrs.py:214 msgid "Hostname prefix" msgstr "主机名前缀" -#: xpack/plugins/cloud/serializers/account_attrs.py:209 +#: xpack/plugins/cloud/serializers/account_attrs.py:217 msgid "IP segment" msgstr "IP 网段" -#: xpack/plugins/cloud/serializers/account_attrs.py:213 +#: xpack/plugins/cloud/serializers/account_attrs.py:221 msgid "Test port" msgstr "测试端口" -#: xpack/plugins/cloud/serializers/account_attrs.py:216 +#: xpack/plugins/cloud/serializers/account_attrs.py:224 msgid "Test timeout" msgstr "测试超时时间" -#: xpack/plugins/cloud/serializers/account_attrs.py:232 +#: xpack/plugins/cloud/serializers/account_attrs.py:240 msgid "Project" msgstr "project" @@ -11174,8 +11214,3 @@ msgstr "许可证导入成功" #: xpack/plugins/license/api.py:53 msgid "Invalid license" msgstr "许可证无效" - -#, fuzzy -#~| msgid "Domain name" -#~ msgid "domain_name" -#~ msgstr "域名称" diff --git a/apps/i18n/luna/en.json b/apps/i18n/luna/en.json index f9ae8c744..ad59cd256 100644 --- a/apps/i18n/luna/en.json +++ b/apps/i18n/luna/en.json @@ -237,5 +237,8 @@ "success": "Success", "system user": "System user", "user": "User", - "VerificationFailed": "Verification failed" + "VerificationFailed": "Verification failed", + "BatchCommands": "Batch commands", + "SendCommandPlaceholder": "Input command here..., Enter for new line, Ctrl+Enter to send", + "SelectCommand": "Please select the command to execute." } \ No newline at end of file diff --git a/apps/i18n/luna/zh.json b/apps/i18n/luna/zh.json index 6a6e12b57..00314fdb3 100644 --- a/apps/i18n/luna/zh.json +++ b/apps/i18n/luna/zh.json @@ -168,7 +168,7 @@ "Search": "搜索", "Select account": "选择账号", "Send command": "发送命令", - "Send text to all ssh terminals": "发送文本到所有ssh终端", + "Send text to all ssh terminals": "发送给所有会话", "Session": "会话", "SessionIsBeingMonitored": "会话正在被监控", "Set reusable": "开启复用", @@ -234,5 +234,9 @@ "success": "成功", "system user": "系统用户", "user": "用户", - "VerificationFailed": "校验失败" + "VerificationFailed": "校验失败", + "Send": "发送", + "BatchCommands": "批量命令", + "SendCommandPlaceholder": "输入命令,回车换行,Ctrl+Enter 执行命令", + "SelectCommand": "请选择要执行的命令" } \ No newline at end of file diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index d4dfd1127..e9c67e4f4 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -577,6 +577,7 @@ class Config(dict): 'SECURITY_MAX_IDLE_TIME': 30, 'SECURITY_MAX_SESSION_TIME': 24, 'SECURITY_PASSWORD_EXPIRATION_TIME': 9999, + 'SECURITY_EXPIRED_TOKEN_RECORD_KEEP_DAYS': 180, 'SECURITY_PASSWORD_MIN_LENGTH': 6, 'SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH': 6, 'SECURITY_PASSWORD_UPPER_CASE': False, diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 7c656d019..33c49913d 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -40,6 +40,7 @@ SECURITY_MAX_SESSION_TIME = CONFIG.SECURITY_MAX_SESSION_TIME # Unit: hour SECURITY_COMMAND_EXECUTION = CONFIG.SECURITY_COMMAND_EXECUTION SECURITY_COMMAND_BLACKLIST = CONFIG.SECURITY_COMMAND_BLACKLIST SECURITY_PASSWORD_EXPIRATION_TIME = CONFIG.SECURITY_PASSWORD_EXPIRATION_TIME # Unit: day +SECURITY_EXPIRED_TOKEN_RECORD_KEEP_DAYS = CONFIG.SECURITY_EXPIRED_TOKEN_RECORD_KEEP_DAYS SECURITY_PASSWORD_MIN_LENGTH = CONFIG.SECURITY_PASSWORD_MIN_LENGTH # Unit: bit SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH = CONFIG.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH # Unit: bit OLD_PASSWORD_HISTORY_LIMIT_COUNT = CONFIG.OLD_PASSWORD_HISTORY_LIMIT_COUNT diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index b6e2cc5de..ed44df165 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -91,7 +91,7 @@ cache_kwargs = { } # docs 路由 urlpatterns += [ - path('api/swagger.', views.get_swagger_view(False).without_ui(**cache_kwargs), name='schema-json'), + path('api/swagger.', views.get_swagger_view().without_ui(**cache_kwargs), name='schema-json'), re_path('api/docs/?', views.get_swagger_view().with_ui('swagger', **cache_kwargs), name="docs"), re_path('api/redoc/?', views.get_swagger_view().with_ui('redoc', **cache_kwargs), name='redoc'), ] diff --git a/apps/jumpserver/views/swagger.py b/apps/jumpserver/views/swagger.py index 91d270b43..04d2fc499 100644 --- a/apps/jumpserver/views/swagger.py +++ b/apps/jumpserver/views/swagger.py @@ -1,3 +1,5 @@ +import os + from drf_yasg import openapi from drf_yasg.generators import OpenAPISchemaGenerator from drf_yasg.inspectors import SwaggerAutoSchema @@ -149,13 +151,14 @@ api_info = openapi.Info( ) -def get_swagger_view(with_auth=True): +def get_swagger_view(): from ..urls import api_v1 from django.urls import path, include patterns = [ path('api/v1/', include(api_v1)) ] + with_auth = os.environ.get('DOC_AUTH', '1') == '1' if with_auth: permission_classes = (permissions.IsAuthenticated,) public = False diff --git a/apps/settings/api/security.py b/apps/settings/api/security.py index df10b627b..14c4ed4b1 100644 --- a/apps/settings/api/security.py +++ b/apps/settings/api/security.py @@ -4,8 +4,9 @@ from django.conf import settings from django.core.cache import cache from rest_framework.generics import ListAPIView, CreateAPIView from rest_framework.views import Response -from rest_framework.viewsets import ModelViewSet +from rest_framework_bulk.generics import BulkModelViewSet +from common.drf.filters import IDSpmFilterBackend from users.utils import LoginIpBlockUtil from ..models import LeakPasswords from ..serializers import SecurityBlockIPSerializer, LeakPasswordPSerializer @@ -60,7 +61,7 @@ class UnlockIPSecurityAPI(CreateAPIView): return Response(status=200) -class LeakPasswordViewSet(ModelViewSet): +class LeakPasswordViewSet(BulkModelViewSet): serializer_class = LeakPasswordPSerializer model = LeakPasswords rbac_perms = { @@ -71,3 +72,11 @@ class LeakPasswordViewSet(ModelViewSet): def get_queryset(self): return LeakPasswords.objects.using('sqlite').all() + + def allow_bulk_destroy(self, qs, filtered): + return True + + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + queryset = IDSpmFilterBackend().filter_queryset(self.request, queryset, self) + return queryset diff --git a/apps/settings/serializers/public.py b/apps/settings/serializers/public.py index 6e72e59e2..da047edfd 100644 --- a/apps/settings/serializers/public.py +++ b/apps/settings/serializers/public.py @@ -27,6 +27,7 @@ class PrivateSettingSerializer(PublicSettingSerializer): SECURITY_COMMAND_EXECUTION = serializers.BooleanField() SECURITY_COMMAND_BLACKLIST = serializers.ListField() SECURITY_PASSWORD_EXPIRATION_TIME = serializers.IntegerField() + SECURITY_EXPIRED_TOKEN_RECORD_KEEP_DAYS = serializers.IntegerField() SECURITY_LUNA_REMEMBER_AUTH = serializers.BooleanField() SECURITY_WATERMARK_ENABLED = serializers.BooleanField() SECURITY_WATERMARK_SESSION_CONTENT = serializers.CharField() diff --git a/apps/settings/serializers/security.py b/apps/settings/serializers/security.py index 0b83f0f99..6e6e1bf38 100644 --- a/apps/settings/serializers/security.py +++ b/apps/settings/serializers/security.py @@ -23,6 +23,11 @@ class SecurityPasswordRuleSerializer(serializers.Serializer): 'automatic sent to the user by system within 5 days (daily) before the password expires' ) ) + SECURITY_EXPIRED_TOKEN_RECORD_KEEP_DAYS = serializers.IntegerField( + min_value=1, max_value=99999, required=True, + label=_('User expired tokens record keep days'), + help_text=_("Retention period (in days) for expired user tokens before automatic cleanup.") + ) OLD_PASSWORD_HISTORY_LIMIT_COUNT = serializers.IntegerField( min_value=0, max_value=99999, required=True, label=_('Recent password count'), diff --git a/apps/terminal/applets/chrome/ChangeLog b/apps/terminal/applets/chrome/ChangeLog index 6b0c079b9..06b810439 100644 --- a/apps/terminal/applets/chrome/ChangeLog +++ b/apps/terminal/applets/chrome/ChangeLog @@ -1,3 +1,7 @@ +#2025-05-30 Version 1.2 +## 功能更新 + - 新增用户配置的语言支持,默认使用系统当前语言 + #2024-10-24 Version 1.1 ## 功能优化 - 优化快速点击造成页面卡住的问题 diff --git a/apps/terminal/applets/chrome/app.py b/apps/terminal/applets/chrome/app.py index c78f31953..6983f95dc 100644 --- a/apps/terminal/applets/chrome/app.py +++ b/apps/terminal/applets/chrome/app.py @@ -13,7 +13,7 @@ from code_dialog import CodeDialog, wrapper_progress_bar from common import (Asset, User, Account, Platform, Step) from common import (BaseApplication) from common import (notify_err_message, block_input, unblock_input) - +from common import get_system_language class Command(Enum): TYPE = 'type' @@ -251,6 +251,10 @@ class AppletApplication(BaseApplication): # 加载 extensions extension_paths = load_extensions() self._chrome_options.add_argument('--load-extension={}'.format(','.join(extension_paths))) + # 设置语言 + lang = self.connect_option.lang if self.connect_option.lang else get_system_language() + self._chrome_options.add_experimental_option('prefs', {'intl.accept_languages': lang}) + self._chrome_options.add_argument('--lang={}'.format(lang)) @wrapper_progress_bar def run(self): diff --git a/apps/terminal/applets/chrome/common.py b/apps/terminal/applets/chrome/common.py index 8d2ab3df2..a6a7af7d6 100644 --- a/apps/terminal/applets/chrome/common.py +++ b/apps/terminal/applets/chrome/common.py @@ -80,6 +80,22 @@ def wait_pid(pid): print("pid {} is not alive".format(pid)) break +def get_system_language(): + """ + 获取系统默认语言 + :return: 系统默认语言代码 + """ + try: + import ctypes + import locale + # 获取系统默认的语言ID + lang_id = ctypes.windll.kernel32.GetUserDefaultUILanguage() + # 转换为语言代码 + language = locale.windows_locale[lang_id] + return language + except Exception as e: + print(f"获取系统语言失败: {e}") + return 'en_US' class DictObj(dict): def __init__(self, *args, **kwargs): @@ -186,6 +202,13 @@ class Platform(DictObj): return item.setting return None +class ConnectOption(DictObj): + lang: str + charset: str + terminal_theme_name: str + disableautohash: bool + backspaceAsCtrlH: bool + class Manifest(DictObj): name: str @@ -234,6 +257,7 @@ class BaseApplication(abc.ABC): self.asset = Asset(kwargs.get('asset', {})) self.account = Account(kwargs.get('account', {})) self.platform = Platform(kwargs.get('platform', {})) + self.connect_option = ConnectOption(kwargs.get('connect_options', {})) @abc.abstractmethod def run(self): diff --git a/apps/terminal/applets/chrome/manifest.yml b/apps/terminal/applets/chrome/manifest.yml index 15bfdbf12..fca5a61cb 100644 --- a/apps/terminal/applets/chrome/manifest.yml +++ b/apps/terminal/applets/chrome/manifest.yml @@ -1,6 +1,6 @@ name: chrome display_name: "{{ 'Chrome Browser' | trans }}" -version: 1.1 +version: 1.2 comment: "{{ 'Chrome Browser Open URL Page Address' | trans }}" author: JumpServer Team exec_type: python diff --git a/apps/terminal/applets/dbeaver/README_EN.md b/apps/terminal/applets/dbeaver/README_EN.md index cc4165853..7ff4b39b8 100644 --- a/apps/terminal/applets/dbeaver/README_EN.md +++ b/apps/terminal/applets/dbeaver/README_EN.md @@ -2,3 +2,4 @@ - When connecting to a database application, it is necessary to download the driver. You can either install it offline in advance or install the corresponding driver as prompted when connecting. +- Due to the implementation mechanism of autofill, the database password used for connection does not support the | character. diff --git a/apps/terminal/applets/dbeaver/README_JA.md b/apps/terminal/applets/dbeaver/README_JA.md index 5f2cd55ce..46f6150be 100644 --- a/apps/terminal/applets/dbeaver/README_JA.md +++ b/apps/terminal/applets/dbeaver/README_JA.md @@ -1,3 +1,5 @@ ## DBeaver - データベースに接続する際には、ドライバをダウンロードする必要があります。事前にオフラインでインストールするか、接続時に表示される指示に従って該当するドライバをインストールしてください。 + +- 代入力の実装方式により、接続するデータベースのパスワードで | 記号は使用できません。 \ No newline at end of file diff --git a/apps/terminal/applets/dbeaver/README_ZH.md b/apps/terminal/applets/dbeaver/README_ZH.md index f18d62381..600aa35c7 100644 --- a/apps/terminal/applets/dbeaver/README_ZH.md +++ b/apps/terminal/applets/dbeaver/README_ZH.md @@ -2,3 +2,4 @@ - 连接数据库应用时,需要下载驱动,可提前离线安装或者连接时按提示安装相应驱动 +- 因代填的实现机制,连接的数据库密码不支持使用 `|` 字符 \ No newline at end of file diff --git a/apps/terminal/automations/deploy_applet_host/playbook.yml b/apps/terminal/automations/deploy_applet_host/playbook.yml index 53cf87cd6..20d1a6b2e 100644 --- a/apps/terminal/automations/deploy_applet_host/playbook.yml +++ b/apps/terminal/automations/deploy_applet_host/playbook.yml @@ -18,7 +18,7 @@ PYTHON_VERSION: 3.11.11 CHROME_VERSION: 118.0.5993.118 CHROME_DRIVER_VERSION: 118.0.5993.70 - TINKER_VERSION: v0.2.1 + TINKER_VERSION: v0.2.2 tasks: - block: