refactor: 重构危险命令告警类型: Warning (#10970)

* refactor: 重构危险命令告警类型: Warning

* Update _msg_command_warning.html

* Update _msg_command_warning.html

* Update command.py

* Update django.po

* perf: 优化 command acl warning 的代码逻辑

* perf: 优化 command acl warning 的代码逻辑

* perf: 优化 CommandWarningMessage 逻辑

---------

Co-authored-by: fangfang.dong <fangfang.dong@fit2cloud.com>
Co-authored-by: Bai <baijiangjie@gmail.com>
pull/10989/head
fit2bot 2023-07-17 20:52:54 +08:00 committed by GitHub
parent a2c6e5f3fb
commit 0771b804d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 390 additions and 228 deletions

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-13 15:56+0800\n"
"POT-Creation-Date: 2023-07-17 17:12+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -95,7 +95,7 @@ msgstr "更新"
#: accounts/const/account.py:29
#: accounts/serializers/automations/change_secret.py:156 audits/const.py:54
#: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19
#: ops/const.py:58 terminal/const.py:72 xpack/plugins/cloud/const.py:43
#: ops/const.py:58 terminal/const.py:77 xpack/plugins/cloud/const.py:41
msgid "Failed"
msgstr "失敗しました"
@ -200,8 +200,8 @@ msgstr "作成のみ"
#: audits/models.py:53 authentication/models/connection_token.py:36
#: perms/models/asset_permission.py:64 perms/serializers/permission.py:34
#: terminal/backends/command/models.py:18 terminal/models/session/session.py:31
#: terminal/notifications.py:134 terminal/serializers/command.py:18
#: terminal/templates/terminal/_msg_command_warning.html:4
#: terminal/notifications.py:147 terminal/serializers/command.py:18
#: terminal/templates/terminal/_msg_command_warning.html:39
#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:212
msgid "Asset"
msgstr "資産"
@ -235,6 +235,7 @@ msgstr "ソース ID"
#: assets/serializers/gateway.py:28 audits/models.py:54 ops/models/base.py:18
#: perms/models/asset_permission.py:70 perms/serializers/permission.py:39
#: terminal/backends/command/models.py:19 terminal/models/session/session.py:33
#: terminal/templates/terminal/_msg_command_warning.html:45
#: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:85
msgid "Account"
msgstr "アカウント"
@ -655,16 +656,17 @@ msgstr "ID"
#: perms/serializers/permission.py:30 rbac/builtin.py:123
#: rbac/models/rolebinding.py:49 terminal/backends/command/models.py:17
#: terminal/models/session/session.py:29 terminal/models/session/sharing.py:32
#: terminal/notifications.py:135 terminal/notifications.py:183
#: terminal/serializers/command.py:17 tickets/models/comment.py:21
#: users/const.py:14 users/models/user.py:947 users/models/user.py:978
#: users/serializers/group.py:18
#: terminal/notifications.py:148 terminal/notifications.py:196
#: terminal/serializers/command.py:17
#: terminal/templates/terminal/_msg_command_warning.html:33
#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:947
#: users/models/user.py:978 users/serializers/group.py:18
msgid "User"
msgstr "ユーザー"
#: accounts/serializers/account/account.py:428
#: authentication/templates/authentication/_access_key_modal.html:33
#: terminal/notifications.py:137 terminal/notifications.py:185
#: terminal/notifications.py:150 terminal/notifications.py:198
msgid "Date"
msgstr "日付"
@ -743,7 +745,7 @@ msgstr "自動タスク実行履歴"
#: accounts/serializers/automations/change_secret.py:155 audits/const.py:53
#: audits/models.py:59 audits/signal_handlers/activity_log.py:33
#: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:40
#: terminal/const.py:71 terminal/models/session/sharing.py:107
#: terminal/const.py:76 terminal/models/session/sharing.py:107
#: tickets/views/approve.py:114
msgid "Success"
msgstr "成功"
@ -843,11 +845,11 @@ msgid "Accounts"
msgstr "アカウント"
#: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60
#: ops/serializers/job.py:55 terminal/const.py:79
#: ops/serializers/job.py:55 terminal/const.py:84
#: terminal/models/session/session.py:42 terminal/serializers/command.py:19
#: terminal/templates/terminal/_msg_command_alert.html:12
#: terminal/templates/terminal/_msg_command_execute_alert.html:10
#: terminal/templates/terminal/_msg_command_warning.html:16
#: terminal/templates/terminal/_msg_command_warning.html:51
msgid "Command"
msgstr "コマンド"
@ -879,7 +881,7 @@ msgid "The generated regular expression is incorrect: {}"
msgstr "生成された正規表現が正しくありません: {}"
#: acls/models/command_acl.py:100
#: terminal/templates/terminal/_msg_command_warning.html:10
#: terminal/templates/terminal/_msg_command_warning.html:57
msgid "Command acl"
msgstr "コマンドフィルタリング"
@ -976,7 +978,7 @@ msgid "Applications"
msgstr "アプリケーション"
#: applications/models.py:16 xpack/plugins/cloud/models.py:33
#: xpack/plugins/cloud/serializers/account.py:63
#: xpack/plugins/cloud/serializers/account.py:62
msgid "Attrs"
msgstr "ツールバーの"
@ -1412,7 +1414,7 @@ msgstr "証明書チェックを無視"
#: assets/models/asset/gpt.py:8
msgid "Proxy"
msgstr ""
msgstr "プロキシー"
#: assets/models/automations/base.py:22 ops/models/job.py:187
#: settings/serializers/auth/sms.py:99
@ -1990,6 +1992,7 @@ msgid "Rename dir"
msgstr "マップディレクトリ"
#: audits/const.py:23 rbac/tree.py:229
#: terminal/templates/terminal/_msg_command_warning.html:71
msgid "View"
msgstr "表示"
@ -2073,7 +2076,7 @@ msgstr "書類"
#: terminal/models/session/replay.py:9 terminal/models/session/sharing.py:18
#: terminal/models/session/sharing.py:81
#: terminal/templates/terminal/_msg_command_alert.html:10
#: terminal/templates/terminal/_msg_command_warning.html:7
#: terminal/templates/terminal/_msg_command_warning.html:69
#: tickets/models/ticket/command_confirm.py:15
msgid "Session"
msgstr "セッション"
@ -2092,7 +2095,7 @@ msgid "Resource"
msgstr "リソース"
#: audits/models.py:96 audits/models.py:142 audits/models.py:168
#: terminal/serializers/command.py:61
#: terminal/serializers/command.py:76
msgid "Datetime"
msgstr "時間"
@ -3022,7 +3025,7 @@ msgid "Copy success"
msgstr "コピー成功"
#: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24
#: xpack/plugins/cloud/const.py:29
#: xpack/plugins/cloud/const.py:27
msgid "LAN"
msgstr "ローカルエリアネットワーク"
@ -3177,7 +3180,7 @@ msgstr "タイミングトリガー"
msgid "Ready"
msgstr "の準備を"
#: common/const/choices.py:16 terminal/const.py:70 tickets/const.py:29
#: common/const/choices.py:16 terminal/const.py:75 tickets/const.py:29
#: tickets/const.py:39
msgid "Pending"
msgstr "未定"
@ -3916,6 +3919,7 @@ msgstr "アプリ組織"
#: orgs/mixins/models.py:57 orgs/mixins/serializers.py:25 orgs/models.py:89
#: rbac/const.py:7 rbac/models/rolebinding.py:56
#: rbac/serializers/rolebinding.py:40 settings/serializers/auth/ldap.py:63
#: terminal/templates/terminal/_msg_command_warning.html:75
#: tickets/models/ticket/general.py:302 tickets/serializers/ticket/ticket.py:60
msgid "Organization"
msgstr "組織"
@ -5691,7 +5695,7 @@ msgstr "テスト失敗: {}"
msgid "Test successful"
msgstr "テスト成功"
#: terminal/api/component/storage.py:124 terminal/notifications.py:218
#: terminal/api/component/storage.py:124 terminal/notifications.py:231
#: terminal/tasks.py:144
msgid "Test failure: Account invalid"
msgstr "テスト失敗: アカウントが無効"
@ -5724,7 +5728,7 @@ msgstr "ターミナル管理"
msgid "Input"
msgstr "入力"
#: terminal/backends/command/models.py:21 terminal/serializers/command.py:59
#: terminal/backends/command/models.py:21 terminal/serializers/command.py:74
msgid "Output"
msgstr "出力"
@ -5748,40 +5752,40 @@ msgstr "確認して同意する"
msgid "Review & Cancel"
msgstr "確認してキャンセル"
#: terminal/const.py:39
#: terminal/const.py:44
msgid "Critical"
msgstr "クリティカル"
#: terminal/const.py:40
#: terminal/const.py:45
msgid "High"
msgstr "高い"
#: terminal/const.py:41 terminal/const.py:77
#: terminal/const.py:46 terminal/const.py:82
#: users/templates/users/reset_password.html:50
msgid "Normal"
msgstr "正常"
#: terminal/const.py:42
#: terminal/const.py:47
msgid "Offline"
msgstr "オフライン"
#: terminal/const.py:73
#: terminal/const.py:78
msgid "Mismatch"
msgstr "一致しない"
#: terminal/const.py:78
#: terminal/const.py:83
msgid "Tunnel"
msgstr ""
#: terminal/const.py:80
#: terminal/const.py:85
msgid "SFTP"
msgstr "SFTP"
#: terminal/const.py:84
#: terminal/const.py:89
msgid "Read Only"
msgstr "読み取り専用"
#: terminal/const.py:85
#: terminal/const.py:90
msgid "Writable"
msgstr "書き込み可能"
@ -5956,7 +5960,7 @@ msgstr "再生ストレージ"
msgid "type"
msgstr "タイプ"
#: terminal/models/component/terminal.py:89 terminal/serializers/command.py:62
#: terminal/models/component/terminal.py:89 terminal/serializers/command.py:77
msgid "Remote Address"
msgstr "リモートアドレス"
@ -6073,35 +6077,36 @@ msgstr "検証コードが無効"
msgid "You have already joined this session"
msgstr "すでにこのセッションに参加しています"
#: terminal/notifications.py:21
#: terminal/notifications.py:22
msgid "Sessions"
msgstr "セッション"
#: terminal/notifications.py:68
#: terminal/notifications.py:69
#: terminal/templates/terminal/_msg_command_warning.html:5
msgid "Danger command warning"
msgstr "危険コマンドアラート"
#: terminal/notifications.py:109
#: terminal/notifications.py:122
msgid "Danger command alert"
msgstr "危険コマンドアラート"
#: terminal/notifications.py:136 terminal/notifications.py:184
#: terminal/notifications.py:149 terminal/notifications.py:197
msgid "Level"
msgstr "レベル"
#: terminal/notifications.py:154
#: terminal/notifications.py:167
msgid "Batch danger command alert"
msgstr "一括危険コマンド警告"
#: terminal/notifications.py:202
#: terminal/notifications.py:215
msgid "Command and replay storage"
msgstr "コマンド及び録画記憶"
#: terminal/notifications.py:203
#: terminal/notifications.py:216
msgid "Connectivity alarm"
msgstr "接続性アラーム"
#: terminal/notifications.py:228
#: terminal/notifications.py:241
#: terminal/templates/terminal/_msg_check_command_replay_storage_connectivity.html:4
msgid "Invalid storage"
msgstr "無効なストレージ"
@ -6185,11 +6190,23 @@ msgstr "コマンドフィルター"
msgid "Command Group"
msgstr "コマンドグループ"
#: terminal/serializers/command.py:58
#: terminal/serializers/command.py:56
msgid "Invalid command filter ACL id"
msgstr "無効なコマンドフィルターID"
#: terminal/serializers/command.py:60
msgid "Invalid command group id"
msgstr "無効なコマンドグループID"
#: terminal/serializers/command.py:64
msgid "Invalid session id"
msgstr "無効なセッションID"
#: terminal/serializers/command.py:73
msgid "Account "
msgstr "アカウント"
#: terminal/serializers/command.py:60
#: terminal/serializers/command.py:75
msgid "Timestamp"
msgstr "タイムスタンプ"
@ -6363,14 +6380,18 @@ msgid "Check command replay storage connectivity"
msgstr "チェックコマンドと録画ストレージの接続性"
#: terminal/templates/terminal/_msg_command_alert.html:10
#: terminal/templates/terminal/_msg_command_warning.html:5
#: terminal/templates/terminal/_msg_command_warning.html:8
#: terminal/templates/terminal/_msg_command_warning.html:11
#: terminal/templates/terminal/_msg_command_warning.html:14
msgid "view"
msgstr "表示"
#: terminal/templates/terminal/_msg_command_warning.html:13
#: terminal/templates/terminal/_msg_command_warning.html:21
msgid "Item"
msgstr "アイテム"
#: terminal/templates/terminal/_msg_command_warning.html:25
msgid "Url"
msgstr "リンク"
#: terminal/templates/terminal/_msg_command_warning.html:63
msgid "Command acl group"
msgstr "コマンドフィルタリンググループ"
@ -6844,7 +6865,6 @@ msgid "Not a valid ssh public key"
msgstr "有効なssh公開鍵ではありません"
#: users/forms/profile.py:173 users/models/user.py:786
#: xpack/plugins/cloud/serializers/account_attrs.py:206
msgid "Public key"
msgstr "公開キー"
@ -6873,7 +6893,6 @@ msgid "OTP secret key"
msgstr "OTP 秘密"
#: users/models/user.py:783
#: xpack/plugins/cloud/serializers/account_attrs.py:209
msgid "Private key"
msgstr "ssh秘密鍵"
@ -7353,74 +7372,70 @@ msgid "Tencent Cloud (Lighthouse)"
msgstr "テンセント雲(軽量アプリケーション)"
#: xpack/plugins/cloud/const.py:19
msgid "Google Cloud Platform"
msgstr "谷歌雲"
#: xpack/plugins/cloud/const.py:20
msgid "UCloud"
msgstr ""
#: xpack/plugins/cloud/const.py:22
msgid "VMware"
msgstr "VMware"
#: xpack/plugins/cloud/const.py:23 xpack/plugins/cloud/providers/nutanix.py:13
#: xpack/plugins/cloud/const.py:20 xpack/plugins/cloud/providers/nutanix.py:13
msgid "Nutanix"
msgstr "Nutanix"
#: xpack/plugins/cloud/const.py:24
#: xpack/plugins/cloud/const.py:21
msgid "Huawei Private Cloud"
msgstr "華為私有雲"
#: xpack/plugins/cloud/const.py:25
#: xpack/plugins/cloud/const.py:22
msgid "Qingyun Private Cloud"
msgstr "青雲私有雲"
#: xpack/plugins/cloud/const.py:26
#: xpack/plugins/cloud/const.py:23
msgid "CTYun Private Cloud"
msgstr "スカイウィング私有雲"
#: xpack/plugins/cloud/const.py:27
#: xpack/plugins/cloud/const.py:24
msgid "OpenStack"
msgstr "OpenStack"
#: xpack/plugins/cloud/const.py:28
#: xpack/plugins/cloud/const.py:25
msgid "Google Cloud Platform"
msgstr "谷歌雲"
#: xpack/plugins/cloud/const.py:26
msgid "Fusion Compute"
msgstr "融合計算"
#: xpack/plugins/cloud/const.py:33
#: xpack/plugins/cloud/const.py:31
msgid "Private IP"
msgstr "プライベートIP"
#: xpack/plugins/cloud/const.py:34
#: xpack/plugins/cloud/const.py:32
msgid "Public IP"
msgstr "パブリックIP"
#: xpack/plugins/cloud/const.py:38
#: xpack/plugins/cloud/const.py:36
msgid "Instance name"
msgstr "インスタンス名"
#: xpack/plugins/cloud/const.py:39
#: xpack/plugins/cloud/const.py:37
msgid "Instance name and Partial IP"
msgstr "インスタンス名と部分IP"
#: xpack/plugins/cloud/const.py:44
#: xpack/plugins/cloud/const.py:42
msgid "Succeed"
msgstr "成功"
#: xpack/plugins/cloud/const.py:48
#: xpack/plugins/cloud/const.py:46
msgid "Unsync"
msgstr "同期していません"
#: xpack/plugins/cloud/const.py:49
#: xpack/plugins/cloud/const.py:47
msgid "New Sync"
msgstr "新しい同期"
#: xpack/plugins/cloud/const.py:50
#: xpack/plugins/cloud/const.py:48
msgid "Synced"
msgstr "同期済み"
#: xpack/plugins/cloud/const.py:51
#: xpack/plugins/cloud/const.py:49
msgid "Released"
msgstr "リリース済み"
@ -7686,11 +7701,11 @@ msgstr "華南-広州-友好ユーザー環境"
msgid "CN East-Suqian"
msgstr "華東-宿遷"
#: xpack/plugins/cloud/serializers/account.py:64
#: xpack/plugins/cloud/serializers/account.py:63
msgid "Validity display"
msgstr "有効表示"
#: xpack/plugins/cloud/serializers/account.py:65
#: xpack/plugins/cloud/serializers/account.py:64
msgid "Provider display"
msgstr "プロバイダ表示"
@ -7710,7 +7725,6 @@ msgstr "サブスクリプションID"
#: xpack/plugins/cloud/serializers/account_attrs.py:103
#: xpack/plugins/cloud/serializers/account_attrs.py:119
#: xpack/plugins/cloud/serializers/account_attrs.py:149
#: xpack/plugins/cloud/serializers/account_attrs.py:202
msgid "API Endpoint"
msgstr "APIエンドポイント"
@ -7776,10 +7790,6 @@ msgstr "テストポート"
msgid "Test timeout"
msgstr "テストタイムアウト"
#: xpack/plugins/cloud/serializers/account_attrs.py:212
msgid "Project"
msgstr ""
#: xpack/plugins/cloud/serializers/task.py:28
msgid ""
"Only instances matching the IP range will be synced. <br>If the instance "

View File

@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: JumpServer 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-07-13 15:56+0800\n"
"POT-Creation-Date: 2023-07-17 17:12+0800\n"
"PO-Revision-Date: 2021-05-20 10:54+0800\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: JumpServer team<ibuler@qq.com>\n"
@ -94,7 +94,7 @@ msgstr "更新"
#: accounts/const/account.py:29
#: accounts/serializers/automations/change_secret.py:156 audits/const.py:54
#: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19
#: ops/const.py:58 terminal/const.py:72 xpack/plugins/cloud/const.py:43
#: ops/const.py:58 terminal/const.py:77 xpack/plugins/cloud/const.py:41
msgid "Failed"
msgstr "失败"
@ -199,8 +199,8 @@ msgstr "仅创建"
#: audits/models.py:53 authentication/models/connection_token.py:36
#: perms/models/asset_permission.py:64 perms/serializers/permission.py:34
#: terminal/backends/command/models.py:18 terminal/models/session/session.py:31
#: terminal/notifications.py:134 terminal/serializers/command.py:18
#: terminal/templates/terminal/_msg_command_warning.html:4
#: terminal/notifications.py:147 terminal/serializers/command.py:18
#: terminal/templates/terminal/_msg_command_warning.html:39
#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:212
msgid "Asset"
msgstr "资产"
@ -234,6 +234,7 @@ msgstr "来源 ID"
#: assets/serializers/gateway.py:28 audits/models.py:54 ops/models/base.py:18
#: perms/models/asset_permission.py:70 perms/serializers/permission.py:39
#: terminal/backends/command/models.py:19 terminal/models/session/session.py:33
#: terminal/templates/terminal/_msg_command_warning.html:45
#: tickets/models/ticket/command_confirm.py:13 xpack/plugins/cloud/models.py:85
msgid "Account"
msgstr "账号"
@ -651,16 +652,17 @@ msgstr "ID"
#: perms/serializers/permission.py:30 rbac/builtin.py:123
#: rbac/models/rolebinding.py:49 terminal/backends/command/models.py:17
#: terminal/models/session/session.py:29 terminal/models/session/sharing.py:32
#: terminal/notifications.py:135 terminal/notifications.py:183
#: terminal/serializers/command.py:17 tickets/models/comment.py:21
#: users/const.py:14 users/models/user.py:947 users/models/user.py:978
#: users/serializers/group.py:18
#: terminal/notifications.py:148 terminal/notifications.py:196
#: terminal/serializers/command.py:17
#: terminal/templates/terminal/_msg_command_warning.html:33
#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:947
#: users/models/user.py:978 users/serializers/group.py:18
msgid "User"
msgstr "用户"
#: accounts/serializers/account/account.py:428
#: authentication/templates/authentication/_access_key_modal.html:33
#: terminal/notifications.py:137 terminal/notifications.py:185
#: terminal/notifications.py:150 terminal/notifications.py:198
msgid "Date"
msgstr "日期"
@ -739,7 +741,7 @@ msgstr "自动化任务执行历史"
#: accounts/serializers/automations/change_secret.py:155 audits/const.py:53
#: audits/models.py:59 audits/signal_handlers/activity_log.py:33
#: common/const/choices.py:18 ops/const.py:56 ops/serializers/celery.py:40
#: terminal/const.py:71 terminal/models/session/sharing.py:107
#: terminal/const.py:76 terminal/models/session/sharing.py:107
#: tickets/views/approve.py:114
msgid "Success"
msgstr "成功"
@ -839,11 +841,11 @@ msgid "Accounts"
msgstr "账号管理"
#: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60
#: ops/serializers/job.py:55 terminal/const.py:79
#: ops/serializers/job.py:55 terminal/const.py:84
#: terminal/models/session/session.py:42 terminal/serializers/command.py:19
#: terminal/templates/terminal/_msg_command_alert.html:12
#: terminal/templates/terminal/_msg_command_execute_alert.html:10
#: terminal/templates/terminal/_msg_command_warning.html:16
#: terminal/templates/terminal/_msg_command_warning.html:51
msgid "Command"
msgstr "命令"
@ -875,7 +877,7 @@ msgid "The generated regular expression is incorrect: {}"
msgstr "生成的正则表达式有误"
#: acls/models/command_acl.py:100
#: terminal/templates/terminal/_msg_command_warning.html:10
#: terminal/templates/terminal/_msg_command_warning.html:57
msgid "Command acl"
msgstr "命令过滤"
@ -971,7 +973,7 @@ msgid "Applications"
msgstr "应用管理"
#: applications/models.py:16 xpack/plugins/cloud/models.py:33
#: xpack/plugins/cloud/serializers/account.py:63
#: xpack/plugins/cloud/serializers/account.py:62
msgid "Attrs"
msgstr "属性"
@ -1974,6 +1976,7 @@ msgid "Rename dir"
msgstr "映射目录"
#: audits/const.py:23 rbac/tree.py:229
#: terminal/templates/terminal/_msg_command_warning.html:71
msgid "View"
msgstr "查看"
@ -2057,7 +2060,7 @@ msgstr "文件"
#: terminal/models/session/replay.py:9 terminal/models/session/sharing.py:18
#: terminal/models/session/sharing.py:81
#: terminal/templates/terminal/_msg_command_alert.html:10
#: terminal/templates/terminal/_msg_command_warning.html:7
#: terminal/templates/terminal/_msg_command_warning.html:69
#: tickets/models/ticket/command_confirm.py:15
msgid "Session"
msgstr "会话"
@ -2076,7 +2079,7 @@ msgid "Resource"
msgstr "资源"
#: audits/models.py:96 audits/models.py:142 audits/models.py:168
#: terminal/serializers/command.py:61
#: terminal/serializers/command.py:76
msgid "Datetime"
msgstr "日期"
@ -2982,7 +2985,7 @@ msgid "Copy success"
msgstr "复制成功"
#: authentication/utils.py:28 common/utils/ip/geoip/utils.py:24
#: xpack/plugins/cloud/const.py:29
#: xpack/plugins/cloud/const.py:27
msgid "LAN"
msgstr "局域网"
@ -3137,7 +3140,7 @@ msgstr "定时触发"
msgid "Ready"
msgstr "准备"
#: common/const/choices.py:16 terminal/const.py:70 tickets/const.py:29
#: common/const/choices.py:16 terminal/const.py:75 tickets/const.py:29
#: tickets/const.py:39
msgid "Pending"
msgstr "待定的"
@ -3868,6 +3871,7 @@ msgstr "组织管理"
#: orgs/mixins/models.py:57 orgs/mixins/serializers.py:25 orgs/models.py:89
#: rbac/const.py:7 rbac/models/rolebinding.py:56
#: rbac/serializers/rolebinding.py:40 settings/serializers/auth/ldap.py:63
#: terminal/templates/terminal/_msg_command_warning.html:75
#: tickets/models/ticket/general.py:302 tickets/serializers/ticket/ticket.py:60
msgid "Organization"
msgstr "组织"
@ -5604,7 +5608,7 @@ msgstr "测试失败: {}"
msgid "Test successful"
msgstr "测试成功"
#: terminal/api/component/storage.py:124 terminal/notifications.py:218
#: terminal/api/component/storage.py:124 terminal/notifications.py:231
#: terminal/tasks.py:144
msgid "Test failure: Account invalid"
msgstr "测试失败: 账号无效"
@ -5637,7 +5641,7 @@ msgstr "终端管理"
msgid "Input"
msgstr "输入"
#: terminal/backends/command/models.py:21 terminal/serializers/command.py:59
#: terminal/backends/command/models.py:21 terminal/serializers/command.py:74
msgid "Output"
msgstr "输出"
@ -5661,40 +5665,40 @@ msgstr "审批 & 接受"
msgid "Review & Cancel"
msgstr "审批 & 取消"
#: terminal/const.py:39
#: terminal/const.py:44
msgid "Critical"
msgstr "严重"
#: terminal/const.py:40
#: terminal/const.py:45
msgid "High"
msgstr "较高"
#: terminal/const.py:41 terminal/const.py:77
#: terminal/const.py:46 terminal/const.py:82
#: users/templates/users/reset_password.html:50
msgid "Normal"
msgstr "正常"
#: terminal/const.py:42
#: terminal/const.py:47
msgid "Offline"
msgstr "离线"
#: terminal/const.py:73
#: terminal/const.py:78
msgid "Mismatch"
msgstr "未匹配"
#: terminal/const.py:78
#: terminal/const.py:83
msgid "Tunnel"
msgstr "隧道"
#: terminal/const.py:80
#: terminal/const.py:85
msgid "SFTP"
msgstr "SFTP"
#: terminal/const.py:84
#: terminal/const.py:89
msgid "Read Only"
msgstr "只读"
#: terminal/const.py:85
#: terminal/const.py:90
msgid "Writable"
msgstr "读写"
@ -5869,7 +5873,7 @@ msgstr "录像存储"
msgid "type"
msgstr "类型"
#: terminal/models/component/terminal.py:89 terminal/serializers/command.py:62
#: terminal/models/component/terminal.py:89 terminal/serializers/command.py:77
msgid "Remote Address"
msgstr "远端地址"
@ -5986,35 +5990,36 @@ msgstr "验证码不正确"
msgid "You have already joined this session"
msgstr "您已经加入过此会话"
#: terminal/notifications.py:21
#: terminal/notifications.py:22
msgid "Sessions"
msgstr "会话管理"
#: terminal/notifications.py:68
#: terminal/notifications.py:69
#: terminal/templates/terminal/_msg_command_warning.html:5
msgid "Danger command warning"
msgstr "危险命令告警"
#: terminal/notifications.py:109
#: terminal/notifications.py:122
msgid "Danger command alert"
msgstr "危险命令告警"
#: terminal/notifications.py:136 terminal/notifications.py:184
#: terminal/notifications.py:149 terminal/notifications.py:197
msgid "Level"
msgstr "级别"
#: terminal/notifications.py:154
#: terminal/notifications.py:167
msgid "Batch danger command alert"
msgstr "批量危险命令告警"
#: terminal/notifications.py:202
#: terminal/notifications.py:215
msgid "Command and replay storage"
msgstr "命令及录像存储"
#: terminal/notifications.py:203
#: terminal/notifications.py:216
msgid "Connectivity alarm"
msgstr "可连接性告警"
#: terminal/notifications.py:228
#: terminal/notifications.py:241
#: terminal/templates/terminal/_msg_check_command_replay_storage_connectivity.html:4
msgid "Invalid storage"
msgstr "无效的存储"
@ -6096,11 +6101,23 @@ msgstr "命令过滤器"
msgid "Command Group"
msgstr "命令组"
#: terminal/serializers/command.py:58
#: terminal/serializers/command.py:56
msgid "Invalid command filter ACL id"
msgstr "无效的 命令过滤器 ID"
#: terminal/serializers/command.py:60
msgid "Invalid command group id"
msgstr "无效的 命令组 ID"
#: terminal/serializers/command.py:64
msgid "Invalid session id"
msgstr "无效的 Session ID"
#: terminal/serializers/command.py:73
msgid "Account "
msgstr "账号"
#: terminal/serializers/command.py:60
#: terminal/serializers/command.py:75
msgid "Timestamp"
msgstr "时间戳"
@ -6271,14 +6288,18 @@ msgid "Check command replay storage connectivity"
msgstr "检查命令及录像存储可连接性 "
#: terminal/templates/terminal/_msg_command_alert.html:10
#: terminal/templates/terminal/_msg_command_warning.html:5
#: terminal/templates/terminal/_msg_command_warning.html:8
#: terminal/templates/terminal/_msg_command_warning.html:11
#: terminal/templates/terminal/_msg_command_warning.html:14
msgid "view"
msgstr "查看"
#: terminal/templates/terminal/_msg_command_warning.html:13
#: terminal/templates/terminal/_msg_command_warning.html:21
msgid "Item"
msgstr "项目"
#: terminal/templates/terminal/_msg_command_warning.html:25
msgid "Url"
msgstr "链接"
#: terminal/templates/terminal/_msg_command_warning.html:63
msgid "Command acl group"
msgstr "命令过滤组"
@ -6746,7 +6767,6 @@ msgid "Not a valid ssh public key"
msgstr "SSH密钥不合法"
#: users/forms/profile.py:173 users/models/user.py:786
#: xpack/plugins/cloud/serializers/account_attrs.py:206
msgid "Public key"
msgstr "SSH公钥"
@ -6775,7 +6795,6 @@ msgid "OTP secret key"
msgstr "OTP 密钥"
#: users/models/user.py:783
#: xpack/plugins/cloud/serializers/account_attrs.py:209
msgid "Private key"
msgstr "ssh私钥"
@ -7242,74 +7261,70 @@ msgid "Tencent Cloud (Lighthouse)"
msgstr "腾讯云(轻量服务器应用)"
#: xpack/plugins/cloud/const.py:19
msgid "Google Cloud Platform"
msgstr "谷歌云"
#: xpack/plugins/cloud/const.py:20
msgid "UCloud"
msgstr ""
#: xpack/plugins/cloud/const.py:22
msgid "VMware"
msgstr "VMware"
#: xpack/plugins/cloud/const.py:23 xpack/plugins/cloud/providers/nutanix.py:13
#: xpack/plugins/cloud/const.py:20 xpack/plugins/cloud/providers/nutanix.py:13
msgid "Nutanix"
msgstr "Nutanix"
#: xpack/plugins/cloud/const.py:24
#: xpack/plugins/cloud/const.py:21
msgid "Huawei Private Cloud"
msgstr "华为私有云"
#: xpack/plugins/cloud/const.py:25
#: xpack/plugins/cloud/const.py:22
msgid "Qingyun Private Cloud"
msgstr "青云私有云"
#: xpack/plugins/cloud/const.py:26
#: xpack/plugins/cloud/const.py:23
msgid "CTYun Private Cloud"
msgstr "天翼私有云"
#: xpack/plugins/cloud/const.py:27
#: xpack/plugins/cloud/const.py:24
msgid "OpenStack"
msgstr "OpenStack"
#: xpack/plugins/cloud/const.py:28
#: xpack/plugins/cloud/const.py:25
msgid "Google Cloud Platform"
msgstr "谷歌云"
#: xpack/plugins/cloud/const.py:26
msgid "Fusion Compute"
msgstr "融合计算"
#: xpack/plugins/cloud/const.py:33
#: xpack/plugins/cloud/const.py:31
msgid "Private IP"
msgstr "私有IP"
#: xpack/plugins/cloud/const.py:34
#: xpack/plugins/cloud/const.py:32
msgid "Public IP"
msgstr "公网IP"
#: xpack/plugins/cloud/const.py:38
#: xpack/plugins/cloud/const.py:36
msgid "Instance name"
msgstr "实例名称"
#: xpack/plugins/cloud/const.py:39
#: xpack/plugins/cloud/const.py:37
msgid "Instance name and Partial IP"
msgstr "实例名称和部分IP"
#: xpack/plugins/cloud/const.py:44
#: xpack/plugins/cloud/const.py:42
msgid "Succeed"
msgstr "成功"
#: xpack/plugins/cloud/const.py:48
#: xpack/plugins/cloud/const.py:46
msgid "Unsync"
msgstr "未同步"
#: xpack/plugins/cloud/const.py:49
#: xpack/plugins/cloud/const.py:47
msgid "New Sync"
msgstr "新同步"
#: xpack/plugins/cloud/const.py:50
#: xpack/plugins/cloud/const.py:48
msgid "Synced"
msgstr "已同步"
#: xpack/plugins/cloud/const.py:51
#: xpack/plugins/cloud/const.py:49
msgid "Released"
msgstr "已释放"
@ -7575,11 +7590,11 @@ msgstr "华南-广州-友好用户环境"
msgid "CN East-Suqian"
msgstr "华东-宿迁"
#: xpack/plugins/cloud/serializers/account.py:64
#: xpack/plugins/cloud/serializers/account.py:63
msgid "Validity display"
msgstr "有效性显示"
#: xpack/plugins/cloud/serializers/account.py:65
#: xpack/plugins/cloud/serializers/account.py:64
msgid "Provider display"
msgstr "服务商显示"
@ -7599,7 +7614,6 @@ msgstr "订阅 ID"
#: xpack/plugins/cloud/serializers/account_attrs.py:103
#: xpack/plugins/cloud/serializers/account_attrs.py:119
#: xpack/plugins/cloud/serializers/account_attrs.py:149
#: xpack/plugins/cloud/serializers/account_attrs.py:202
msgid "API Endpoint"
msgstr "API 端点"
@ -7664,10 +7678,6 @@ msgstr "测试端口"
msgid "Test timeout"
msgstr "测试超时时间"
#: xpack/plugins/cloud/serializers/account_attrs.py:212
msgid "Project"
msgstr ""
#: xpack/plugins/cloud/serializers/task.py:28
msgid ""
"Only instances matching the IP range will be synced. <br>If the instance "

View File

@ -27,6 +27,7 @@ from orgs.mixins.models import JMSOrgBaseModel
from perms.models import AssetPermission
from perms.utils import UserPermAssetUtil
from terminal.notifications import CommandExecutionAlert
from terminal.notifications import CommandWarningMessage
def get_parent_keys(key, include_self=True):
@ -398,6 +399,16 @@ class JobExecution(JMSOrgBaseModel):
"user": self.creator,
}).publish_async()
raise Exception("command is rejected by ACL")
elif acl.is_action(CommandFilterACL.ActionChoices.warning):
# TODO: warning message
# user = ''
# command = {
# 'user': '',
# 'user_id': ''
# }
# CommandWarningMessage(user, command).publish_async()
return True
return False
def check_command_acl(self):

