Luna is a separately deployed program, you need to deploy Luna, coco, "
@@ -2421,8 +2438,8 @@ msgstr "完成时间"
#: ops/models/adhoc.py:326 ops/templates/ops/adhoc_history.html:57
#: ops/templates/ops/task_history.html:63 ops/templates/ops/task_list.html:33
-#: xpack/plugins/change_auth_plan/models.py:248
-#: xpack/plugins/change_auth_plan/models.py:418
+#: xpack/plugins/change_auth_plan/models.py:249
+#: xpack/plugins/change_auth_plan/models.py:419
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:58
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_subtask_list.html:16
msgid "Time"
@@ -2572,33 +2589,33 @@ msgstr "任务列表"
msgid "Go"
msgstr ""
-#: ops/templates/ops/command_execution_create.html:148
+#: ops/templates/ops/command_execution_create.html:152
msgid "Selected assets"
msgstr "已选择资产"
-#: ops/templates/ops/command_execution_create.html:151
+#: ops/templates/ops/command_execution_create.html:155
msgid "In total"
msgstr "总共"
-#: ops/templates/ops/command_execution_create.html:186
+#: ops/templates/ops/command_execution_create.html:190
msgid ""
"Select the left asset, select the running system user, execute command in "
"batch"
msgstr "选择左侧资产, 选择运行的系统用户,批量执行命令"
-#: ops/templates/ops/command_execution_create.html:204
+#: ops/templates/ops/command_execution_create.html:208
msgid "Unselected assets"
msgstr "没有选中资产"
-#: ops/templates/ops/command_execution_create.html:208
+#: ops/templates/ops/command_execution_create.html:212
msgid "No input command"
msgstr "没有输入命令"
-#: ops/templates/ops/command_execution_create.html:212
+#: ops/templates/ops/command_execution_create.html:216
msgid "No system user was selected"
msgstr "没有选择系统用户"
-#: ops/templates/ops/command_execution_create.html:257
+#: ops/templates/ops/command_execution_create.html:261
msgid "Pending"
msgstr "等待"
@@ -2683,11 +2700,23 @@ msgstr "命令执行"
msgid "Organization"
msgstr "组织管理"
-#: perms/forms.py:39 perms/models.py:29 perms/models.py:85
+#: perms/const.py:18 settings/forms.py:136
+msgid "All"
+msgstr "全部"
+
+#: perms/const.py:20
+msgid "Upload file"
+msgstr "上传文件"
+
+#: perms/const.py:21
+msgid "Download file"
+msgstr "下载文件"
+
+#: perms/forms.py:39 perms/models.py:49 perms/models.py:106
#: perms/templates/perms/asset_permission_list.html:55
#: perms/templates/perms/asset_permission_list.html:75
#: perms/templates/perms/asset_permission_list.html:122 templates/_nav.html:14
-#: users/forms.py:253 users/models/group.py:26 users/models/user.py:60
+#: users/forms.py:253 users/models/group.py:26 users/models/user.py:67
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:213
#: users/templates/users/user_list.html:26
@@ -2695,22 +2724,28 @@ msgstr "组织管理"
msgid "User group"
msgstr "用户组"
-#: perms/forms.py:61
+#: perms/forms.py:58
+msgid ""
+"Tips: The RDP protocol does not support separate controls for uploading or "
+"downloading files"
+msgstr "提示:RDP 协议不支持单独控制上传或下载文件"
+
+#: perms/forms.py:68
msgid "User or group at least one required"
msgstr "用户和用户组至少选一个"
-#: perms/forms.py:70
+#: perms/forms.py:77
msgid "Asset or group at least one required"
msgstr "资产和节点至少选一个"
-#: perms/models.py:35 perms/models.py:88
+#: perms/models.py:56 perms/models.py:109
#: perms/templates/perms/asset_permission_detail.html:90
-#: users/models/user.py:92 users/templates/users/user_detail.html:107
+#: users/models/user.py:99 users/templates/users/user_detail.html:107
#: users/templates/users/user_profile.html:116
msgid "Date expired"
msgstr "失效日期"
-#: perms/models.py:44 perms/models.py:97 templates/_nav.html:34
+#: perms/models.py:65 perms/models.py:118 templates/_nav.html:34
msgid "Asset permission"
msgstr "资产授权"
@@ -2756,12 +2791,12 @@ msgstr "添加节点"
msgid "Join"
msgstr "加入"
-#: perms/templates/perms/asset_permission_create_update.html:58
+#: perms/templates/perms/asset_permission_create_update.html:61
msgid "Validity period"
msgstr "有效期"
#: perms/templates/perms/asset_permission_detail.html:66
-#: xpack/plugins/license/templates/license/license_detail.html:67
+#: xpack/plugins/license/templates/license/license_detail.html:76
msgid "User count"
msgstr "用户数量"
@@ -2770,7 +2805,7 @@ msgid "User group count"
msgstr "用户组列表"
#: perms/templates/perms/asset_permission_detail.html:74
-#: xpack/plugins/license/templates/license/license_detail.html:63
+#: xpack/plugins/license/templates/license/license_detail.html:72
msgid "Asset count"
msgstr "资产数量"
@@ -2814,29 +2849,29 @@ msgstr "添加用户组"
msgid "Select user groups"
msgstr "选择用户组"
-#: perms/views.py:23 perms/views.py:53 perms/views.py:68 perms/views.py:83
-#: perms/views.py:118 perms/views.py:150 templates/_nav.html:31
+#: perms/views.py:24 perms/views.py:56 perms/views.py:71 perms/views.py:86
+#: perms/views.py:121 perms/views.py:153 templates/_nav.html:31
#: xpack/plugins/orgs/templates/orgs/org_list.html:21
msgid "Perms"
msgstr "权限管理"
-#: perms/views.py:24
+#: perms/views.py:25
msgid "Asset permission list"
msgstr "资产授权列表"
-#: perms/views.py:54
+#: perms/views.py:57
msgid "Create asset permission"
msgstr "创建权限规则"
-#: perms/views.py:69 perms/views.py:84
+#: perms/views.py:72 perms/views.py:87
msgid "Update asset permission"
msgstr "更新资产授权"
-#: perms/views.py:119
+#: perms/views.py:122
msgid "Asset permission user list"
msgstr "资产授权用户列表"
-#: perms/views.py:151
+#: perms/views.py:154
msgid "Asset permission asset list"
msgstr "资产授权资产列表"
@@ -2968,10 +3003,6 @@ msgstr ""
msgid "Enable LDAP auth"
msgstr "启用LDAP认证"
-#: settings/forms.py:136
-msgid "All"
-msgstr "全部"
-
#: settings/forms.py:137
msgid "Auto"
msgstr "自动"
@@ -3123,7 +3154,7 @@ msgid "Please submit the LDAP configuration before import"
msgstr "请先提交LDAP配置再进行导入"
#: settings/templates/settings/_ldap_list_users_modal.html:39
-#: users/models/user.py:56 users/templates/users/user_detail.html:71
+#: users/models/user.py:63 users/templates/users/user_detail.html:71
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
@@ -3349,7 +3380,7 @@ msgstr "商业支持"
#: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:57
-#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:368
+#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:377
msgid "Profile"
msgstr "个人信息"
@@ -3431,9 +3462,9 @@ msgstr ""
#: templates/_nav.html:10 users/views/group.py:27 users/views/group.py:43
#: users/views/group.py:59 users/views/group.py:75 users/views/group.py:91
-#: users/views/login.py:151 users/views/user.py:68 users/views/user.py:83
-#: users/views/user.py:113 users/views/user.py:194 users/views/user.py:355
-#: users/views/user.py:405 users/views/user.py:445
+#: users/views/login.py:154 users/views/user.py:68 users/views/user.py:83
+#: users/views/user.py:122 users/views/user.py:203 users/views/user.py:364
+#: users/views/user.py:414 users/views/user.py:454
msgid "Users"
msgstr "用户管理"
@@ -3822,11 +3853,11 @@ msgstr "地址"
msgid "Alive"
msgstr "在线"
-#: terminal/templates/terminal/terminal_list.html:76
+#: terminal/templates/terminal/terminal_list.html:77
msgid "Accept"
msgstr "接受"
-#: terminal/templates/terminal/terminal_list.html:78
+#: terminal/templates/terminal/terminal_list.html:79
msgid "Reject"
msgstr "拒绝"
@@ -3867,11 +3898,15 @@ msgid ""
"You should use your ssh client tools connect terminal: {}
{}"
msgstr "你可以使用ssh客户端工具连接终端"
-#: users/api/user.py:146
+#: users/api/user.py:69 users/api/user.py:80 users/api/user.py:106
+msgid "You do not have permission."
+msgstr "你没有权限"
+
+#: users/api/user.py:210
msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
-#: users/forms.py:32 users/models/user.py:64
+#: users/forms.py:32 users/models/user.py:71
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
@@ -3964,53 +3999,53 @@ msgstr "复制你的公钥到这里"
msgid "Select users"
msgstr "选择用户"
-#: users/models/user.py:31 users/models/user.py:453
+#: users/models/user.py:35 users/models/user.py:471
msgid "Administrator"
msgstr "管理员"
-#: users/models/user.py:33
+#: users/models/user.py:37
msgid "Application"
msgstr "应用程序"
-#: users/models/user.py:36 users/templates/users/user_profile.html:92
+#: users/models/user.py:40 users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:159
#: users/templates/users/user_profile.html:162
msgid "Disable"
msgstr "禁用"
-#: users/models/user.py:37 users/templates/users/user_profile.html:90
+#: users/models/user.py:41 users/templates/users/user_profile.html:90
#: users/templates/users/user_profile.html:166
msgid "Enable"
msgstr "启用"
-#: users/models/user.py:38 users/templates/users/user_profile.html:88
+#: users/models/user.py:42 users/templates/users/user_profile.html:88
msgid "Force enable"
msgstr "强制启用"
-#: users/models/user.py:67
+#: users/models/user.py:74
msgid "Avatar"
msgstr "头像"
-#: users/models/user.py:70 users/templates/users/user_detail.html:82
+#: users/models/user.py:77 users/templates/users/user_detail.html:82
msgid "Wechat"
msgstr "微信"
-#: users/models/user.py:99 users/templates/users/user_detail.html:103
+#: users/models/user.py:106 users/templates/users/user_detail.html:103
#: users/templates/users/user_list.html:27
#: users/templates/users/user_profile.html:100
msgid "Source"
msgstr "用户来源"
-#: users/models/user.py:103
+#: users/models/user.py:110
msgid "Date password last updated"
msgstr "最后更新密码日期"
-#: users/models/user.py:129 users/templates/users/user_update.html:22
-#: users/views/login.py:45 users/views/login.py:104 users/views/user.py:418
+#: users/models/user.py:136 users/templates/users/user_update.html:22
+#: users/views/login.py:47 users/views/login.py:108 users/views/user.py:427
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
-#: users/models/user.py:456
+#: users/models/user.py:474
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
@@ -4201,7 +4236,7 @@ msgid "Reset link will be generated and sent to the user. "
msgstr "生成重置密码连接,通过邮件发送给用户"
#: users/templates/users/user_detail.html:19
-#: users/templates/users/user_granted_asset.html:18 users/views/user.py:195
+#: users/templates/users/user_granted_asset.html:18 users/views/user.py:204
msgid "User detail"
msgstr "用户详情"
@@ -4317,45 +4352,45 @@ msgstr "添加用户"
msgid "Create user group"
msgstr "创建用户组"
-#: users/templates/users/user_group_list.html:83
+#: users/templates/users/user_group_list.html:85
msgid "This will delete the selected groups !!!"
msgstr "删除选择组"
-#: users/templates/users/user_group_list.html:92
+#: users/templates/users/user_group_list.html:94
msgid "UserGroups Deleted."
msgstr "用户组删除"
-#: users/templates/users/user_group_list.html:93
-#: users/templates/users/user_group_list.html:98
+#: users/templates/users/user_group_list.html:95
+#: users/templates/users/user_group_list.html:100
msgid "UserGroups Delete"
msgstr "用户组删除"
-#: users/templates/users/user_group_list.html:97
+#: users/templates/users/user_group_list.html:99
msgid "UserGroup Deleting failed."
msgstr "用户组删除失败"
-#: users/templates/users/user_list.html:203
+#: users/templates/users/user_list.html:210
msgid "This will delete the selected users !!!"
msgstr "删除选中用户 !!!"
-#: users/templates/users/user_list.html:212
+#: users/templates/users/user_list.html:219
msgid "User Deleted."
msgstr "已被删除"
-#: users/templates/users/user_list.html:213
-#: users/templates/users/user_list.html:218
+#: users/templates/users/user_list.html:220
+#: users/templates/users/user_list.html:225
msgid "User Delete"
msgstr "删除"
-#: users/templates/users/user_list.html:217
+#: users/templates/users/user_list.html:224
msgid "User Deleting failed."
msgstr "用户删除失败"
-#: users/templates/users/user_list.html:253
+#: users/templates/users/user_list.html:260
msgid "User is expired"
msgstr "用户已失效"
-#: users/templates/users/user_list.html:256
+#: users/templates/users/user_list.html:263
msgid "User is inactive"
msgstr "用户已禁用"
@@ -4404,8 +4439,8 @@ msgstr "安装完成后点击下一步进入绑定页面(如已安装,直接
msgid "Administrator Settings force MFA login"
msgstr "管理员设置强制使用MFA登录"
-#: users/templates/users/user_profile.html:120 users/views/user.py:231
-#: users/views/user.py:285
+#: users/templates/users/user_profile.html:120 users/views/user.py:240
+#: users/views/user.py:294
msgid "User groups"
msgstr "用户组"
@@ -4455,7 +4490,7 @@ msgid ""
"corresponding private key."
msgstr "新的公钥已设置成功,请下载对应的私钥"
-#: users/templates/users/user_update.html:4 users/views/user.py:114
+#: users/templates/users/user_update.html:4 users/views/user.py:123
msgid "Update user"
msgstr "更新用户"
@@ -4658,88 +4693,88 @@ msgstr "更新用户组"
msgid "User group granted asset"
msgstr "用户组授权资产"
-#: users/views/login.py:42
+#: users/views/login.py:44
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
-#: users/views/login.py:58
+#: users/views/login.py:60
msgid "Send reset password message"
msgstr "发送重置密码邮件"
-#: users/views/login.py:59
+#: users/views/login.py:61
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
-#: users/views/login.py:72
+#: users/views/login.py:74
msgid "Reset password success"
msgstr "重置密码成功"
-#: users/views/login.py:73
+#: users/views/login.py:75
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
-#: users/views/login.py:88 users/views/login.py:107
+#: users/views/login.py:90 users/views/login.py:106
msgid "Token invalid or expired"
msgstr "Token错误或失效"
-#: users/views/login.py:100
+#: users/views/login.py:102
msgid "Password not same"
msgstr "密码不一致"
-#: users/views/login.py:113 users/views/user.py:128 users/views/user.py:428
+#: users/views/login.py:115 users/views/user.py:137 users/views/user.py:437
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
-#: users/views/login.py:151
+#: users/views/login.py:154
msgid "First login"
msgstr "首次登录"
-#: users/views/user.py:145
+#: users/views/user.py:154
msgid "Bulk update user success"
msgstr "批量更新用户成功"
-#: users/views/user.py:175
+#: users/views/user.py:184
msgid "Bulk update user"
msgstr "批量更新用户"
-#: users/views/user.py:260
+#: users/views/user.py:269
msgid "Invalid file."
msgstr "文件不合法"
-#: users/views/user.py:356
+#: users/views/user.py:365
msgid "User granted assets"
msgstr "用户授权资产"
-#: users/views/user.py:387
+#: users/views/user.py:396
msgid "Profile setting"
msgstr "个人信息设置"
-#: users/views/user.py:406
+#: users/views/user.py:415
msgid "Password update"
msgstr "密码更新"
-#: users/views/user.py:446
+#: users/views/user.py:455
msgid "Public key update"
msgstr "密钥更新"
-#: users/views/user.py:487
+#: users/views/user.py:496
msgid "Password invalid"
msgstr "用户名或密码无效"
-#: users/views/user.py:587
+#: users/views/user.py:596
msgid "MFA enable success"
msgstr "MFA 绑定成功"
-#: users/views/user.py:588
+#: users/views/user.py:597
msgid "MFA enable success, return login page"
msgstr "MFA 绑定成功,返回到登录页面"
-#: users/views/user.py:590
+#: users/views/user.py:599
msgid "MFA disable success"
msgstr "MFA 解绑成功"
-#: users/views/user.py:591
+#: users/views/user.py:600
msgid "MFA disable success, return login page"
msgstr "MFA 解绑成功,返回登录页面"
@@ -4796,8 +4831,8 @@ msgstr ""
"具)
注意: 如果同时设置了定期执行和周期执行,优先使用定期执行"
#: xpack/plugins/change_auth_plan/meta.py:9
-#: xpack/plugins/change_auth_plan/models.py:110
-#: xpack/plugins/change_auth_plan/models.py:252
+#: xpack/plugins/change_auth_plan/models.py:111
+#: xpack/plugins/change_auth_plan/models.py:253
#: xpack/plugins/change_auth_plan/views.py:31
#: xpack/plugins/change_auth_plan/views.py:47
#: xpack/plugins/change_auth_plan/views.py:68
@@ -4821,13 +4856,13 @@ msgid "All assets use different random password"
msgstr "所有资产使用不同的随机密码"
#: xpack/plugins/change_auth_plan/models.py:73
-#: xpack/plugins/change_auth_plan/models.py:141
+#: xpack/plugins/change_auth_plan/models.py:142
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:100
msgid "Cycle perform"
msgstr "周期执行"
#: xpack/plugins/change_auth_plan/models.py:78
-#: xpack/plugins/change_auth_plan/models.py:139
+#: xpack/plugins/change_auth_plan/models.py:140
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:92
msgid "Regularly perform"
msgstr "定期执行"
@@ -4845,32 +4880,32 @@ msgstr "密码策略"
msgid "Password rules"
msgstr "密码规则"
-#: xpack/plugins/change_auth_plan/models.py:209
+#: xpack/plugins/change_auth_plan/models.py:210
msgid "For security, do not change root user's password"
msgstr "为了安全,禁止更改root用户的密码"
-#: xpack/plugins/change_auth_plan/models.py:212
+#: xpack/plugins/change_auth_plan/models.py:213
msgid "Assets is empty, please add the asset"
msgstr "资产为空,请添加资产"
-#: xpack/plugins/change_auth_plan/models.py:256
+#: xpack/plugins/change_auth_plan/models.py:257
msgid "Change auth plan snapshot"
msgstr "改密计划快照"
-#: xpack/plugins/change_auth_plan/models.py:271
-#: xpack/plugins/change_auth_plan/models.py:422
+#: xpack/plugins/change_auth_plan/models.py:272
+#: xpack/plugins/change_auth_plan/models.py:423
msgid "Change auth plan execution"
msgstr "改密计划执行"
-#: xpack/plugins/change_auth_plan/models.py:431
+#: xpack/plugins/change_auth_plan/models.py:432
msgid "Change auth plan execution subtask"
msgstr "改密计划执行子任务"
-#: xpack/plugins/change_auth_plan/models.py:449
+#: xpack/plugins/change_auth_plan/models.py:450
msgid "Authentication failed"
msgstr "认证失败"
-#: xpack/plugins/change_auth_plan/models.py:451
+#: xpack/plugins/change_auth_plan/models.py:452
msgid "Connection timeout"
msgstr "连接超时"
@@ -5217,30 +5252,57 @@ msgid "Interface settings"
msgstr "界面设置"
#: xpack/plugins/interface/templates/interface/interface.html:15
-#: xpack/plugins/interface/views.py:21
+#: xpack/plugins/interface/views.py:24
msgid "Interface setting"
msgstr "界面设置"
-#: xpack/plugins/interface/views.py:20
+#: xpack/plugins/interface/templates/interface/interface.html:73
+#: xpack/plugins/interface/templates/interface/interface.html:108
+#: xpack/plugins/interface/templates/interface/interface.html:115
+msgid "Restore Default"
+msgstr "恢复默认"
+
+#: xpack/plugins/interface/templates/interface/interface.html:98
+msgid "This will restore default Settings of the interface !!!"
+msgstr "您确定要恢复默认初始化吗?"
+
+#: xpack/plugins/interface/templates/interface/interface.html:107
+msgid "Restore default successfully."
+msgstr "恢复默认成功!"
+
+#: xpack/plugins/interface/templates/interface/interface.html:114
+msgid "Restore default failed."
+msgstr "恢复默认失败!"
+
+#: xpack/plugins/interface/views.py:23
msgid "Interface"
msgstr "界面"
+#: xpack/plugins/interface/views.py:49
+msgid "It is already in the default setting state!"
+msgstr "当前已经是初始化状态!"
+
+#: xpack/plugins/interface/views.py:53
+msgid "Restore default successfully!"
+msgstr "恢复默认成功!"
+
#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:94
#: xpack/plugins/license/templates/license/license_detail.html:50
+#: xpack/plugins/license/templates/license/license_detail.html:55
#: xpack/plugins/license/views.py:31
msgid "License"
msgstr "许可证"
#: xpack/plugins/license/models.py:74
msgid "Standard edition"
-msgstr ""
+msgstr "标准版"
#: xpack/plugins/license/models.py:76
msgid "Enterprise edition"
msgstr "企业版"
#: xpack/plugins/license/templates/license/_license_import_modal.html:4
-#: xpack/plugins/license/templates/license/license_detail.html:99
+#: xpack/plugins/license/templates/license/license_detail.html:108
msgid "Import license"
msgstr "导入许可证"
@@ -5253,6 +5315,7 @@ msgid "Please Import License"
msgstr "请导入许可证"
#: xpack/plugins/license/templates/license/license_detail.html:17
+#: xpack/plugins/license/templates/license/license_detail.html:56
msgid "License has expired"
msgstr "许可证已经过期"
@@ -5273,34 +5336,38 @@ msgstr "许可证详情"
msgid "No license"
msgstr "暂无许可证"
-#: xpack/plugins/license/templates/license/license_detail.html:55
+#: xpack/plugins/license/templates/license/license_detail.html:60
+msgid "Subscription ID"
+msgstr "订阅授权ID"
+
+#: xpack/plugins/license/templates/license/license_detail.html:64
msgid "Corporation"
msgstr "公司"
-#: xpack/plugins/license/templates/license/license_detail.html:59
+#: xpack/plugins/license/templates/license/license_detail.html:68
msgid "Expired"
msgstr "过期时间"
-#: xpack/plugins/license/templates/license/license_detail.html:64
-#: xpack/plugins/license/templates/license/license_detail.html:68
-#: xpack/plugins/license/templates/license/license_detail.html:72
-#: xpack/plugins/license/templates/license/license_detail.html:76
+#: xpack/plugins/license/templates/license/license_detail.html:73
+#: xpack/plugins/license/templates/license/license_detail.html:77
+#: xpack/plugins/license/templates/license/license_detail.html:81
+#: xpack/plugins/license/templates/license/license_detail.html:85
msgid "Unlimited"
msgstr "无限制"
-#: xpack/plugins/license/templates/license/license_detail.html:75
+#: xpack/plugins/license/templates/license/license_detail.html:84
msgid "Concurrent connections"
msgstr "并发连接"
-#: xpack/plugins/license/templates/license/license_detail.html:80
+#: xpack/plugins/license/templates/license/license_detail.html:89
msgid "Edition"
msgstr "版本"
-#: xpack/plugins/license/templates/license/license_detail.html:106
+#: xpack/plugins/license/templates/license/license_detail.html:115
msgid "Technology consulting"
msgstr "技术咨询"
-#: xpack/plugins/license/templates/license/license_detail.html:109
+#: xpack/plugins/license/templates/license/license_detail.html:118
msgid "Consult"
msgstr "咨询"
@@ -5356,6 +5423,9 @@ msgstr "创建组织"
msgid "Update org"
msgstr "更新组织"
+#~ msgid "Beijing Duizhan Tech, Inc."
+#~ msgstr "北京堆栈科技有限公司"
+
#~ msgid "Sync User"
#~ msgstr "同步用户"
diff --git a/apps/ops/templates/ops/command_execution_create.html b/apps/ops/templates/ops/command_execution_create.html
index 8352d1607..4aaee0406 100644
--- a/apps/ops/templates/ops/command_execution_create.html
+++ b/apps/ops/templates/ops/command_execution_create.html
@@ -82,6 +82,7 @@
-
{% endblock %}
{% block content %}
diff --git a/apps/templates/_message.html b/apps/templates/_message.html
index 3f13ff87b..bdcbb6d5f 100644
--- a/apps/templates/_message.html
+++ b/apps/templates/_message.html
@@ -47,7 +47,8 @@
{% if messages %}
{% for message in messages %}
- {{ message|safe }}
+{# {{ message|safe }}#}
+ {{ message }}
diff --git a/apps/terminal/serializers/v1.py b/apps/terminal/serializers/v1.py
index adf75c936..27b12b77a 100644
--- a/apps/terminal/serializers/v1.py
+++ b/apps/terminal/serializers/v1.py
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
-from rest_framework_bulk.serializers import BulkListSerializer
from common.mixins import BulkSerializerMixin
+from common.serializers import AdaptedBulkListSerializer
from ..models import Terminal, Status, Session, Task
@@ -29,7 +29,7 @@ class SessionSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class Meta:
model = Session
- list_serializer_class = BulkListSerializer
+ list_serializer_class = AdaptedBulkListSerializer
fields = '__all__'
@@ -44,7 +44,7 @@ class TaskSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class Meta:
fields = '__all__'
model = Task
- list_serializer_class = BulkListSerializer
+ list_serializer_class = AdaptedBulkListSerializer
class ReplaySerializer(serializers.Serializer):
diff --git a/apps/terminal/templates/terminal/terminal_list.html b/apps/terminal/templates/terminal/terminal_list.html
index 3039c0425..479944c00 100644
--- a/apps/terminal/templates/terminal/terminal_list.html
+++ b/apps/terminal/templates/terminal/terminal_list.html
@@ -50,6 +50,7 @@ function initTable() {
buttons: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
+ cellData = htmlEscape(cellData);
var detail_btn = '
' + cellData + '';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
diff --git a/apps/users/api/user.py b/apps/users/api/user.py
index 33874deb7..63c3dab12 100644
--- a/apps/users/api/user.py
+++ b/apps/users/api/user.py
@@ -5,6 +5,7 @@ from django.core.cache import cache
from django.contrib.auth import logout
from django.utils.translation import ugettext as _
+from rest_framework import status
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
@@ -52,9 +53,72 @@ class UserViewSet(IDInFilterMixin, BulkModelViewSet):
self.permission_classes = (IsOrgAdminOrAppUser,)
return super().get_permissions()
+ def _deny_permission(self, instance):
+ """
+ check current user has permission to handle instance
+ (update, destroy, bulk_update, bulk destroy)
+ """
+ return not self.request.user.is_superuser and instance.is_superuser
+
+ def destroy(self, request, *args, **kwargs):
+ """
+ rewrite because limit org_admin destroy superuser
+ """
+ instance = self.get_object()
+ if self._deny_permission(instance):
+ data = {'msg': _("You do not have permission.")}
+ return Response(data=data, status=status.HTTP_403_FORBIDDEN)
+
+ return super().destroy(request, *args, **kwargs)
+
+ def update(self, request, *args, **kwargs):
+ """
+ rewrite because limit org_admin update superuser
+ """
+ instance = self.get_object()
+ if self._deny_permission(instance):
+ data = {'msg': _("You do not have permission.")}
+ return Response(data=data, status=status.HTTP_403_FORBIDDEN)
+
+ return super().update(request, *args, **kwargs)
+
+ def _bulk_deny_permission(self, instances):
+ deny_instances = [i for i in instances if self._deny_permission(i)]
+ if len(deny_instances) > 0:
+ return True
+ else:
+ return False
+
def allow_bulk_destroy(self, qs, filtered):
+ if self._bulk_deny_permission(filtered):
+ return False
return qs.count() != filtered.count()
+ def bulk_update(self, request, *args, **kwargs):
+ """
+ rewrite because limit org_admin update superuser
+ """
+ partial = kwargs.pop('partial', False)
+
+ # restrict the update to the filtered queryset
+ queryset = self.filter_queryset(self.get_queryset())
+ if self._bulk_deny_permission(queryset):
+ data = {'msg': _("You do not have permission.")}
+ return Response(data=data, status=status.HTTP_403_FORBIDDEN)
+
+ serializer = self.get_serializer(
+ queryset, data=request.data, many=True, partial=partial,
+ )
+
+ try:
+ serializer.is_valid(raise_exception=True)
+ except Exception as e:
+ data = {'error': str(e)}
+ return Response(data=data, status=status.HTTP_400_BAD_REQUEST)
+
+ self.perform_bulk_update(serializer)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
permission_classes = (IsOrgAdmin,)
diff --git a/apps/users/models/user.py b/apps/users/models/user.py
index 86751e359..9d2fc1a08 100644
--- a/apps/users/models/user.py
+++ b/apps/users/models/user.py
@@ -3,24 +3,28 @@
#
import uuid
import base64
+import string
+import random
from collections import OrderedDict
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser
-from django.core import signing
from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.shortcuts import reverse
-from common.utils import get_signer, date_expired_default
+from common.utils import get_signer, date_expired_default, get_logger
__all__ = ['User']
+
signer = get_signer()
+logger = get_logger(__file__)
+
class User(AbstractUser):
ROLE_ADMIN = 'Admin'
@@ -47,6 +51,9 @@ class User(AbstractUser):
(SOURCE_OPENID, 'OpenID'),
(SOURCE_RADIUS, 'Radius'),
)
+
+ CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
+
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField(
max_length=128, unique=True, verbose_name=_('Username')
@@ -346,9 +353,32 @@ class User(AbstractUser):
return user_default
def generate_reset_token(self):
- return signer.sign_t(
- {'reset': str(self.id), 'email': self.email}, expires_in=3600
- )
+ letter = string.ascii_letters + string.digits
+ token =''.join([random.choice(letter) for _ in range(50)])
+ self.set_cache(token)
+ return token
+
+ def set_cache(self, token):
+ key = self.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
+ cache.set(key, {'id': self.id, 'email': self.email}, 3600)
+
+ @classmethod
+ def validate_reset_password_token(cls, token):
+ try:
+ key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
+ value = cache.get(key)
+ user_id = value.get('id', '')
+ email = value.get('email', '')
+ user = cls.objects.get(id=user_id, email=email)
+ except (AttributeError, cls.DoesNotExist) as e:
+ logger.error(e, exc_info=True)
+ user = None
+ return user
+
+ @classmethod
+ def expired_reset_password_token(cls, token):
+ key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
+ cache.delete(key)
@property
def otp_enabled(self):
@@ -400,18 +430,6 @@ class User(AbstractUser):
access_key = app.create_access_key()
return app, access_key
- @classmethod
- def validate_reset_token(cls, token):
- try:
- data = signer.unsign_t(token)
- user_id = data.get('reset', None)
- user_email = data.get('email', '')
- user = cls.objects.get(id=user_id, email=user_email)
-
- except (signing.BadSignature, cls.DoesNotExist):
- user = None
- return user
-
def reset_password(self, new_password):
self.set_password(new_password)
self.date_password_last_updated = timezone.now()
diff --git a/apps/users/serializers/v1.py b/apps/users/serializers/v1.py
index 160df7c62..b8c91417d 100644
--- a/apps/users/serializers/v1.py
+++ b/apps/users/serializers/v1.py
@@ -3,10 +3,10 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
-from rest_framework_bulk import BulkListSerializer
from common.utils import get_signer, validate_ssh_public_key
from common.mixins import BulkSerializerMixin
+from common.serializers import AdaptedBulkListSerializer
from ..models import User, UserGroup
signer = get_signer()
@@ -16,7 +16,7 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class Meta:
model = User
- list_serializer_class = BulkListSerializer
+ list_serializer_class = AdaptedBulkListSerializer
fields = [
'id', 'name', 'username', 'email', 'groups', 'groups_display',
'role', 'role_display', 'avatar_url', 'wechat', 'phone',
@@ -52,7 +52,7 @@ class UserGroupSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class Meta:
model = UserGroup
- list_serializer_class = BulkListSerializer
+ list_serializer_class = AdaptedBulkListSerializer
fields = '__all__'
read_only_fields = ['created_by']
diff --git a/apps/users/templates/users/user_detail.html b/apps/users/templates/users/user_detail.html
index deb96eb68..351823a9e 100644
--- a/apps/users/templates/users/user_detail.html
+++ b/apps/users/templates/users/user_detail.html
@@ -22,11 +22,11 @@
{% trans 'Asset granted' %}
- {% trans 'Update' %}
+ {% trans 'Update' %}
-
+
{% trans 'Delete' %}
diff --git a/apps/users/templates/users/user_granted_asset.html b/apps/users/templates/users/user_granted_asset.html
index 6c0534937..41aea998c 100644
--- a/apps/users/templates/users/user_granted_asset.html
+++ b/apps/users/templates/users/user_granted_asset.html
@@ -77,6 +77,7 @@ function initTable() {
ele: $('#user_assets_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
+ cellData = htmlEscape(cellData);
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '
' + cellData + '';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
@@ -91,7 +92,8 @@ function initTable() {
{targets: 4, createdCell: function (td, cellData) {
var users = [];
$.each(cellData, function (id, data) {
- users.push(data.name);
+ var name = htmlEscape(data.name);
+ users.push(name);
});
$(td).html(users.join(', '))
}}
diff --git a/apps/users/templates/users/user_group_granted_asset.html b/apps/users/templates/users/user_group_granted_asset.html
index 70e2beef6..e4a8d77f0 100644
--- a/apps/users/templates/users/user_group_granted_asset.html
+++ b/apps/users/templates/users/user_group_granted_asset.html
@@ -77,6 +77,7 @@ function initTable() {
ele: $('#user_assets_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
+ cellData = htmlEscape(cellData);
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
var detail_btn = '
' + cellData + '';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
@@ -91,7 +92,8 @@ function initTable() {
{targets: 4, createdCell: function (td, cellData) {
var users = [];
$.each(cellData, function (id, data) {
- users.push(data.name);
+ var name = htmlEscape(data.name);
+ users.push(name);
});
$(td).html(users.join(', '))
}}
diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html
index b2eecc01f..6f6c6fc72 100644
--- a/apps/users/templates/users/user_group_list.html
+++ b/apps/users/templates/users/user_group_list.html
@@ -28,6 +28,7 @@ $(document).ready(function() {
buttons: [],
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
+ cellData = htmlEscape(cellData);
var detail_btn = '
' + cellData + '';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}},
@@ -36,6 +37,7 @@ $(document).ready(function() {
$(td).html(html);
}},
{targets: 3, createdCell: function (td, cellData) {
+ cellData = htmlEscape(cellData);
var innerHtml = cellData.length > 30 ? cellData.substring(0, 30) + '...': cellData;
$(td).html('
' + innerHtml + '');
}},
diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html
index 2ebfa6664..15b5fb2f9 100644
--- a/apps/users/templates/users/user_list.html
+++ b/apps/users/templates/users/user_list.html
@@ -59,6 +59,7 @@ function initTable() {
ele: $('#user_list_table'),
columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) {
+ cellData = htmlEscape(cellData);
var detail_btn = '
' + cellData + '';
$(td).html(detail_btn.replace("{{ DEFAULT_PK }}", rowData.id));
}},
@@ -77,10 +78,16 @@ function initTable() {
}
}},
{targets: 7, createdCell: function (td, cellData, rowData) {
- var update_btn = '
{% trans "Update" %}'.replace('00000000-0000-0000-0000-000000000000', cellData);
+ var update_btn = "";
+ if (rowData.role === 'Admin' && ('{{ request.user.role }}' !== 'Admin')) {
+ update_btn = '
{% trans "Update" %}';
+ }
+ else{
+ update_btn = '
{% trans "Update" %}'.replace('00000000-0000-0000-0000-000000000000', cellData);
+ }
var del_btn = "";
- if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ request.user.username }}") {
+ if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ request.user.username }}" || (rowData.role === 'Admin' && ('{{ request.user.role }}' !== 'Admin'))) {
del_btn = '
{% trans "Delete" %}'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', rowData.name);
diff --git a/apps/users/views/login.py b/apps/users/views/login.py
index ca8bbf1ac..8f79b6f43 100644
--- a/apps/users/views/login.py
+++ b/apps/users/views/login.py
@@ -1,6 +1,7 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
+from django.core.cache import cache
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import RedirectView
@@ -84,7 +85,7 @@ class UserResetPasswordView(TemplateView):
def get(self, request, *args, **kwargs):
token = request.GET.get('token', '')
- user = User.validate_reset_token(token)
+ user = User.validate_reset_password_token(token)
if not user:
kwargs.update({'errors': _('Token invalid or expired')})
else:
@@ -100,12 +101,12 @@ class UserResetPasswordView(TemplateView):
if password != password_confirm:
return self.get(request, errors=_('Password not same'))
- user = User.validate_reset_token(token)
+ user = User.validate_reset_password_token(token)
+ if not user:
+ return self.get(request, errors=_('Token invalid or expired'))
if not user.can_update_password():
error = _('User auth from {}, go there change password'.format(user.source))
return self.get(request, errors=error)
- if not user:
- return self.get(request, errors=_('Token invalid or expired'))
is_ok = check_password_rules(password)
if not is_ok:
@@ -115,6 +116,7 @@ class UserResetPasswordView(TemplateView):
)
user.reset_password(password)
+ User.expired_reset_password_token(token)
return HttpResponseRedirect(reverse('users:reset-password-success'))
diff --git a/apps/users/views/user.py b/apps/users/views/user.py
index 2ffbd4688..270a9a5c9 100644
--- a/apps/users/views/user.py
+++ b/apps/users/views/user.py
@@ -107,6 +107,15 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
success_url = reverse_lazy('users:user-list')
success_message = update_success_msg
+ def _deny_permission(self):
+ obj = self.get_object()
+ return not self.request.user.is_superuser and obj.is_superuser
+
+ def get(self, request, *args, **kwargs):
+ if self._deny_permission():
+ return redirect(self.success_url)
+ return super().get(request, *args, **kwargs)
+
def get_context_data(self, **kwargs):
check_rules = get_password_check_rules()
context = {
diff --git a/utils/backup_db.sh b/utils/backup_db.sh
index 21ed938a8..7e2b824a7 100755
--- a/utils/backup_db.sh
+++ b/utils/backup_db.sh
@@ -7,4 +7,4 @@ if [ ! -d $backup_dir ];then
mkdir $backup_dir
fi
-mysqldump -uroot -h127.0.0.1 -p jumpserver > ${backup_dir}/jumpserver_$(date +'%Y-%m-%d_%H:%M:%S').sql
+mysqldump -uroot -h127.0.0.1 -p jumpserver -P3307 > ${backup_dir}/jumpserver_$(date +'%Y-%m-%d_%H:%M:%S').sql