mirror of https://github.com/jumpserver/jumpserver
commit
db290609a8
27
README.md
27
README.md
|
@ -8,11 +8,11 @@
|
|||
|
||||
----
|
||||
|
||||
Jumpserver是全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
|
||||
Jumpserver 是全球首款完全开源的堡垒机,使用 GNU GPL v2.0 开源协议,是符合 4A 的专业运维审计系统。
|
||||
|
||||
Jumpserver使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
|
||||
Jumpserver 使用 Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
|
||||
|
||||
Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发限制。
|
||||
Jumpserver 采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发限制。
|
||||
|
||||
改变世界,从一点点开始。
|
||||
|
||||
|
@ -20,29 +20,30 @@ Jumpserver采纳分布式架构,支持多机房跨区域部署,中心节点
|
|||
|
||||
### 功能
|
||||
|
||||
![Jumpserver功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver-14.png "Jumpserver功能")
|
||||
![Jumpserver 功能](https://jumpserver-release.oss-cn-hangzhou.aliyuncs.com/Jumpserver-14.png "Jumpserver 功能")
|
||||
|
||||
### 开始使用
|
||||
|
||||
快速开始文档 [Docker安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
|
||||
快速开始文档 [Docker 安装](http://docs.jumpserver.org/zh/docs/dockerinstall.html)
|
||||
|
||||
一步一步安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
|
||||
Step by Step 安装文档 [详细部署](http://docs.jumpserver.org/zh/docs/step_by_step.html)
|
||||
|
||||
也可以查看我们完整文档包括了使用和开发 [文档](http://docs.jumpserver.org)
|
||||
也可以查看我们完整文档 [文档](http://docs.jumpserver.org)
|
||||
|
||||
### Demo 和 截图
|
||||
### Demo、视频 和 截图
|
||||
|
||||
我们提供了DEMO和截图可以让你快速了解Jumpserver
|
||||
我们提供了 Demo 、演示视频和截图可以让你快速了解 Jumpserver
|
||||
|
||||
[DEMO](https://demo.jumpserver.org)
|
||||
[Demo](https://demo.jumpserver.org/auth/login/?next=/)
|
||||
[视频](https://fit2cloud2-offline-installer.oss-cn-beijing.aliyuncs.com/tools/Jumpserver%20%E4%BB%8B%E7%BB%8Dv1.4.mp4)
|
||||
[截图](http://docs.jumpserver.org/zh/docs/snapshot.html)
|
||||
|
||||
### SDK
|
||||
|
||||
我们还编写了一些SDK,供你其它系统快速和Jumpserver APi交互,
|
||||
我们还编写了一些SDK,供你其它系统快速和 Jumpserver API 交互
|
||||
|
||||
- [python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
|
||||
- [java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
|
||||
- [Python](https://github.com/jumpserver/jumpserver-python-sdk) Jumpserver其它组件使用这个SDK完成交互
|
||||
- [Java](https://github.com/KaiJunYan/jumpserver-java-sdk.git) 恺珺同学提供的Java版本的SDK
|
||||
|
||||
|
||||
### License & Copyright
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Jumpserver 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-03-26 14:56+0800\n"
|
||||
"POT-Creation-Date: 2019-03-28 12:41+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
||||
|
@ -165,7 +165,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
|
|||
#: perms/templates/perms/asset_permission_list.html:53
|
||||
#: perms/templates/perms/asset_permission_list.html:72
|
||||
#: perms/templates/perms/asset_permission_user.html:54 settings/models.py:29
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:35
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:38
|
||||
#: settings/templates/settings/command_storage_create.html:41
|
||||
#: settings/templates/settings/replay_storage_create.html:44
|
||||
#: settings/templates/settings/terminal_setting.html:80
|
||||
|
@ -203,11 +203,12 @@ msgstr "名称"
|
|||
#: assets/templates/assets/system_user_detail.html:62
|
||||
#: assets/templates/assets/system_user_list.html:30 audits/models.py:94
|
||||
#: audits/templates/audits/login_log_list.html:51 authentication/forms.py:11
|
||||
#: authentication/templates/authentication/login.html:64
|
||||
#: authentication/templates/authentication/new_login.html:90
|
||||
#: ops/models/adhoc.py:164 perms/templates/perms/asset_permission_list.html:74
|
||||
#: perms/templates/perms/asset_permission_user.html:55
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:34 users/forms.py:13
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:37 users/forms.py:13
|
||||
#: users/models/user.py:52 users/templates/users/_select_user_modal.html:14
|
||||
#: users/templates/users/login.html:64 users/templates/users/new_login.html:110
|
||||
#: users/templates/users/user_detail.html:67
|
||||
#: users/templates/users/user_list.html:24
|
||||
#: users/templates/users/user_profile.html:47
|
||||
|
@ -228,9 +229,10 @@ msgstr "密码或密钥密码"
|
|||
#: assets/forms/user.py:26 assets/models/base.py:28
|
||||
#: assets/serializers/asset_user.py:19
|
||||
#: assets/templates/assets/_asset_user_auth_modal.html:21
|
||||
#: authentication/forms.py:13 settings/forms.py:103 users/forms.py:15
|
||||
#: users/forms.py:27 users/templates/users/login.html:67
|
||||
#: users/templates/users/new_login.html:113
|
||||
#: authentication/forms.py:13
|
||||
#: authentication/templates/authentication/login.html:67
|
||||
#: authentication/templates/authentication/new_login.html:93
|
||||
#: settings/forms.py:103 users/forms.py:15 users/forms.py:27
|
||||
#: users/templates/users/reset_password.html:53
|
||||
#: users/templates/users/user_create.html:10
|
||||
#: users/templates/users/user_password_authentication.html:18
|
||||
|
@ -1023,7 +1025,7 @@ msgstr "其它"
|
|||
#: settings/templates/settings/basic_setting.html:61
|
||||
#: settings/templates/settings/command_storage_create.html:79
|
||||
#: settings/templates/settings/email_setting.html:62
|
||||
#: settings/templates/settings/ldap_setting.html:62
|
||||
#: settings/templates/settings/ldap_setting.html:61
|
||||
#: settings/templates/settings/replay_storage_create.html:151
|
||||
#: settings/templates/settings/security_setting.html:70
|
||||
#: settings/templates/settings/terminal_setting.html:68
|
||||
|
@ -1039,7 +1041,7 @@ msgstr "其它"
|
|||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:71
|
||||
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:33
|
||||
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:35
|
||||
#: xpack/plugins/interface/templates/interface/interface.html:88
|
||||
#: xpack/plugins/interface/templates/interface/interface.html:72
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
||||
|
@ -1059,7 +1061,7 @@ msgstr "重置"
|
|||
#: settings/templates/settings/basic_setting.html:62
|
||||
#: settings/templates/settings/command_storage_create.html:80
|
||||
#: settings/templates/settings/email_setting.html:63
|
||||
#: settings/templates/settings/ldap_setting.html:63
|
||||
#: settings/templates/settings/ldap_setting.html:64
|
||||
#: settings/templates/settings/replay_storage_create.html:152
|
||||
#: settings/templates/settings/security_setting.html:71
|
||||
#: settings/templates/settings/terminal_setting.html:70
|
||||
|
@ -1067,14 +1069,14 @@ msgstr "重置"
|
|||
#: terminal/templates/terminal/session_list.html:126
|
||||
#: terminal/templates/terminal/terminal_update.html:46
|
||||
#: users/templates/users/_user.html:51
|
||||
#: users/templates/users/forgot_password.html:49
|
||||
#: users/templates/users/forgot_password.html:42
|
||||
#: users/templates/users/user_bulk_update.html:24
|
||||
#: users/templates/users/user_list.html:45
|
||||
#: users/templates/users/user_password_update.html:72
|
||||
#: users/templates/users/user_profile_update.html:64
|
||||
#: users/templates/users/user_pubkey_update.html:77
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:72
|
||||
#: xpack/plugins/interface/templates/interface/interface.html:89
|
||||
#: xpack/plugins/interface/templates/interface/interface.html:73
|
||||
msgid "Submit"
|
||||
msgstr "提交"
|
||||
|
||||
|
@ -1085,7 +1087,8 @@ msgid "Asset detail"
|
|||
msgstr "资产详情"
|
||||
|
||||
#: assets/templates/assets/_user_asset_detail_modal.html:23
|
||||
#: templates/_modal.html:21
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:96
|
||||
#: templates/_modal.html:22
|
||||
msgid "Close"
|
||||
msgstr "关闭"
|
||||
|
||||
|
@ -1257,7 +1260,7 @@ msgstr "选择节点"
|
|||
#: assets/templates/assets/system_user_detail.html:182
|
||||
#: assets/templates/assets/system_user_list.html:143
|
||||
#: settings/templates/settings/terminal_setting.html:165
|
||||
#: templates/_modal.html:22 terminal/templates/terminal/session_detail.html:108
|
||||
#: templates/_modal.html:23 terminal/templates/terminal/session_detail.html:108
|
||||
#: users/templates/users/user_detail.html:388
|
||||
#: users/templates/users/user_detail.html:414
|
||||
#: users/templates/users/user_detail.html:437
|
||||
|
@ -1390,6 +1393,7 @@ msgid "Create asset"
|
|||
msgstr "创建资产"
|
||||
|
||||
#: assets/templates/assets/asset_list.html:73
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:97
|
||||
#: users/templates/users/user_list.html:7
|
||||
#: xpack/plugins/license/templates/license/license_detail.html:101
|
||||
msgid "Import"
|
||||
|
@ -1609,7 +1613,7 @@ msgstr "创建网关"
|
|||
#: assets/templates/assets/domain_gateway_list.html:99
|
||||
#: assets/templates/assets/domain_gateway_list.html:101
|
||||
#: settings/templates/settings/email_setting.html:61
|
||||
#: settings/templates/settings/ldap_setting.html:61
|
||||
#: settings/templates/settings/ldap_setting.html:62
|
||||
msgid "Test connection"
|
||||
msgstr "测试连接"
|
||||
|
||||
|
@ -2048,8 +2052,9 @@ msgstr "登录日志"
|
|||
msgid "Command execution log"
|
||||
msgstr "命令执行"
|
||||
|
||||
#: authentication/api/auth.py:46 users/templates/users/login.html:52
|
||||
#: users/templates/users/new_login.html:97
|
||||
#: authentication/api/auth.py:46
|
||||
#: authentication/templates/authentication/login.html:52
|
||||
#: authentication/templates/authentication/new_login.html:77
|
||||
msgid "Log in frequently and try again later"
|
||||
msgstr "登录频繁, 稍后重试"
|
||||
|
||||
|
@ -2128,6 +2133,122 @@ msgstr "MFA 验证码"
|
|||
msgid "Private Token"
|
||||
msgstr "ssh密钥"
|
||||
|
||||
#: authentication/templates/authentication/login.html:27
|
||||
#: authentication/templates/authentication/login_otp.html:27
|
||||
#: users/templates/users/reset_password.html:25
|
||||
#: xpack/plugins/interface/models.py:36
|
||||
msgid "Welcome to the Jumpserver open source fortress"
|
||||
msgstr "欢迎使用Jumpserver开源堡垒机"
|
||||
|
||||
#: authentication/templates/authentication/login.html:29
|
||||
#: authentication/templates/authentication/login_otp.html:29
|
||||
msgid ""
|
||||
"The world's first fully open source fortress, using the GNU GPL v2.0 open "
|
||||
"source protocol, is a professional operation and maintenance audit system in "
|
||||
"compliance with 4A."
|
||||
msgstr ""
|
||||
"全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计"
|
||||
"系统。"
|
||||
|
||||
#: authentication/templates/authentication/login.html:32
|
||||
#: authentication/templates/authentication/login_otp.html:32
|
||||
msgid ""
|
||||
"Developed using Python/Django, following the Web 2.0 specification and "
|
||||
"equipped with industry-leading Web Terminal solutions, with beautiful "
|
||||
"interactive interface and good user experience."
|
||||
msgstr ""
|
||||
"使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web "
|
||||
"Terminal 解决方案,交互界面美观、用户体验好。"
|
||||
|
||||
#: authentication/templates/authentication/login.html:35
|
||||
#: authentication/templates/authentication/login_otp.html:35
|
||||
msgid ""
|
||||
"Distributed architecture is adopted to support multi-machine room deployment "
|
||||
"across regions, central node provides API, and each machine room deploys "
|
||||
"login node, which can be extended horizontally and without concurrent access "
|
||||
"restrictions."
|
||||
msgstr ""
|
||||
"采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,"
|
||||
"可横向扩展、无并发访问限制。"
|
||||
|
||||
#: authentication/templates/authentication/login.html:38
|
||||
#: authentication/templates/authentication/login_otp.html:38
|
||||
msgid "Changes the world, starting with a little bit."
|
||||
msgstr "改变世界,从一点点开始。"
|
||||
|
||||
#: authentication/templates/authentication/login.html:46
|
||||
#: authentication/templates/authentication/login.html:72
|
||||
#: authentication/templates/authentication/new_login.html:99
|
||||
#: templates/_header_bar.html:101
|
||||
msgid "Login"
|
||||
msgstr "登录"
|
||||
|
||||
#: authentication/templates/authentication/login.html:54
|
||||
#: authentication/templates/authentication/new_login.html:79
|
||||
msgid "The user password has expired"
|
||||
msgstr "用户密码已过期"
|
||||
|
||||
#: authentication/templates/authentication/login.html:57
|
||||
#: authentication/templates/authentication/new_login.html:82
|
||||
msgid "Captcha invalid"
|
||||
msgstr "验证码错误"
|
||||
|
||||
#: authentication/templates/authentication/login.html:83
|
||||
#: authentication/templates/authentication/new_login.html:103
|
||||
#: users/templates/users/forgot_password.html:10
|
||||
#: users/templates/users/forgot_password.html:25
|
||||
msgid "Forgot password"
|
||||
msgstr "忘记密码"
|
||||
|
||||
#: authentication/templates/authentication/login.html:89
|
||||
msgid "More login options"
|
||||
msgstr "更多登录方式"
|
||||
|
||||
#: authentication/templates/authentication/login.html:93
|
||||
msgid "Keycloak"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:46
|
||||
#: users/templates/users/user_detail.html:91
|
||||
#: users/templates/users/user_profile.html:85
|
||||
msgid "MFA certification"
|
||||
msgstr "MFA认证"
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:51
|
||||
#: users/templates/users/user_otp_authentication.html:11
|
||||
msgid ""
|
||||
"The account protection has been opened, please complete the following "
|
||||
"operations according to the prompts"
|
||||
msgstr "账号保护已开启,请根据提示完成以下操作"
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:55
|
||||
#: users/templates/users/user_otp_authentication.html:13
|
||||
msgid "Open Authenticator and enter the 6-bit dynamic code"
|
||||
msgstr "请打开手机Google Authenticator应用,输入6位动态码"
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:65
|
||||
#: users/templates/users/user_otp_authentication.html:23
|
||||
#: users/templates/users/user_otp_enable_bind.html:26
|
||||
msgid "Six figures"
|
||||
msgstr "6位数字"
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:67
|
||||
#: users/templates/users/first_login.html:105
|
||||
#: users/templates/users/user_otp_authentication.html:26
|
||||
#: users/templates/users/user_otp_enable_bind.html:29
|
||||
#: users/templates/users/user_otp_enable_install_app.html:26
|
||||
#: users/templates/users/user_password_authentication.html:21
|
||||
msgid "Next"
|
||||
msgstr "下一步"
|
||||
|
||||
#: authentication/templates/authentication/login_otp.html:70
|
||||
msgid "Can't provide security? Please contact the administrator!"
|
||||
msgstr "如果不能提供MFA验证码,请联系管理员!"
|
||||
|
||||
#: authentication/templates/authentication/new_login.html:67
|
||||
msgid "Welcome back, please enter username and password to login"
|
||||
msgstr "欢迎回来,请输入用户名和密码登录"
|
||||
|
||||
#: authentication/views/login.py:75
|
||||
msgid "Please enable cookies and try again."
|
||||
msgstr "设置你的浏览器支持cookie"
|
||||
|
@ -2715,38 +2836,33 @@ msgstr "资产授权用户列表"
|
|||
msgid "Asset permission asset list"
|
||||
msgstr "资产授权资产列表"
|
||||
|
||||
#: settings/api.py:23
|
||||
#: settings/api.py:26
|
||||
msgid "Test mail sent to {}, please check"
|
||||
msgstr "邮件已经发送{}, 请检查"
|
||||
|
||||
#: settings/api.py:47
|
||||
#: settings/api.py:50
|
||||
msgid "Test ldap success"
|
||||
msgstr "连接LDAP成功"
|
||||
|
||||
#: settings/api.py:77 settings/utils.py:23
|
||||
msgid "Search no entry matched in ou {}"
|
||||
msgstr "在ou:{}中没有匹配条目"
|
||||
|
||||
#: settings/api.py:86
|
||||
#: settings/api.py:87
|
||||
msgid "Match {} s users"
|
||||
msgstr "匹配 {} 个用户"
|
||||
|
||||
#: settings/api.py:109
|
||||
msgid ""
|
||||
"User is not currently selected, please check the user you want to import"
|
||||
msgstr "当前无勾选用户,请勾选你想要导入的用户"
|
||||
#: settings/api.py:120
|
||||
msgid "succeed: {} failed: {} total: {}"
|
||||
msgstr "成功:{} 失败:{} 总数:{}"
|
||||
|
||||
#: settings/api.py:139 settings/api.py:175
|
||||
#: settings/api.py:142 settings/api.py:178
|
||||
msgid ""
|
||||
"Error: Account invalid (Please make sure the information such as Access key "
|
||||
"or Secret key is correct)"
|
||||
msgstr "错误:账户无效 (请确保 Access key 或 Secret key 等信息正确)"
|
||||
|
||||
#: settings/api.py:145 settings/api.py:181
|
||||
#: settings/api.py:148 settings/api.py:184
|
||||
msgid "Create succeed"
|
||||
msgstr "创建成功"
|
||||
|
||||
#: settings/api.py:163 settings/api.py:201
|
||||
#: settings/api.py:166 settings/api.py:204
|
||||
#: settings/templates/settings/terminal_setting.html:151
|
||||
msgid "Delete succeed"
|
||||
msgstr "删除成功"
|
||||
|
@ -2989,24 +3105,28 @@ msgid ""
|
|||
"characters"
|
||||
msgstr "开启后,用户密码修改、重置必须包含特殊字符"
|
||||
|
||||
#: settings/models.py:126 users/templates/users/reset_password.html:68
|
||||
#: settings/models.py:128 users/templates/users/reset_password.html:68
|
||||
#: users/templates/users/user_profile.html:20
|
||||
msgid "Setting"
|
||||
msgstr "设置"
|
||||
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:7
|
||||
msgid "Ldap users"
|
||||
msgstr "Ldap 用户列表"
|
||||
msgid "LDAP user list"
|
||||
msgstr "LDAP 用户列表"
|
||||
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:36
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:9
|
||||
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/templates/users/user_profile.html:59
|
||||
msgid "Email"
|
||||
msgstr "邮件"
|
||||
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:37
|
||||
msgid "Is imported"
|
||||
msgstr "是否已经导入"
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:40
|
||||
msgid "Existing"
|
||||
msgstr "已存在"
|
||||
|
||||
#: settings/templates/settings/basic_setting.html:15
|
||||
#: settings/templates/settings/email_setting.html:15
|
||||
|
@ -3063,8 +3183,13 @@ msgid "Doc type"
|
|||
msgstr "文档类型"
|
||||
|
||||
#: settings/templates/settings/ldap_setting.html:65
|
||||
msgid "Sync User"
|
||||
msgstr "同步用户"
|
||||
msgid "Bulk import"
|
||||
msgstr "一键导入"
|
||||
|
||||
#: settings/templates/settings/ldap_setting.html:116
|
||||
msgid ""
|
||||
"User is not currently selected, please check the user you want to import"
|
||||
msgstr "当前无勾选用户,请勾选你想要导入的用户"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:66
|
||||
msgid "Bucket"
|
||||
|
@ -3094,56 +3219,60 @@ msgstr "账户密钥"
|
|||
msgid "Endpoint"
|
||||
msgstr "端点"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:113
|
||||
#: settings/templates/settings/replay_storage_create.html:114
|
||||
#, python-brace-format
|
||||
msgid "OSS: http://{REGION_NAME}.aliyuncs.com"
|
||||
msgstr "OSS: http://{REGION_NAME}.aliyuncs.com"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:115
|
||||
#: settings/templates/settings/replay_storage_create.html:116
|
||||
msgid "Example: http://oss-cn-hangzhou.aliyuncs.com"
|
||||
msgstr "如: http://oss-cn-hangzhou.aliyuncs.com"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:117
|
||||
#: settings/templates/settings/replay_storage_create.html:118
|
||||
#, python-brace-format
|
||||
msgid "S3: http://s3.{REGION_NAME}.amazonaws.com"
|
||||
msgstr "S3: http://s3.{REGION_NAME}.amazonaws.com"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:118
|
||||
#: settings/templates/settings/replay_storage_create.html:119
|
||||
#, python-brace-format
|
||||
msgid "S3(China): http://s3.{REGION_NAME}.amazonaws.com.cn"
|
||||
msgstr "S3(中国): http://s3.{REGION_NAME}.amazonaws.com.cn"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:119
|
||||
#: settings/templates/settings/replay_storage_create.html:120
|
||||
msgid "Example: http://s3.cn-north-1.amazonaws.com.cn"
|
||||
msgstr "如: http://s3.cn-north-1.amazonaws.com.cn"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:125
|
||||
#: settings/templates/settings/replay_storage_create.html:126
|
||||
msgid "Endpoint suffix"
|
||||
msgstr "端点后缀"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:135
|
||||
#: settings/templates/settings/replay_storage_create.html:136
|
||||
#: xpack/plugins/cloud/models.py:186
|
||||
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:83
|
||||
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_instance.html:64
|
||||
msgid "Region"
|
||||
msgstr "地域"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:140
|
||||
#: settings/templates/settings/replay_storage_create.html:141
|
||||
msgid "Beijing: cn-north-1"
|
||||
msgstr "北京: cn-north-1"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:141
|
||||
#: settings/templates/settings/replay_storage_create.html:142
|
||||
msgid "Ningxia: cn-northwest-1"
|
||||
msgstr "宁夏: cn-northwest-1"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:142
|
||||
#: settings/templates/settings/replay_storage_create.html:143
|
||||
msgid "More"
|
||||
msgstr "更多"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:246
|
||||
#: settings/templates/settings/replay_storage_create.html:247
|
||||
msgid "Submitting"
|
||||
msgstr "提交中"
|
||||
|
||||
#: settings/templates/settings/replay_storage_create.html:256
|
||||
msgid "Endpoint need contain protocol, ex: http"
|
||||
msgstr "端点需要包含协议,如 http"
|
||||
|
||||
#: settings/templates/settings/security_setting.html:46
|
||||
msgid "Password check rule"
|
||||
msgstr "密码校验规则"
|
||||
|
@ -3166,44 +3295,17 @@ msgstr "删除失败"
|
|||
msgid "Are you sure about deleting it?"
|
||||
msgstr "您确定删除吗?"
|
||||
|
||||
#: settings/utils.py:30
|
||||
msgid "Have user but attr mapping error"
|
||||
msgstr "有用户但attr映射错误"
|
||||
|
||||
#: settings/utils.py:60
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:86
|
||||
msgid "No"
|
||||
msgstr "否"
|
||||
|
||||
#: settings/utils.py:69
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:84
|
||||
msgid "Yes"
|
||||
msgstr "是"
|
||||
msgid "User does not exist"
|
||||
msgstr "用户不存在"
|
||||
|
||||
#: settings/utils.py:137
|
||||
msgid ""
|
||||
"Import {} users successfully; import {} users failed, the database already "
|
||||
"exists with the same name"
|
||||
msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户"
|
||||
#: settings/utils.py:72
|
||||
msgid "The user source is not LDAP"
|
||||
msgstr "用户来源不是LDAP"
|
||||
|
||||
#: settings/utils.py:142
|
||||
msgid ""
|
||||
"Import {} users successfully; import {} users failed, the database already "
|
||||
"exists with the same name; import {}users failed, Because’TypeError' object "
|
||||
"has no attribute 'keys'"
|
||||
msgstr ""
|
||||
"导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户; 导入 {} "
|
||||
"这些用户失败,因为对象没有属性'keys'"
|
||||
|
||||
#: settings/utils.py:148
|
||||
msgid "Import {} users successfully"
|
||||
msgstr "导入 {} 个用户成功"
|
||||
|
||||
#: settings/utils.py:151
|
||||
msgid ""
|
||||
"Import {} users successfully;import {} users failed, Because’TypeError' "
|
||||
"object has no attribute 'keys'"
|
||||
msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,因为对象没有属性'keys'"
|
||||
#: settings/utils.py:146
|
||||
msgid "Search no entry matched in ou {}"
|
||||
msgstr "在ou:{}中没有匹配条目"
|
||||
|
||||
#: settings/views.py:18 settings/views.py:44 settings/views.py:70
|
||||
#: settings/views.py:99 settings/views.py:126 settings/views.py:138
|
||||
|
@ -3263,11 +3365,6 @@ msgstr "用户页面"
|
|||
msgid "Logout"
|
||||
msgstr "注销登录"
|
||||
|
||||
#: templates/_header_bar.html:101 users/templates/users/login.html:46
|
||||
#: users/templates/users/login.html:72 users/templates/users/new_login.html:119
|
||||
msgid "Login"
|
||||
msgstr "登录"
|
||||
|
||||
#: templates/_header_bar.html:114 templates/_nav.html:4
|
||||
msgid "Dashboard"
|
||||
msgstr "仪表盘"
|
||||
|
@ -3412,15 +3509,7 @@ msgstr "语言播放验证码"
|
|||
msgid "Captcha"
|
||||
msgstr "验证码"
|
||||
|
||||
#: templates/flash_message_standalone.html:35
|
||||
#: users/templates/users/login.html:27 users/templates/users/login_otp.html:27
|
||||
#: users/templates/users/new_login.html:82
|
||||
#: users/templates/users/reset_password.html:25
|
||||
#: xpack/plugins/interface/models.py:36
|
||||
msgid "Welcome to the Jumpserver open source fortress"
|
||||
msgstr "欢迎使用Jumpserver开源堡垒机"
|
||||
|
||||
#: templates/flash_message_standalone.html:56
|
||||
#: templates/flash_message_standalone.html:47
|
||||
msgid "Return"
|
||||
msgstr "返回"
|
||||
|
||||
|
@ -3998,15 +4087,6 @@ msgstr "请选择同意条款和条件"
|
|||
msgid "Previous"
|
||||
msgstr "上一步"
|
||||
|
||||
#: users/templates/users/first_login.html:105
|
||||
#: users/templates/users/login_otp.html:67
|
||||
#: users/templates/users/user_otp_authentication.html:26
|
||||
#: users/templates/users/user_otp_enable_bind.html:29
|
||||
#: users/templates/users/user_otp_enable_install_app.html:26
|
||||
#: users/templates/users/user_password_authentication.html:21
|
||||
msgid "Next"
|
||||
msgstr "下一步"
|
||||
|
||||
#: users/templates/users/first_login_done.html:31
|
||||
msgid "Welcome to use jumpserver, visit "
|
||||
msgstr "欢迎使用Jumpserver开源跳板机系统"
|
||||
|
@ -4019,96 +4099,10 @@ msgstr "向导"
|
|||
msgid " for more information"
|
||||
msgstr "获取更多信息"
|
||||
|
||||
#: users/templates/users/forgot_password.html:11
|
||||
#: users/templates/users/forgot_password.html:31
|
||||
#: users/templates/users/login.html:83 users/templates/users/new_login.html:123
|
||||
msgid "Forgot password"
|
||||
msgstr "忘记密码"
|
||||
|
||||
#: users/templates/users/forgot_password.html:38
|
||||
msgid "Input your email, that will send a mail to your"
|
||||
msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中"
|
||||
|
||||
#: users/templates/users/login.html:29 users/templates/users/login_otp.html:29
|
||||
msgid ""
|
||||
"The world's first fully open source fortress, using the GNU GPL v2.0 open "
|
||||
"source protocol, is a professional operation and maintenance audit system in "
|
||||
"compliance with 4A."
|
||||
msgstr ""
|
||||
"全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计"
|
||||
"系统。"
|
||||
|
||||
#: users/templates/users/login.html:32 users/templates/users/login_otp.html:32
|
||||
msgid ""
|
||||
"Developed using Python/Django, following the Web 2.0 specification and "
|
||||
"equipped with industry-leading Web Terminal solutions, with beautiful "
|
||||
"interactive interface and good user experience."
|
||||
msgstr ""
|
||||
"使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web "
|
||||
"Terminal 解决方案,交互界面美观、用户体验好。"
|
||||
|
||||
#: users/templates/users/login.html:35 users/templates/users/login_otp.html:35
|
||||
msgid ""
|
||||
"Distributed architecture is adopted to support multi-machine room deployment "
|
||||
"across regions, central node provides API, and each machine room deploys "
|
||||
"login node, which can be extended horizontally and without concurrent access "
|
||||
"restrictions."
|
||||
msgstr ""
|
||||
"采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,"
|
||||
"可横向扩展、无并发访问限制。"
|
||||
|
||||
#: users/templates/users/login.html:38 users/templates/users/login_otp.html:38
|
||||
msgid "Changes the world, starting with a little bit."
|
||||
msgstr "改变世界,从一点点开始。"
|
||||
|
||||
#: users/templates/users/login.html:54 users/templates/users/new_login.html:99
|
||||
msgid "The user password has expired"
|
||||
msgstr "用户密码已过期"
|
||||
|
||||
#: users/templates/users/login.html:57 users/templates/users/new_login.html:102
|
||||
msgid "Captcha invalid"
|
||||
msgstr "验证码错误"
|
||||
|
||||
#: users/templates/users/login.html:89
|
||||
msgid "More login options"
|
||||
msgstr "更多登录方式"
|
||||
|
||||
#: users/templates/users/login.html:93
|
||||
msgid "Keycloak"
|
||||
msgstr ""
|
||||
|
||||
#: users/templates/users/login_otp.html:46
|
||||
#: users/templates/users/user_detail.html:91
|
||||
#: users/templates/users/user_profile.html:85
|
||||
msgid "MFA certification"
|
||||
msgstr "MFA认证"
|
||||
|
||||
#: users/templates/users/login_otp.html:51
|
||||
#: users/templates/users/user_otp_authentication.html:11
|
||||
msgid ""
|
||||
"The account protection has been opened, please complete the following "
|
||||
"operations according to the prompts"
|
||||
msgstr "账号保护已开启,请根据提示完成以下操作"
|
||||
|
||||
#: users/templates/users/login_otp.html:55
|
||||
#: users/templates/users/user_otp_authentication.html:13
|
||||
msgid "Open Authenticator and enter the 6-bit dynamic code"
|
||||
msgstr "请打开手机Google Authenticator应用,输入6位动态码"
|
||||
|
||||
#: users/templates/users/login_otp.html:65
|
||||
#: users/templates/users/user_otp_authentication.html:23
|
||||
#: users/templates/users/user_otp_enable_bind.html:26
|
||||
msgid "Six figures"
|
||||
msgstr "6位数字"
|
||||
|
||||
#: users/templates/users/login_otp.html:70
|
||||
msgid "Can't provide security? Please contact the administrator!"
|
||||
msgstr "如果不能提供MFA验证码,请联系管理员!"
|
||||
|
||||
#: users/templates/users/new_login.html:87
|
||||
msgid "Welcome back, please enter username and password to login"
|
||||
msgstr "欢迎回来,请输入用户名和密码登录"
|
||||
|
||||
#: users/templates/users/reset_password.html:28
|
||||
msgid ""
|
||||
"Jumpserver is an open source desktop system developed using Python and "
|
||||
|
@ -4905,6 +4899,14 @@ msgstr "更改资产上的用户密码时,将会使用与该资产关联的管
|
|||
msgid "Length"
|
||||
msgstr "长度"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:84
|
||||
msgid "Yes"
|
||||
msgstr "是"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:86
|
||||
msgid "No"
|
||||
msgstr "否"
|
||||
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:134
|
||||
msgid "Run plan manually"
|
||||
msgstr "手动执行计划"
|
||||
|
@ -5354,6 +5356,33 @@ msgstr "创建组织"
|
|||
msgid "Update org"
|
||||
msgstr "更新组织"
|
||||
|
||||
#~ msgid "Sync User"
|
||||
#~ msgstr "同步用户"
|
||||
|
||||
#~ msgid "Have user but attr mapping error"
|
||||
#~ msgstr "有用户但attr映射错误"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Import {} users successfully; import {} users failed, the database "
|
||||
#~ "already exists with the same name"
|
||||
#~ msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Import {} users successfully; import {} users failed, the database "
|
||||
#~ "already exists with the same name; import {}users failed, "
|
||||
#~ "Because’TypeError' object has no attribute 'keys'"
|
||||
#~ msgstr ""
|
||||
#~ "导入 {} 个用户成功; 导入 {} 这些用户失败,数据库已经存在同名的用户; 导入 "
|
||||
#~ "{} 这些用户失败,因为对象没有属性'keys'"
|
||||
|
||||
#~ msgid "Import {} users successfully"
|
||||
#~ msgstr "导入 {} 个用户成功"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Import {} users successfully;import {} users failed, Because’TypeError' "
|
||||
#~ "object has no attribute 'keys'"
|
||||
#~ msgstr "导入 {} 个用户成功; 导入 {} 这些用户失败,因为对象没有属性'keys'"
|
||||
|
||||
#~ msgid "Monitor"
|
||||
#~ msgstr "监控"
|
||||
|
||||
|
|
|
@ -5,18 +5,21 @@ import os
|
|||
import json
|
||||
import jms_storage
|
||||
|
||||
from ldap3 import Server, Connection
|
||||
from rest_framework.views import Response, APIView
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import Setting
|
||||
from .utils import get_ldap_users_list, save_user
|
||||
from .utils import LDAPUtil
|
||||
from common.permissions import IsOrgAdmin, IsSuperUser
|
||||
from common.utils import get_logger
|
||||
from .serializers import MailTestSerializer, LDAPTestSerializer
|
||||
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class MailTestingAPI(APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = MailTestSerializer
|
||||
|
@ -46,78 +49,78 @@ class LDAPTestingAPI(APIView):
|
|||
serializer_class = LDAPTestSerializer
|
||||
success_message = _("Test ldap success")
|
||||
|
||||
@staticmethod
|
||||
def get_ldap_util(serializer):
|
||||
host = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
|
||||
bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"]
|
||||
password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"]
|
||||
use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False)
|
||||
search_ougroup = serializer.validated_data["AUTH_LDAP_SEARCH_OU"]
|
||||
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
|
||||
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
|
||||
try:
|
||||
attr_map = json.loads(attr_map)
|
||||
except json.JSONDecodeError:
|
||||
return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401)
|
||||
|
||||
util = LDAPUtil(
|
||||
use_settings_config=False, server_uri=host, bind_dn=bind_dn,
|
||||
password=password, use_ssl=use_ssl,
|
||||
search_ougroup=search_ougroup, search_filter=search_filter,
|
||||
attr_map=attr_map
|
||||
)
|
||||
return util
|
||||
|
||||
def post(self, request):
|
||||
serializer = self.serializer_class(data=request.data)
|
||||
if serializer.is_valid():
|
||||
host = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
|
||||
bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"]
|
||||
password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"]
|
||||
use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False)
|
||||
search_ougroup = serializer.validated_data["AUTH_LDAP_SEARCH_OU"]
|
||||
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
|
||||
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
|
||||
|
||||
try:
|
||||
attr_map = json.loads(attr_map)
|
||||
except json.JSONDecodeError:
|
||||
return Response({"error": "AUTH_LDAP_USER_ATTR_MAP not valid"}, status=401)
|
||||
|
||||
server = Server(host, use_ssl=use_ssl)
|
||||
conn = Connection(server, bind_dn, password)
|
||||
try:
|
||||
conn.bind()
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=401)
|
||||
|
||||
users = []
|
||||
for search_ou in str(search_ougroup).split("|"):
|
||||
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
|
||||
attributes=list(attr_map.values()))
|
||||
if not ok:
|
||||
return Response({"error": _("Search no entry matched in ou {}").format(search_ou)}, status=401)
|
||||
|
||||
for entry in conn.entries:
|
||||
user = {}
|
||||
for attr, mapping in attr_map.items():
|
||||
if hasattr(entry, mapping):
|
||||
user[attr] = getattr(entry, mapping)
|
||||
users.append(user)
|
||||
if len(users) > 0:
|
||||
return Response({"msg": _("Match {} s users").format(len(users))})
|
||||
else:
|
||||
return Response({"error": "Have user but attr mapping error"}, status=401)
|
||||
else:
|
||||
if not serializer.is_valid():
|
||||
return Response({"error": str(serializer.errors)}, status=401)
|
||||
|
||||
util = self.get_ldap_util(serializer)
|
||||
|
||||
class LDAPSyncAPI(APIView):
|
||||
try:
|
||||
users = util.get_search_user_items()
|
||||
except Exception as e:
|
||||
return Response({"error": str(e)}, status=401)
|
||||
|
||||
if len(users) > 0:
|
||||
return Response({"msg": _("Match {} s users").format(len(users))})
|
||||
else:
|
||||
return Response({"error": "Have user but attr mapping error"}, status=401)
|
||||
|
||||
|
||||
class LDAPUserListApi(APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get(self, request):
|
||||
ldap_users_list = get_ldap_users_list()
|
||||
if not isinstance(ldap_users_list, list):
|
||||
return Response(ldap_users_list, status=401)
|
||||
return Response(ldap_users_list)
|
||||
util = LDAPUtil()
|
||||
try:
|
||||
users = util.get_search_user_items()
|
||||
except Exception as e:
|
||||
users = []
|
||||
logger.error(e, exc_info=True)
|
||||
else:
|
||||
users = sorted(users, key=lambda u: (u['existing'], u['username']))
|
||||
return Response(users)
|
||||
|
||||
|
||||
class LDAPConfirmSyncAPI(APIView):
|
||||
class LDAPUserSyncAPI(APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def post(self, request):
|
||||
user_names = request.data.get('user_names', '')
|
||||
if not user_names:
|
||||
error = _('User is not currently selected, please check the user '
|
||||
'you want to import')
|
||||
return Response({'error': error}, status=401)
|
||||
|
||||
ldap_users_list = get_ldap_users_list(user_names=user_names)
|
||||
if not isinstance(ldap_users_list, list):
|
||||
return Response(ldap_users_list, status=401)
|
||||
|
||||
save_result = save_user(ldap_users_list)
|
||||
if 'error' in save_result.keys():
|
||||
return Response(save_result, status=401)
|
||||
return Response(save_result)
|
||||
util = LDAPUtil()
|
||||
try:
|
||||
result = util.sync_users(username_set=user_names)
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return Response({'error': str(e)}, status=401)
|
||||
else:
|
||||
msg = _("succeed: {} failed: {} total: {}").format(
|
||||
result['succeed'], result['failed'], result['total']
|
||||
)
|
||||
return Response({'msg': msg})
|
||||
|
||||
|
||||
class ReplayStorageCreateAPI(APIView):
|
||||
|
|
|
@ -79,6 +79,8 @@ class Setting(models.Model):
|
|||
obj.cleaned_value = data
|
||||
else:
|
||||
value = obj.cleaned_value
|
||||
if value is None:
|
||||
value = {}
|
||||
value.update(data)
|
||||
obj.cleaned_value = value
|
||||
obj.save()
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
|
||||
{% block modal_class %}modal-lg{% endblock %}
|
||||
{% block modal_id %}ldap_list_users_modal{% endblock %}
|
||||
{% block modal_title%}{% trans "Ldap users" %}{% endblock %}
|
||||
{% block modal_title%}{% trans "LDAP user list" %}{% endblock %}
|
||||
|
||||
{% block modal_help_message%} <div class="alert alert-info help-message" style="width: 838px; margin-left: 30px">{% trans 'Please submit the LDAP configuration before import' %}</div>{% endblock %}
|
||||
|
||||
{% block modal_body %}
|
||||
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
|
||||
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
|
||||
|
@ -34,7 +37,7 @@
|
|||
<th class="text-center">{% trans 'Username' %}</th>
|
||||
<th class="text-center">{% trans 'Name' %}</th>
|
||||
<th class="text-center">{% trans 'Email' %}</th>
|
||||
<th class="text-center">{% trans 'Is imported' %}</th>
|
||||
<th class="text-center">{% trans 'Existing' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -47,16 +50,25 @@
|
|||
|
||||
<script>
|
||||
var ldap_users_table = 0;
|
||||
function initLdapTable() {
|
||||
function initLdapUsersTable() {
|
||||
if(ldap_users_table){
|
||||
return
|
||||
}
|
||||
var options = {
|
||||
ele: $('#ldap_list_users_table'),
|
||||
ajax_url: '{% url "api-settings:ldap-sync" %}',
|
||||
ajax_url: '{% url "api-settings:ldap-user-list" %}',
|
||||
columnDefs: [
|
||||
{targets: 4, createdCell: function (td, cellData, rowData) {
|
||||
if(cellData){
|
||||
$(td).html('<i class="fa fa-check text-navy"></i>')
|
||||
}else{
|
||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
||||
}
|
||||
}}
|
||||
],
|
||||
columns: [
|
||||
{data: "username" },{data: "username" }, {data: "name" },
|
||||
{data:"email"}, {data:'is_imported'}
|
||||
{data:"email"}, {data:'existing'}
|
||||
],
|
||||
pageLength: 10
|
||||
};
|
||||
|
@ -68,8 +80,7 @@ function initLdapTable() {
|
|||
|
||||
$(document).ready(function(){
|
||||
}).on('show.bs.modal', function () {
|
||||
initLdapTable();
|
||||
|
||||
initLdapUsersTable();
|
||||
})
|
||||
.on('click','.close_btn1',function () {
|
||||
window.location.reload()
|
||||
|
@ -82,9 +93,9 @@ $(document).ready(function(){
|
|||
{% endblock %}
|
||||
|
||||
{% block modal_button %}
|
||||
{{ block.super }}
|
||||
<button data-dismiss="modal" class="btn btn-white close_btn2" type="button">{% trans "Close" %}</button>
|
||||
<button class="btn btn-primary" type="button" id="{% block modal_confirm_id %}btn_ldap_modal_confirm{% endblock %}">{% trans 'Import' %}</button>
|
||||
{% endblock %}
|
||||
{% block modal_confirm_id %}btn_ldap_modal_confirm{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -58,11 +58,11 @@
|
|||
<div class="hr-line-dashed"></div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4 col-sm-offset-2">
|
||||
<button class="btn btn-default btn-test" type="button"> {% trans 'Test connection' %}</button>
|
||||
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
|
||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||
<button class="btn btn-default btn-test" type="button"> {% trans 'Test connection' %}</button>
|
||||
{# <button class="btn btn-primary sync_button " data-toggle="modal" data-target="#sync_users_modal" type="button">{% trans 'Synchronization' %}</button>#}
|
||||
<button class="btn btn-primary sync_button " data-toggle="modal" data-target="#ldap_list_users_modal" type="button">{% trans 'Sync User' %}</button>
|
||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
|
||||
<button class="btn btn-default sync_button " data-toggle="modal" data-target="#ldap_list_users_modal" type="button">{% trans 'Bulk import' %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -108,11 +108,17 @@ $(document).ready(function () {
|
|||
})
|
||||
.on("click","#btn_ldap_modal_confirm",function () {
|
||||
var user_names=[];
|
||||
var cheked = $("tbody input[type='checkbox']:checked").each(function () {
|
||||
$("tbody input[type='checkbox']:checked").each(function () {
|
||||
user_names.push($(this).attr('id'));
|
||||
|
||||
});
|
||||
var the_url = "{% url "api-settings:ldap-comfirm-sync" %}";
|
||||
|
||||
if (user_names.length === 0){
|
||||
var msg = "{% trans 'User is not currently selected, please check the user you want to import'%}"
|
||||
toastr.error(msg);
|
||||
return
|
||||
}
|
||||
|
||||
var the_url = "{% url "api-settings:ldap-user-sync" %}";
|
||||
|
||||
function error(message) {
|
||||
toastr.error(message)
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
<label class="col-md-2 control-label" for="id_endpoint">{% trans "Endpoint" %}</label>
|
||||
<div class="col-md-9">
|
||||
<input id="id_endpoint" class="form-control" type="text" name="ENDPOINT" value="" placeholder="Endpoint">
|
||||
<div id="endpoint_error" style="color: red;"></div>
|
||||
<div class="help-block">
|
||||
<span class="oss">
|
||||
{% trans 'OSS: http://{REGION_NAME}.aliyuncs.com' %}
|
||||
|
@ -251,6 +252,13 @@ $(document).ready(function() {
|
|||
var name = $(id_field).attr('name');
|
||||
data[name] = $(id_field).val();
|
||||
});
|
||||
if (data['ENDPOINT'] !== '' && data['ENDPOINT'].indexOf('http') === -1) {
|
||||
var msg = "{% trans 'Endpoint need contain protocol, ex: http' %}";
|
||||
$("#endpoint_error").html(msg);
|
||||
submitBtn.removeClass('disabled');
|
||||
submitBtn.html(origin_text);
|
||||
return
|
||||
}
|
||||
var url = "{% url 'api-settings:replay-storage-create' %}";
|
||||
var success = function(data, textStatus) {
|
||||
location = "{% url 'settings:terminal-setting' %}";
|
||||
|
|
|
@ -9,8 +9,8 @@ app_name = 'common'
|
|||
urlpatterns = [
|
||||
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
|
||||
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
|
||||
path('ldap/sync/', api.LDAPSyncAPI.as_view(), name='ldap-sync'),
|
||||
path('ldap/comfirm/sync/', api.LDAPConfirmSyncAPI.as_view(), name='ldap-comfirm-sync'),
|
||||
path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'),
|
||||
path('ldap/users/sync/', api.LDAPUserSyncAPI.as_view(), name='ldap-user-sync'),
|
||||
path('terminal/replay-storage/create/', api.ReplayStorageCreateAPI.as_view(), name='replay-storage-create'),
|
||||
path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'),
|
||||
path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'),
|
||||
|
|
|
@ -4,151 +4,159 @@
|
|||
from ldap3 import Server, Connection
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import settings
|
||||
from users.models import User
|
||||
from common.utils import get_logger
|
||||
from .models import settings
|
||||
|
||||
|
||||
def ldap_conn(host, use_ssl, bind_dn, password):
|
||||
server = Server(host, use_ssl=use_ssl)
|
||||
conn = Connection(server, bind_dn, password)
|
||||
return conn
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
def ldap_search(conn, search_ougroup, search_filter, attr_map, user_names=None):
|
||||
users_list = []
|
||||
for search_ou in str(search_ougroup).split("|"):
|
||||
ok = conn.search(search_ou, search_filter % ({"user": "*"}),
|
||||
attributes=list(attr_map.values()))
|
||||
if not ok:
|
||||
error = _("Search no entry matched in ou {}").format(search_ou)
|
||||
return {"error": error}
|
||||
|
||||
ldap_map_users(conn, attr_map, users_list, user_names)
|
||||
|
||||
if len(users_list) > 0:
|
||||
return users_list
|
||||
return {"error": _("Have user but attr mapping error")}
|
||||
class LDAPOUGroupException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def get_ldap_users_list(user_names=None):
|
||||
ldap_setting = get_ldap_setting()
|
||||
conn = ldap_conn(ldap_setting['host'], ldap_setting['use_ssl'],
|
||||
ldap_setting['bind_dn'], ldap_setting['password'])
|
||||
try:
|
||||
conn.bind()
|
||||
except Exception as e:
|
||||
return {"error": str(e)}
|
||||
class LDAPUtil:
|
||||
|
||||
result_search = ldap_search(conn, ldap_setting['search_ougroup'],
|
||||
ldap_setting['search_filter'],
|
||||
ldap_setting['attr_map'], user_names=user_names)
|
||||
return result_search
|
||||
def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None,
|
||||
password=None, use_ssl=None, search_ougroup=None,
|
||||
search_filter=None, attr_map=None, auth_ldap=None):
|
||||
|
||||
|
||||
def ldap_map_users(conn, attr_map, users, user_names=None):
|
||||
for entry in conn.entries:
|
||||
user = entry_user(entry, attr_map)
|
||||
if user_names:
|
||||
if user.get('username', '') in user_names:
|
||||
users.append(user)
|
||||
# config
|
||||
if use_settings_config:
|
||||
self._load_config_from_settings()
|
||||
else:
|
||||
users.append(user)
|
||||
self.server_uri = server_uri
|
||||
self.bind_dn = bind_dn
|
||||
self.password = password
|
||||
self.use_ssl = use_ssl
|
||||
self.search_ougroup = search_ougroup
|
||||
self.search_filter = search_filter
|
||||
self.attr_map = attr_map
|
||||
self.auth_ldap = auth_ldap
|
||||
|
||||
def _load_config_from_settings(self):
|
||||
self.server_uri = settings.AUTH_LDAP_SERVER_URI
|
||||
self.bind_dn = settings.AUTH_LDAP_BIND_DN
|
||||
self.password = settings.AUTH_LDAP_BIND_PASSWORD
|
||||
self.use_ssl = settings.AUTH_LDAP_START_TLS
|
||||
self.search_ougroup = settings.AUTH_LDAP_SEARCH_OU
|
||||
self.search_filter = settings.AUTH_LDAP_SEARCH_FILTER
|
||||
self.attr_map = settings.AUTH_LDAP_USER_ATTR_MAP
|
||||
self.auth_ldap = settings.AUTH_LDAP
|
||||
|
||||
def entry_user(entry, attr_map):
|
||||
user = {}
|
||||
user['is_imported'] = _('No')
|
||||
for attr, mapping in attr_map.items():
|
||||
if not hasattr(entry, mapping):
|
||||
continue
|
||||
value = getattr(entry, mapping).value
|
||||
user[attr] = value if value else ''
|
||||
if attr != 'username':
|
||||
continue
|
||||
if User.objects.filter(username=user[attr]):
|
||||
user['is_imported'] = _('Yes')
|
||||
return user
|
||||
@staticmethod
|
||||
def get_user_by_username(username):
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except Exception as e:
|
||||
logger.info(e)
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
|
||||
def get_ldap_setting():
|
||||
host = settings.AUTH_LDAP_SERVER_URI
|
||||
bind_dn = settings.AUTH_LDAP_BIND_DN
|
||||
password = settings.AUTH_LDAP_BIND_PASSWORD
|
||||
use_ssl = settings.AUTH_LDAP_START_TLS
|
||||
search_ougroup = settings.AUTH_LDAP_SEARCH_OU
|
||||
search_filter = settings.AUTH_LDAP_SEARCH_FILTER
|
||||
attr_map = settings.AUTH_LDAP_USER_ATTR_MAP
|
||||
auth_ldap = settings.AUTH_LDAP
|
||||
|
||||
ldap_setting = {
|
||||
'host': host, 'bind_dn': bind_dn, 'password': password,
|
||||
'search_ougroup': search_ougroup, 'search_filter': search_filter,
|
||||
'attr_map': attr_map, 'auth_ldap': auth_ldap, 'use_ssl': use_ssl,
|
||||
}
|
||||
return ldap_setting
|
||||
|
||||
|
||||
def save_user(users):
|
||||
exist = []
|
||||
username_list = [item.get('username') for item in users]
|
||||
for name in username_list:
|
||||
if User.objects.filter(username=name).exclude(source='ldap'):
|
||||
exist.append(name)
|
||||
users = [user for user in users if (user.get('username') not in exist)]
|
||||
|
||||
result_save = save(users, exist)
|
||||
return result_save
|
||||
|
||||
|
||||
def save(users, exist):
|
||||
fail_user = []
|
||||
for item in users:
|
||||
item = set_default_item(item)
|
||||
user = User.objects.filter(username=item['username'], source='ldap')
|
||||
user = user.first()
|
||||
if not user:
|
||||
try:
|
||||
user = User.objects.create(**item)
|
||||
except Exception as e:
|
||||
fail_user.append(item.get('username'))
|
||||
@staticmethod
|
||||
def _update_user(user, user_item):
|
||||
for field, value in user_item.items():
|
||||
if not hasattr(user, field):
|
||||
continue
|
||||
for key, value in item.items():
|
||||
user.key = value
|
||||
user.save()
|
||||
setattr(user, field, value)
|
||||
user.save()
|
||||
|
||||
get_msg = get_messages(users, exist, fail_user)
|
||||
return get_msg
|
||||
def update_user(self, user_item):
|
||||
user = self.get_user_by_username(user_item['username'])
|
||||
if not user:
|
||||
msg = _('User does not exist')
|
||||
return False, msg
|
||||
if user.source != User.SOURCE_LDAP:
|
||||
msg = _('The user source is not LDAP')
|
||||
return False, msg
|
||||
|
||||
|
||||
def set_default_item(item):
|
||||
item['source'] = 'ldap'
|
||||
if not item.get('email', ''):
|
||||
if '@' in item['username']:
|
||||
item['email'] = item['username']
|
||||
try:
|
||||
self._update_user(user, user_item)
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return False, str(e)
|
||||
else:
|
||||
item['email'] = item['username'] + '@' + settings.EMAIL_SUFFIX
|
||||
if 'is_imported' in item.keys():
|
||||
item.pop('is_imported')
|
||||
return item
|
||||
return True, None
|
||||
|
||||
@staticmethod
|
||||
def create_user(user_item):
|
||||
user_item['source'] = User.SOURCE_LDAP
|
||||
try:
|
||||
User.objects.create(**user_item)
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
return False, str(e)
|
||||
else:
|
||||
return True, None
|
||||
|
||||
def get_messages(users, exist, fail_user):
|
||||
if exist:
|
||||
info = _("Import {} users successfully; import {} users failed, the "
|
||||
"database already exists with the same name")
|
||||
msg = info.format(len(users), str(exist))
|
||||
@staticmethod
|
||||
def get_or_construct_email(user_item):
|
||||
if not user_item.get('email', None):
|
||||
if '@' in user_item['username']:
|
||||
email = user_item['username']
|
||||
else:
|
||||
email = '{}@{}'.format(
|
||||
user_item['username'], settings.EMAIL_SUFFIX)
|
||||
else:
|
||||
email = user_item['email']
|
||||
return email
|
||||
|
||||
if fail_user:
|
||||
info = _("Import {} users successfully; import {} users failed, "
|
||||
"the database already exists with the same name; import {}"
|
||||
"users failed, Because’TypeError' object has no attribute "
|
||||
"'keys'")
|
||||
msg = info.format(len(users)-len(fail_user), str(exist), str(fail_user))
|
||||
else:
|
||||
msg = _("Import {} users successfully").format(len(users))
|
||||
def create_or_update_users(self, user_items, force_update=True):
|
||||
succeed = failed = 0
|
||||
for user_item in user_items:
|
||||
user_item['email'] = self.get_or_construct_email(user_item)
|
||||
exist = user_item.pop('existing', None)
|
||||
if exist:
|
||||
ok, error = self.update_user(user_item)
|
||||
else:
|
||||
ok, error = self.create_user(user_item)
|
||||
if not ok:
|
||||
failed += 1
|
||||
else:
|
||||
succeed += 1
|
||||
result = {'total': len(user_items), 'succeed': succeed, 'failed': failed}
|
||||
return result
|
||||
|
||||
if fail_user:
|
||||
info = _("Import {} users successfully;import {} users failed, "
|
||||
"Because’TypeError' object has no attribute 'keys'")
|
||||
msg = info.format(len(users)-len(fail_user), str(fail_user))
|
||||
return {'msg': msg}
|
||||
def _ldap_entry_to_user_item(self, entry):
|
||||
user_item = {}
|
||||
for attr, mapping in self.attr_map.items():
|
||||
if not hasattr(entry, mapping):
|
||||
continue
|
||||
user_item[attr] = getattr(entry, mapping).value or ''
|
||||
return user_item
|
||||
|
||||
def get_connection(self):
|
||||
server = Server(self.server_uri, use_ssl=self.use_ssl)
|
||||
conn = Connection(server, self.bind_dn, self.password)
|
||||
conn.bind()
|
||||
return conn
|
||||
|
||||
def get_search_user_items(self):
|
||||
conn = self.get_connection()
|
||||
user_items = []
|
||||
search_ougroup = str(self.search_ougroup).split("|")
|
||||
for search_ou in search_ougroup:
|
||||
ok = conn.search(
|
||||
search_ou, self.search_filter % ({"user": "*"}),
|
||||
attributes=list(self.attr_map.values())
|
||||
)
|
||||
if not ok:
|
||||
error = _("Search no entry matched in ou {}".format(search_ou))
|
||||
raise LDAPOUGroupException(error)
|
||||
|
||||
for entry in conn.entries:
|
||||
user_item = self._ldap_entry_to_user_item(entry)
|
||||
user = self.get_user_by_username(user_item['username'])
|
||||
user_item['existing'] = bool(user)
|
||||
user_items.append(user_item)
|
||||
|
||||
return user_items
|
||||
|
||||
def sync_users(self, username_set):
|
||||
user_items = self.get_search_user_items()
|
||||
if username_set:
|
||||
user_items = [u for u in user_items if u['username'] in username_set]
|
||||
result = self.create_or_update_users(user_items)
|
||||
return result
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
|
@ -12,6 +12,7 @@
|
|||
<h4 class="modal-title">{% block modal_title %}{% endblock %}</h4>
|
||||
<small>{% block modal_comment %}{% endblock %}</small>
|
||||
</div>
|
||||
{% block modal_help_message %}{% endblock %}
|
||||
<div class="modal-body">
|
||||
{% block modal_body %}
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in New Issue