View File

@ -1,17 +1,17 @@
# -*- coding: utf-8 -*-
#
from django.conf import settings
from django.utils import translation
from django.utils import timezone
from rest_framework import generics
from rest_framework.fields import DateTimeField
from rest_framework.response import Response
from acls.models import CommandFilterACL
from acls.models import CommandFilterACL, CommandGroup
from terminal.models import CommandStorage, Session, Command
from terminal.filters import CommandFilter
from orgs.utils import current_org
from common.api import JMSBulkModelViewSet
from common.utils import get_logger, is_uuid
from common.utils import get_logger
from terminal.serializers import (
SessionCommandSerializer, InsecureCommandAlertSerializer
)
@ -201,30 +201,44 @@ class InsecureCommandAlertAPI(generics.CreateAPIView):
serializer = InsecureCommandAlertSerializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
commands = serializer.validated_data
acl_ids = []
for cmd in commands:
acl_id = cmd.get('cmd_filter_acl')
if not is_uuid(acl_id):
continue
acl_ids.append(acl_id)
acls = CommandFilterACL.objects.filter(id__in=acl_ids)
acls_mapper = {str(acl.id): acl for acl in acls}
session_ids, acl_ids, cmd_group_ids = set(), set(), set()
for command in commands:
risk_level = command.get('risk_level')
session_ids.add(command.get('session'))
acl_ids.add(command.get('cmd_filter_acl'))
cmd_group_ids.add(command.get('cmd_group'))
sessions = Session.objects.filter(id__in=session_ids).only(
'id', 'org_id', 'asset', 'asset_id', 'user', 'user_id', 'account', 'account_id'
)
session_mapper = {str(i.id): i for i in sessions}
acls = CommandFilterACL.objects.filter(id__in=acl_ids).only('id', 'name', 'reviewers')
acl_mapper = {str(i.id): i for i in acls}
cmd_groups = CommandGroup.objects.filter(id__in=cmd_group_ids).only('id', 'name')
cmd_group_mapper = {str(i.id): i for i in cmd_groups}
lang = request.stream.COOKIES.get('django_language', 'zh')
with translation.override(lang):
for command in commands:
cmd_acl = acl_mapper.get(command['cmd_filter_acl'])
command['_cmd_filter_acl'] = cmd_acl
cmd_group = cmd_group_mapper.get(command['cmd_group'])
command['_cmd_group'] = cmd_group
session = session_mapper.get(command['session'])
if session:
command.update({
'_user_id': session.user_id,
'_asset_id': session.asset_id,
'_account_id': session.account_id,
'_org_name': session.org.name
})
risk_level = command.get('risk_level')
if risk_level in [RiskLevelChoices.reject, RiskLevelChoices.review_reject]:
CommandAlertMessage(command).publish_async()
elif risk_level in [RiskLevelChoices.warning]:
for reviewer in cmd_acl.reviewers.all():
CommandWarningMessage(reviewer, command).publish_async()
else:
logger.info(f'Risk level ignore: {risk_level}')
if risk_level in [RiskLevelChoices.reject, RiskLevelChoices.review_reject]:
CommandAlertMessage(command).publish_async()
elif risk_level in [RiskLevelChoices.warning]:
acl_id = command.get('cmd_filter_acl')
acl = acls_mapper.get(acl_id)
if not acl:
logger.info(f'ACL not found: {acl_id}')
continue
for reviewer in acl.reviewers.all():
CommandWarningMessage(reviewer, command).publish_async()
else:
logger.info(f'Risk level ignore: {risk_level}')
return Response({'msg': 'ok'})

View File

@ -44,7 +44,7 @@ class AbstractSessionCommand(OrgModelMixin):
@classmethod
def get_risk_level_str(cls, risk_level):
risk_mapper = dict(cls.RiskLevelChoices.choices)
risk_mapper = dict(RiskLevelChoices.choices)
return risk_mapper.get(risk_level)
def to_dict(self):

View File

@ -11,11 +11,15 @@ from notifications.backends import BACKEND
from notifications.models import SystemMsgSubscription
from notifications.notifications import SystemMessage, UserMessage
from terminal.models import Session, Command
from acls.models import CommandFilterACL, CommandGroup
from users.models import User
logger = get_logger(__name__)
__all__ = ('CommandAlertMessage', 'CommandExecutionAlert', 'StorageConnectivityMessage')
__all__ = (
'CommandAlertMessage', 'CommandExecutionAlert', 'StorageConnectivityMessage',
'CommandWarningMessage'
)
CATEGORY = 'terminal'
CATEGORY_LABEL = _('Sessions')
@ -70,32 +74,72 @@ class CommandWarningMessage(CommandAlertMixin, UserMessage):
def __init__(self, user, command):
super().__init__(user)
self.command = command
def get_html_msg(self) -> dict:
session = self.command.get('session')
session_url = reverse(
'api-terminal:session-detail', kwargs={'pk': session},
external=True, api_to_ui=True
) + '?oid={}'.format(self.command['org_id'])
command = self.command
asset = self.command.get('asset')
asset_url = reverse(
'assets:asset-detail', kwargs={'pk': asset},
api_to_ui=True, external=True, is_console=True
) + '?oid={}'.format(self.command.get('org_id'))
command_input = command['input']
user = command['user']
user_id = command.get('_user_id', '')
asset = command['asset']
asset_id = command.get('_asset_id', '')
account = command['account']
account_id = command.get('_account_id', '')
cmd_acl = command.get('_cmd_filter_acl')
cmd_group = command.get('_cmd_group')
session_id = command['session']
org_id = command['org_id']
org_name = command.get('_org_name') or org_id
cmd_filter_acl = self.command.get('cmd_filter_acl')
cmd_group = self.command.get('cmd_group')
user_url = asset_url = account_url = session_url = ''
if user_id:
user_url = reverse(
'users:user-detail', kwargs={'pk': user_id},
api_to_ui=True, external=True, is_console=True
) + '?oid={}'.format(org_id)
if asset_id:
asset_url = reverse(
'assets:asset-detail', kwargs={'pk': asset_id},
api_to_ui=True, external=True, is_console=True
) + '?oid={}'.format(org_id)
if account_id:
account_url = reverse(
'accounts:account-detail', kwargs={'pk': account_id},
api_to_ui=True, external=True, is_console=True
) + '?oid={}'.format(org_id)
if session_id:
session_url = reverse(
'api-terminal:session-detail', kwargs={'pk': session_id},
external=True, api_to_ui=True
) + '?oid={}'.format(org_id)
session_url = session_url.replace('/terminal/sessions/', '/audit/sessions/sessions/')
# Command ACL
cmd_acl_url = cmd_group_url = ''
cmd_acl_name = cmd_group_name = ''
if cmd_acl:
cmd_acl_name = cmd_acl.name
cmd_acl_url = settings.SITE_URL + f'/ui/#/console/perms/cmd-acls/{cmd_acl.id}/'
if cmd_group:
cmd_group_name = cmd_group.name
cmd_group_url = settings.SITE_URL + f'/ui/#/console/perms/cmd-groups/{cmd_group.id}/'
context = {
"command": self.command['input'],
'command': command_input,
'user': user,
'user_url': user_url,
'asset': asset,
'asset_url': asset_url,
'session_url': session_url.replace(
'/terminal/sessions/', '/audit/sessions/sessions/'
),
'cmd_filter_acl_url': settings.SITE_URL + '/ui/#/console/perms/cmd-acls/%s/' % cmd_filter_acl,
'cmd_group_url': settings.SITE_URL + '/ui/#/console/perms/cmd-groups/%s/' % cmd_group,
'account': account,
'account_url': account_url,
'cmd_filter_acl': cmd_acl_name,
'cmd_filter_acl_url': cmd_acl_url,
'cmd_group': cmd_group_name,
'cmd_group_url': cmd_group_url,
'session_url': session_url,
'org': org_name,
}
message = render_to_string('terminal/_msg_command_warning.html', context)
return {
'subject': self.subject,

View File

@ -2,12 +2,12 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.utils import pretty_string
from common.utils import pretty_string, is_uuid, get_logger
from common.serializers.fields import LabeledChoiceField
from terminal.backends.command.models import AbstractSessionCommand
from terminal.models import Command
from terminal.const import RiskLevelChoices
logger = get_logger(__name__)
__all__ = ['SessionCommandSerializer', 'InsecureCommandAlertSerializer']
@ -39,17 +39,32 @@ class SimpleSessionCommandSerializer(serializers.ModelSerializer):
class InsecureCommandAlertSerializer(SimpleSessionCommandSerializer):
cmd_filter_acl = serializers.CharField(
max_length=128, required=False, label=_("Command Filter ACL")
max_length=36, required=False, label=_("Command Filter ACL")
)
cmd_group = serializers.CharField(
max_length=128, required=True, label=_("Command Group")
max_length=36, required=True, label=_("Command Group")
)
class Meta(SimpleSessionCommandSerializer.Meta):
fields = SimpleSessionCommandSerializer.Meta.fields + [
'cmd_filter_acl', 'cmd_group'
'cmd_filter_acl', 'cmd_group',
]
def validate(self, attrs):
if not is_uuid(attrs['cmd_filter_acl']):
raise serializers.ValidationError(
_("Invalid command filter ACL id")
)
if not is_uuid(attrs['cmd_group']):
raise serializers.ValidationError(
_("Invalid command group id")
)
if not is_uuid(attrs['session']):
raise serializers.ValidationError(
_("Invalid session id")
)
return super().validate(attrs)
class SessionCommandSerializerMixin(serializers.Serializer):
"""使用这个类作为基础Command Log Serializer类, 用来序列化"""
@ -74,4 +89,3 @@ class SessionCommandSerializer(SessionCommandSerializerMixin, SimpleSessionComma
fields = SimpleSessionCommandSerializer.Meta.fields + [
'id', 'account', 'output', 'timestamp', 'timestamp_display', 'remote_addr'
]

View File

@ -1,23 +1,82 @@
{% load i18n %}
<div>
<b>{% trans 'Asset' %}:</b>
<span><a href="{{ asset_url }}" target="_blank">{% trans 'view' %}</a></span>
<br />
<b>{% trans 'Session' %}:</b>
<a href="{{ session_url }}" target="_blank">{% trans 'view' %}</a>
<br />
<b>{% trans 'Command acl' %}:</b>
<a href="{{ cmd_filter_acl_url }}" target="_blank">{% trans 'view' %}</a>
<br />
<b>{% trans 'Command acl group' %}:</b>
<a href="{{ cmd_group_url }}" target="_blank">{% trans 'view' %}</a>
<br />
<b>{% trans 'Command' %}: </b><br />
<code>
<pre style="
background-color: hsl(0,0%,96.5%);
border-radius: 5px;
"> {{ command }} </pre>
</code>
<p>
{% blocktranslate %}Danger command warning{% endblocktranslate %}
</p>
<table style="
display: inline-block;
margin: .6em 0;
max-width: 100%;
min-width: 60%;
white-space: nowrap;">
<thead>
<tr style="
text-align: left;
text-transform: uppercase;
background: rgb(249, 238, 181);
border: 1px solid #d9d7ce;">
<th style="
padding: 0 .6em;">
{% trans 'Item' %}
</th>
<th style="
padding: 0 .6em;">
{% trans 'Url' %}
</th>
</tr>
</thead>
<tbody style="
background: #fbfbf9;
border: 1px solid #d9d7ce;">
<tr>
<td style="padding: 0 .6em;">{% trans 'User' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<a href="{{ user_url }}" target="_blank">{{ user }}</a>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Asset' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<a href="{{ asset_url }}" target="_blank">{{ asset }}</a>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Account' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<a href="{{ account_url }}" target="_blank">{{ account }}</a>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Command' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<span>{{ command }}</span>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Command acl' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<a href="{{ cmd_filter_acl_url }}" target="_blank">{{ cmd_filter_acl }}</a>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Command acl group' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<a href="{{ cmd_group_url }}" target="_blank">{{ cmd_group }}</a>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Session' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<a href="{{ session_url }}" target="_blank">{% trans 'View' %}</a>
</td>
</tr>
<tr>
<td style="padding: 0 .6em;">{% trans 'Organization' %}</td>
<td style="white-space: normal; padding: 0 .6em;">
<span>{{ org }}</span>
</td>
</tr>
</tbody>
</table>
</div>