From 12c8cf6b76a3fdebd9066c429db2f1f971bfe949 Mon Sep 17 00:00:00 2001 From: BaiJiangjie Date: Thu, 19 Apr 2018 11:13:11 +0800 Subject: [PATCH] =?UTF-8?q?[Update]=20=E6=B7=BB=E5=8A=A0OTP=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/i18n/zh/LC_MESSAGES/django.mo | Bin 30779 -> 31646 bytes apps/i18n/zh/LC_MESSAGES/django.po | 364 +++++++++++------- apps/users/api.py | 84 ++-- apps/users/models/user.py | 2 +- apps/users/serializers.py | 2 +- apps/users/templates/users/_base_otp.html | 2 +- apps/users/templates/users/login_otp.html | 2 +- .../templates/users/user_otp_enable_bind.html | 7 +- .../users/user_password_authentication.html | 6 +- apps/users/templates/users/user_profile.html | 3 +- apps/users/urls/api_urls.py | 1 + apps/users/utils.py | 24 +- apps/users/views/login.py | 11 +- apps/users/views/user.py | 53 ++- requirements/requirements.txt | 1 + 15 files changed, 347 insertions(+), 215 deletions(-) diff --git a/apps/i18n/zh/LC_MESSAGES/django.mo b/apps/i18n/zh/LC_MESSAGES/django.mo index a343d985af7dfd3579a03d47852c15e6b2e7ed68..21cce3fc3140daf1c93525e482b6fa13ff0683f0 100644 GIT binary patch delta 11772 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 delta 10954 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 lJnXwL;aR`\n" "Language-Team: Jumpserver team\n" @@ -154,7 +154,7 @@ msgstr "名称" #: assets/templates/assets/system_user_detail.html:62 #: assets/templates/assets/system_user_list.html:27 #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:13 -#: users/models/authentication.py:45 users/models/user.py:39 +#: users/forms.py:22 users/models/authentication.py:45 users/models/user.py:39 #: users/templates/users/_select_user_modal.html:14 #: users/templates/users/login.html:56 #: users/templates/users/login_log_list.html:49 @@ -169,9 +169,11 @@ msgid "Password or private key passphrase" msgstr "密码或密钥密码" #: assets/forms/user.py:25 assets/models/base.py:22 common/forms.py:113 -#: users/forms.py:15 users/forms.py:24 users/templates/users/login.html:59 +#: users/forms.py:15 users/forms.py:24 users/forms.py:36 +#: users/templates/users/login.html:59 #: users/templates/users/reset_password.html:52 #: users/templates/users/user_create.html:11 +#: users/templates/users/user_password_authentication.html:13 #: users/templates/users/user_password_update.html:40 #: users/templates/users/user_profile_update.html:40 #: users/templates/users/user_pubkey_update.html:40 @@ -310,7 +312,7 @@ msgstr "标签管理" #: assets/templates/assets/system_user_detail.html:96 #: ops/templates/ops/adhoc_detail.html:86 perms/models.py:28 perms/models.py:72 #: perms/templates/perms/asset_permission_detail.html:98 -#: users/models/user.py:55 users/templates/users/user_detail.html:99 +#: users/models/user.py:55 users/templates/users/user_detail.html:107 msgid "Created by" msgstr "创建者" @@ -341,10 +343,10 @@ msgstr "创建日期" #: ops/models/adhoc.py:42 perms/models.py:30 perms/models.py:74 #: perms/templates/perms/asset_permission_detail.html:102 terminal/models.py:26 #: terminal/templates/terminal/terminal_detail.html:63 users/models/group.py:15 -#: users/models/user.py:52 users/templates/users/user_detail.html:111 +#: users/models/user.py:52 users/templates/users/user_detail.html:119 #: users/templates/users/user_group_detail.html:67 #: users/templates/users/user_group_list.html:14 -#: users/templates/users/user_profile.html:114 +#: users/templates/users/user_profile.html:122 msgid "Comment" msgstr "备注" @@ -390,7 +392,7 @@ msgid "Default" msgstr "默认" #: assets/models/cluster.py:36 assets/models/label.py:13 -#: users/models/user.py:285 +#: users/models/user.py:299 msgid "System" msgstr "系统" @@ -428,10 +430,10 @@ msgstr "默认资产组" #: terminal/templates/terminal/command_list.html:32 #: terminal/templates/terminal/command_list.html:72 #: terminal/templates/terminal/session_list.html:33 -#: terminal/templates/terminal/session_list.html:71 users/forms.py:219 -#: users/models/user.py:30 users/models/user.py:273 +#: terminal/templates/terminal/session_list.html:71 users/forms.py:231 +#: users/models/user.py:30 users/models/user.py:287 #: users/templates/users/user_group_detail.html:78 -#: users/templates/users/user_group_list.html:13 users/views/user.py:335 +#: users/templates/users/user_group_list.html:13 users/views/user.py:339 msgid "User" msgstr "用户" @@ -536,34 +538,6 @@ msgstr "" msgid "推送系统用户到入资产: {}" msgstr "" -#: assets/templates/assets/_admin_user_setting_modal.html:4 -#: users/templates/users/reset_password.html:57 -#: users/templates/users/user_profile.html:20 -msgid "Setting" -msgstr "设置" - -#: assets/templates/assets/_admin_user_setting_modal.html:9 -#: assets/templates/assets/_asset_import_modal.html:9 -#: users/templates/users/_user_import_modal.html:10 -msgid "Template" -msgstr "模板" - -#: assets/templates/assets/_admin_user_setting_modal.html:10 -#: assets/templates/assets/_asset_import_modal.html:10 -#: users/templates/users/_user_import_modal.html:11 -msgid "Download" -msgstr "下载" - -#: assets/templates/assets/_admin_user_setting_modal.html:13 -#: assets/templates/assets/_asset_import_modal.html:13 -msgid "Asset csv file" -msgstr "资产csv文件" - -#: assets/templates/assets/_admin_user_setting_modal.html:16 -#: assets/templates/assets/_asset_import_modal.html:16 -msgid "If set id, will use this id update asset existed" -msgstr "如果设置了id,则会使用该行信息更新该id的资产" - #: assets/templates/assets/_asset_group_bulk_update_modal.html:5 msgid "Update asset group" msgstr "更新用户组" @@ -594,6 +568,24 @@ msgstr "二次验证" msgid "Import asset" msgstr "导入资产" +#: assets/templates/assets/_asset_import_modal.html:9 +#: users/templates/users/_user_import_modal.html:10 +msgid "Template" +msgstr "模板" + +#: assets/templates/assets/_asset_import_modal.html:10 +#: users/templates/users/_user_import_modal.html:11 +msgid "Download" +msgstr "下载" + +#: assets/templates/assets/_asset_import_modal.html:13 +msgid "Asset csv file" +msgstr "资产csv文件" + +#: assets/templates/assets/_asset_import_modal.html:16 +msgid "If set id, will use this id update asset existed" +msgstr "如果设置了id,则会使用该行信息更新该id的资产" + #: assets/templates/assets/_asset_list_modal.html:7 assets/views/asset.py:50 #: templates/_nav.html:23 msgid "Asset list" @@ -637,7 +629,7 @@ msgstr "其它" #: assets/templates/assets/asset_update.html:70 #: assets/templates/assets/domain_create_update.html:16 #: assets/templates/assets/gateway_create_update.html:58 -#: assets/templates/assets/label_create_update.html:16 +#: assets/templates/assets/label_create_update.html:18 #: common/templates/common/basic_setting.html:58 #: common/templates/common/email_setting.html:59 #: common/templates/common/ldap_setting.html:59 @@ -647,7 +639,7 @@ msgstr "其它" #: users/templates/users/_user.html:43 #: users/templates/users/user_bulk_update.html:23 #: users/templates/users/user_password_update.html:58 -#: users/templates/users/user_profile.html:151 +#: users/templates/users/user_profile.html:180 #: users/templates/users/user_profile_update.html:63 #: users/templates/users/user_pubkey_update.html:70 #: users/templates/users/user_pubkey_update.html:76 @@ -662,7 +654,7 @@ msgstr "重置" #: assets/templates/assets/asset_update.html:71 #: assets/templates/assets/domain_create_update.html:17 #: assets/templates/assets/gateway_create_update.html:59 -#: assets/templates/assets/label_create_update.html:17 +#: assets/templates/assets/label_create_update.html:19 #: common/templates/common/basic_setting.html:59 #: common/templates/common/email_setting.html:60 #: common/templates/common/ldap_setting.html:60 @@ -740,7 +732,7 @@ msgstr "测试" #: assets/templates/assets/asset_list.html:170 #: assets/templates/assets/domain_detail.html:24 #: assets/templates/assets/domain_detail.html:103 -#: assets/templates/assets/domain_gateway_list.html:90 +#: assets/templates/assets/domain_gateway_list.html:85 #: assets/templates/assets/domain_list.html:42 #: assets/templates/assets/label_list.html:38 #: assets/templates/assets/system_user_detail.html:26 @@ -753,8 +745,8 @@ msgstr "测试" #: users/templates/users/user_group_detail.html:28 #: users/templates/users/user_group_list.html:43 #: users/templates/users/user_list.html:76 -#: users/templates/users/user_profile.html:135 #: users/templates/users/user_profile.html:143 +#: users/templates/users/user_profile.html:172 msgid "Update" msgstr "更新" @@ -764,7 +756,7 @@ msgstr "更新" #: assets/templates/assets/asset_list.html:171 #: assets/templates/assets/domain_detail.html:28 #: assets/templates/assets/domain_detail.html:104 -#: assets/templates/assets/domain_gateway_list.html:91 +#: assets/templates/assets/domain_gateway_list.html:86 #: assets/templates/assets/domain_list.html:43 #: assets/templates/assets/label_list.html:39 #: assets/templates/assets/system_user_detail.html:30 @@ -796,13 +788,13 @@ msgstr "选择节点" #: assets/templates/assets/system_user_detail.html:183 #: assets/templates/assets/system_user_list.html:138 templates/_modal.html:22 #: terminal/templates/terminal/session_detail.html:108 -#: users/templates/users/user_detail.html:339 -#: users/templates/users/user_detail.html:364 -#: users/templates/users/user_detail.html:387 +#: users/templates/users/user_detail.html:357 +#: users/templates/users/user_detail.html:382 +#: users/templates/users/user_detail.html:405 #: users/templates/users/user_group_create_update.html:32 #: users/templates/users/user_group_list.html:86 #: users/templates/users/user_list.html:196 -#: users/templates/users/user_profile.html:185 +#: users/templates/users/user_profile.html:214 msgid "Confirm" msgstr "确认" @@ -852,15 +844,15 @@ msgid "Disk" msgstr "硬盘" #: assets/templates/assets/asset_detail.html:121 -#: users/templates/users/user_detail.html:103 -#: users/templates/users/user_profile.html:88 +#: users/templates/users/user_detail.html:111 +#: users/templates/users/user_profile.html:96 msgid "Date joined" msgstr "创建日期" #: assets/templates/assets/asset_detail.html:137 #: terminal/templates/terminal/session_detail.html:81 -#: users/templates/users/user_detail.html:122 -#: users/templates/users/user_profile.html:126 +#: users/templates/users/user_detail.html:130 +#: users/templates/users/user_profile.html:134 msgid "Quick modify" msgstr "快速修改" @@ -873,7 +865,7 @@ msgstr "快速修改" #: perms/templates/perms/asset_permission_list.html:59 #: terminal/templates/terminal/terminal_list.html:34 #: users/templates/users/_select_user_modal.html:18 -#: users/templates/users/user_detail.html:128 +#: users/templates/users/user_detail.html:136 #: users/templates/users/user_granted_asset.html:46 #: users/templates/users/user_group_granted_asset.html:46 #: users/templates/users/user_list.html:27 @@ -890,7 +882,8 @@ msgid "Refresh" msgstr "刷新" #: assets/templates/assets/asset_detail.html:300 -#: users/templates/users/user_detail.html:273 +#: users/templates/users/user_detail.html:282 +#: users/templates/users/user_detail.html:304 msgid "Update successfully!" msgstr "更新成功" @@ -978,8 +971,8 @@ msgstr "存在资产,不能删除" #: assets/templates/assets/asset_list.html:595 #: assets/templates/assets/system_user_list.html:133 -#: users/templates/users/user_detail.html:334 -#: users/templates/users/user_detail.html:359 +#: users/templates/users/user_detail.html:352 +#: users/templates/users/user_detail.html:377 #: users/templates/users/user_group_list.html:81 #: users/templates/users/user_list.html:191 msgid "Are you sure?" @@ -1032,7 +1025,7 @@ msgstr "网关列表" msgid "Create gateway" msgstr "创建网关" -#: assets/templates/assets/domain_gateway_list.html:92 +#: assets/templates/assets/domain_gateway_list.html:87 #: common/templates/common/email_setting.html:58 #: common/templates/common/ldap_setting.html:58 msgid "Test connection" @@ -1242,14 +1235,18 @@ msgstr "%(name)s 创建成功" msgid "%(name)s was updated successfully" msgstr "%(name)s 更新成功" -#: common/fields.py:26 +#: common/fields.py:30 msgid "Not a valid json" msgstr "不是合法json" -#: common/fields.py:28 +#: common/fields.py:32 msgid "Not a string type" msgstr "不是字符类型" +#: common/fields.py:69 +msgid "Encrypt field using Secret Key" +msgstr "" + #: common/forms.py:70 msgid "Current SITE URL" msgstr "当前站点URL" @@ -1389,7 +1386,7 @@ msgstr "" msgid "discard time" msgstr "" -#: common/models.py:29 +#: common/models.py:29 users/templates/users/user_detail.html:96 msgid "Enabled" msgstr "启用" @@ -1707,8 +1704,8 @@ msgstr "任务列表" msgid "Task run history" msgstr "执行历史" -#: perms/forms.py:18 users/forms.py:176 users/forms.py:181 users/forms.py:193 -#: users/forms.py:223 +#: perms/forms.py:18 users/forms.py:188 users/forms.py:193 users/forms.py:205 +#: users/forms.py:235 msgid "Select users" msgstr "选择用户" @@ -1717,7 +1714,7 @@ msgstr "选择用户" #: perms/templates/perms/asset_permission_list.html:136 templates/_nav.html:14 #: users/models/group.py:25 users/models/user.py:42 #: users/templates/users/_select_user_modal.html:16 -#: users/templates/users/user_detail.html:179 +#: users/templates/users/user_detail.html:188 #: users/templates/users/user_list.html:26 msgid "User group" msgstr "用户组" @@ -1732,8 +1729,8 @@ msgstr "" #: perms/models.py:27 perms/models.py:71 #: perms/templates/perms/asset_permission_detail.html:90 -#: users/models/user.py:54 users/templates/users/user_detail.html:95 -#: users/templates/users/user_profile.html:96 +#: users/models/user.py:54 users/templates/users/user_detail.html:103 +#: users/templates/users/user_profile.html:104 msgid "Date expired" msgstr "失效日期" @@ -1770,7 +1767,7 @@ msgid "Add node to this permission" msgstr "添加节点" #: perms/templates/perms/asset_permission_asset.html:125 -#: users/templates/users/user_detail.html:196 +#: users/templates/users/user_detail.html:205 msgid "Join" msgstr "加入" @@ -1856,13 +1853,13 @@ msgstr "商业支持" msgid "Docs" msgstr "文档" -#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:93 +#: templates/_header_bar.html:37 templates/_nav_user.html:9 users/forms.py:105 #: users/templates/users/_user.html:36 #: users/templates/users/user_password_update.html:37 #: 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:318 +#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:322 msgid "Profile" msgstr "个人信息" @@ -1919,13 +1916,13 @@ msgstr "关闭" #: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 #: users/views/group.py:62 users/views/group.py:79 users/views/group.py:95 -#: users/views/login.py:205 users/views/login.py:254 users/views/user.py:60 -#: users/views/user.py:75 users/views/user.py:95 users/views/user.py:151 -#: users/views/user.py:306 users/views/user.py:353 users/views/user.py:375 +#: users/views/login.py:240 users/views/login.py:289 users/views/user.py:64 +#: users/views/user.py:79 users/views/user.py:99 users/views/user.py:155 +#: users/views/user.py:310 users/views/user.py:357 users/views/user.py:379 msgid "Users" msgstr "用户管理" -#: templates/_nav.html:13 users/views/user.py:61 +#: templates/_nav.html:13 users/views/user.py:65 msgid "User list" msgstr "用户列表" @@ -2231,7 +2228,11 @@ msgstr "" msgid "Invalid token or cache refreshed." msgstr "" -#: users/forms.py:27 users/models/user.py:43 +#: users/forms.py:30 +msgid "Otp_code" +msgstr "" + +#: users/forms.py:39 users/models/user.py:43 #: users/templates/users/_select_user_modal.html:15 #: users/templates/users/user_detail.html:87 #: users/templates/users/user_list.html:25 @@ -2239,57 +2240,57 @@ msgstr "" msgid "Role" msgstr "角色" -#: users/forms.py:29 users/forms.py:139 +#: users/forms.py:41 users/forms.py:151 msgid "ssh public key" msgstr "ssh公钥" -#: users/forms.py:30 users/forms.py:140 +#: users/forms.py:42 users/forms.py:152 msgid "ssh-rsa AAAA..." msgstr "" -#: users/forms.py:31 +#: users/forms.py:43 msgid "Paste user id_rsa.pub here." msgstr "复制用户公钥到这里" -#: users/forms.py:49 users/templates/users/user_detail.html:187 +#: users/forms.py:61 users/templates/users/user_detail.html:196 msgid "Join user groups" msgstr "添加到用户组" -#: users/forms.py:59 users/forms.py:154 +#: users/forms.py:71 users/forms.py:166 msgid "Public key should not be the same as your old one." msgstr "不能和原来的密钥相同" -#: users/forms.py:63 users/forms.py:158 users/serializers.py:42 +#: users/forms.py:75 users/forms.py:170 users/serializers.py:45 msgid "Not a valid ssh public key" msgstr "ssh密钥不合法" -#: users/forms.py:99 +#: users/forms.py:111 msgid "Old password" msgstr "原来密码" -#: users/forms.py:104 +#: users/forms.py:116 msgid "New password" msgstr "新密码" -#: users/forms.py:109 +#: users/forms.py:121 msgid "Confirm password" msgstr "确认密码" -#: users/forms.py:119 +#: users/forms.py:131 msgid "Old password error" msgstr "原来密码错误" -#: users/forms.py:127 +#: users/forms.py:139 msgid "Password does not match" msgstr "密码不一致" -#: users/forms.py:141 +#: users/forms.py:153 msgid "Paste your id_rsa.pub here." msgstr "复制你的公钥到这里" -#: users/forms.py:169 users/models/user.py:51 +#: users/forms.py:181 users/models/user.py:51 #: users/templates/users/user_password_update.html:43 -#: users/templates/users/user_profile.html:71 +#: users/templates/users/user_profile.html:79 #: users/templates/users/user_profile_update.html:43 #: users/templates/users/user_pubkey_update.html:43 msgid "Public key" @@ -2319,7 +2320,7 @@ msgstr "Agent" msgid "Date login" msgstr "登录日期" -#: users/models/user.py:29 users/models/user.py:281 +#: users/models/user.py:29 users/models/user.py:295 msgid "Administrator" msgstr "管理员" @@ -2327,15 +2328,18 @@ msgstr "管理员" msgid "Application" msgstr "应用程序" -#: users/models/user.py:34 +#: users/models/user.py:34 users/templates/users/user_profile.html:74 +#: users/templates/users/user_profile.html:155 +#: users/templates/users/user_profile.html:158 msgid "Disable" msgstr "禁用" -#: users/models/user.py:35 +#: users/models/user.py:35 users/templates/users/user_profile.html:72 +#: users/templates/users/user_profile.html:162 msgid "Enable" msgstr "启用" -#: users/models/user.py:36 +#: users/models/user.py:36 users/templates/users/user_profile.html:70 msgid "Force enable" msgstr "强制启用" @@ -2352,11 +2356,11 @@ msgstr "头像" msgid "Wechat" msgstr "微信" -#: users/models/user.py:47 +#: users/models/user.py:47 users/templates/users/user_detail.html:91 msgid "Enable OTP" msgstr "二次验证" -#: users/models/user.py:284 +#: users/models/user.py:298 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" @@ -2408,11 +2412,16 @@ msgstr "Step" #: users/templates/users/first_login.html:57 msgid "Previous" -msgstr "" +msgstr "上一步" #: users/templates/users/first_login.html:60 +#: users/templates/users/login_otp.html:66 +#: users/templates/users/user_otp_authentication.html:22 +#: users/templates/users/user_otp_enable_bind.html:25 +#: users/templates/users/user_otp_enable_install_app.html:22 +#: users/templates/users/user_password_authentication.html:21 msgid "Next" -msgstr "" +msgstr "下一步" #: users/templates/users/first_login_done.html:30 msgid "Welcome to use jumpserver, visit " @@ -2447,8 +2456,22 @@ msgstr "Agent" msgid "City" msgstr "城市" +#: users/templates/users/login_otp.html:45 +msgid "二次认证" +msgstr "" + +#: users/templates/users/login_otp.html:64 +#: users/templates/users/user_otp_authentication.html:19 +#: users/templates/users/user_otp_enable_bind.html:18 +msgid "Six figures" +msgstr "6位数字" + +#: users/templates/users/login_otp.html:69 +msgid "Can't provide security? Please contact the administrator" +msgstr "如果不能提供OTP码,请联系管理员" + #: users/templates/users/reset_password.html:45 -#: users/templates/users/user_detail.html:325 users/utils.py:71 +#: users/templates/users/user_detail.html:343 users/utils.py:72 msgid "Reset password" msgstr "重置密码" @@ -2456,8 +2479,13 @@ msgstr "重置密码" msgid "Password again" msgstr "再次输入密码" +#: users/templates/users/reset_password.html:57 +#: users/templates/users/user_profile.html:20 +msgid "Setting" +msgstr "设置" + #: users/templates/users/user_create.html:4 -#: users/templates/users/user_list.html:16 users/views/user.py:75 +#: users/templates/users/user_list.html:16 users/views/user.py:79 msgid "Create user" msgstr "创建用户" @@ -2466,7 +2494,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:152 +#: users/templates/users/user_granted_asset.html:18 users/views/user.py:156 msgid "User detail" msgstr "用户详情" @@ -2477,55 +2505,67 @@ msgstr "用户详情" msgid "Asset granted" msgstr "授权的资产" -#: users/templates/users/user_detail.html:107 -#: users/templates/users/user_profile.html:92 +#: users/templates/users/user_detail.html:94 +msgid "Force enabled" +msgstr "强制启用" + +#: users/templates/users/user_detail.html:98 +msgid "Disabled" +msgstr "禁用" + +#: users/templates/users/user_detail.html:115 +#: users/templates/users/user_profile.html:100 msgid "Last login" msgstr "最后登录" -#: users/templates/users/user_detail.html:157 +#: users/templates/users/user_detail.html:151 +msgid "Force enabled OTP" +msgstr "强制启用OTP" + +#: users/templates/users/user_detail.html:166 msgid "Send reset password mail" msgstr "发送重置密码邮件" -#: users/templates/users/user_detail.html:160 -#: users/templates/users/user_detail.html:168 +#: users/templates/users/user_detail.html:169 +#: users/templates/users/user_detail.html:177 msgid "Send" msgstr "发送" -#: users/templates/users/user_detail.html:165 +#: users/templates/users/user_detail.html:174 msgid "Send reset ssh key mail" msgstr "发送重置密钥邮件" -#: users/templates/users/user_detail.html:324 +#: users/templates/users/user_detail.html:342 msgid "An e-mail has been sent to the user`s mailbox." msgstr "已发送邮件到用户邮箱" -#: users/templates/users/user_detail.html:335 +#: users/templates/users/user_detail.html:353 msgid "This will reset the user password and send a reset mail" msgstr "将失效用户当前密码,并发送重设密码邮件到用户邮箱" -#: users/templates/users/user_detail.html:349 +#: users/templates/users/user_detail.html:367 msgid "" "The reset-ssh-public-key E-mail has been sent successfully. Please inform " "the user to update his new ssh public key." msgstr "重设密钥邮件将会发送到用户邮箱" -#: users/templates/users/user_detail.html:350 +#: users/templates/users/user_detail.html:368 msgid "Reset SSH public key" msgstr "重置SSH密钥" -#: users/templates/users/user_detail.html:360 +#: users/templates/users/user_detail.html:378 msgid "This will reset the user public key and send a reset mail" msgstr "将会失效用户当前密钥,并发送重置邮件到用户邮箱" -#: users/templates/users/user_detail.html:377 -#: users/templates/users/user_profile.html:174 +#: users/templates/users/user_detail.html:395 +#: users/templates/users/user_profile.html:203 msgid "Successfully updated the SSH public key." msgstr "更新ssh密钥成功" -#: users/templates/users/user_detail.html:378 -#: users/templates/users/user_detail.html:382 -#: users/templates/users/user_profile.html:175 -#: users/templates/users/user_profile.html:180 +#: users/templates/users/user_detail.html:396 +#: users/templates/users/user_detail.html:400 +#: users/templates/users/user_profile.html:204 +#: users/templates/users/user_profile.html:209 msgid "User SSH public key update" msgstr "ssh密钥" @@ -2585,24 +2625,28 @@ msgstr "用户删除失败" msgid "OTP" msgstr "" -#: users/templates/users/user_profile.html:100 users/views/user.py:181 -#: users/views/user.py:235 +#: users/templates/users/user_profile.html:108 users/views/user.py:185 +#: users/views/user.py:239 msgid "User groups" msgstr "用户组" -#: users/templates/users/user_profile.html:132 +#: users/templates/users/user_profile.html:140 msgid "Update password" msgstr "更改密码" -#: users/templates/users/user_profile.html:140 +#: users/templates/users/user_profile.html:148 +msgid "Update otp" +msgstr "更改OTP设置" + +#: users/templates/users/user_profile.html:169 msgid "Update SSH public key" msgstr "更改SSH密钥" -#: users/templates/users/user_profile.html:148 +#: users/templates/users/user_profile.html:177 msgid "Reset public key and download" msgstr "重置并下载SSH密钥" -#: users/templates/users/user_profile.html:178 +#: users/templates/users/user_profile.html:207 msgid "Failed to update SSH public key." msgstr "更新密钥失败" @@ -2622,15 +2666,15 @@ msgstr "更新密钥" msgid "Or reset by server" msgstr "或者重置并下载密钥" -#: users/templates/users/user_update.html:4 users/views/user.py:95 +#: users/templates/users/user_update.html:4 users/views/user.py:99 msgid "Update user" msgstr "更新用户" -#: users/utils.py:35 +#: users/utils.py:36 msgid "Create account successfully" msgstr "创建账户成功" -#: users/utils.py:37 +#: users/utils.py:38 #, python-format msgid "" "\n" @@ -2671,7 +2715,7 @@ msgstr "" "
\n" " " -#: users/utils.py:73 +#: users/utils.py:74 #, python-format msgid "" "\n" @@ -2715,11 +2759,11 @@ msgstr "" "
\n" " " -#: users/utils.py:104 +#: users/utils.py:105 msgid "SSH Key Reset" msgstr "重置ssh密钥" -#: users/utils.py:106 +#: users/utils.py:107 #, python-format msgid "" "\n" @@ -2744,15 +2788,15 @@ msgstr "" "
\n" " " -#: users/utils.py:139 +#: users/utils.py:140 msgid "User not exist" msgstr "用户不存在" -#: users/utils.py:141 +#: users/utils.py:142 msgid "Disabled or expired" msgstr "禁用或失效" -#: users/utils.py:154 +#: users/utils.py:155 msgid "Password or SSH public key invalid" msgstr "密码或密钥不合法" @@ -2768,78 +2812,102 @@ msgstr "更新用户组" msgid "User group granted asset" msgstr "用户组授权资产" -#: users/views/login.py:55 +#: users/views/login.py:56 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:97 +#: users/views/login.py:106 users/views/user.py:460 users/views/user.py:485 +msgid "Otp code invalid" +msgstr "otp码认证失败" + +#: users/views/login.py:132 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:98 +#: users/views/login.py:133 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:114 +#: users/views/login.py:149 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:127 +#: users/views/login.py:162 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:128 +#: users/views/login.py:163 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:142 +#: users/views/login.py:177 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:143 +#: users/views/login.py:178 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:160 users/views/login.py:173 +#: users/views/login.py:195 users/views/login.py:208 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:169 +#: users/views/login.py:204 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:205 +#: users/views/login.py:240 msgid "First login" msgstr "首次登陆" -#: users/views/login.py:255 +#: users/views/login.py:290 msgid "Login log list" msgstr "登录日志" -#: users/views/user.py:105 +#: users/views/user.py:109 msgid "Bulk update user success" msgstr "批量更新用户成功" -#: users/views/user.py:210 +#: users/views/user.py:214 msgid "Invalid file." msgstr "文件不合法" -#: users/views/user.py:307 +#: users/views/user.py:311 msgid "User granted assets" msgstr "用户授权资产" -#: users/views/user.py:336 +#: users/views/user.py:340 msgid "Profile setting" msgstr "个人信息设置" -#: users/views/user.py:354 +#: users/views/user.py:358 msgid "Password update" msgstr "密码更新" -#: users/views/user.py:376 +#: users/views/user.py:380 msgid "Public key update" msgstr "密钥更新" +#: users/views/user.py:419 +msgid "Password invalid" +msgstr "用户名或密码无效" + +#: users/views/user.py:512 +msgid "OTP enable success" +msgstr "OTP 绑定成功" + +#: users/views/user.py:513 +msgid "OTP enable success, return login page" +msgstr "OTP 绑定成功,返回到登录页面" + +#: users/views/user.py:515 +msgid "OTP disable success" +msgstr "OTP 解绑成功" + +#: users/views/user.py:516 +msgid "OTP disable success, return login page" +msgstr "OTP 解绑成功,返回登录页面" + #~ msgid "Add asset" #~ msgstr "添加资产到节点" diff --git a/apps/users/api.py b/apps/users/api.py index c64c52b44..1f7e4f792 100644 --- a/apps/users/api.py +++ b/apps/users/api.py @@ -2,6 +2,7 @@ import uuid from django.core.cache import cache +from django.urls import reverse from rest_framework import generics from rest_framework.permissions import AllowAny, IsAuthenticated @@ -139,40 +140,75 @@ class UserProfile(APIView): return Response(self.serializer_class(request.user).data) +class UserOtpAuthApi(APIView): + permission_classes = (AllowAny,) + serializer_class = UserSerializer + + def post(self, request): + otp_code = request.data.get('otp_code', '') + seed = request.data.get('seed', '') + + user = cache.get(seed, None) + if not user: + return Response({'msg': '请先进行用户名和密码验证'}, status=401) + + if not check_otp_code(user.otp_secret_key, otp_code): + return Response({'msg': 'otp认证失败'}, status=401) + + token = generate_token(request, user) + self.write_login_log(request, user) + return Response( + { + 'token': token, + 'user': self.serializer_class(user).data + } + ) + + @staticmethod + def write_login_log(request, user): + login_ip = request.data.get('remote_addr', None) + login_type = request.data.get('login_type', '') + user_agent = request.data.get('HTTP_USER_AGENT', '') + + if not login_ip: + login_ip = get_login_ip(request) + + write_login_log_async.delay( + user.username, ip=login_ip, + type=login_type, user_agent=user_agent, + ) + + class UserAuthApi(APIView): permission_classes = (AllowAny,) serializer_class = UserSerializer def post(self, request): - otp_check = request.data.get('otp_check', None) - - if otp_check: - # otp验证 - return self.check_auth_otp(request) - else: - # password验证 - return self.check_auth_password(request) - - def check_auth_password(self, request): user, msg = self.check_user_valid(request) - if user: - token = generate_token(request, user) - if not user.otp_enabled: - self.write_login_log(request, user) - return Response({'token': token, 'user': self.serializer_class(user).data}) - else: + if not user: return Response({'msg': msg}, status=401) - def check_auth_otp(self, request): - otp_code = request.data.get('otp_code', '') - user, msg = self.check_user_valid(request) - if user: + if not user.otp_enabled: token = generate_token(request, user) - if check_otp_code(user.otp_secret_key, otp_code): - self.write_login_log(request, user) - return Response({'token': token, 'user': self.serializer_class(user).data}) - return Response({'msg': msg}, status=401) + self.write_login_log(request, user) + return Response( + { + 'token': token, + 'user': self.serializer_class(user).data + } + ) + + seed = uuid.uuid4().hex + cache.set(seed, user, 300) + return Response( + { + 'code': 101, + 'msg': '请携带seed值,进行OTP二次认证', + 'otp_url': reverse('api-users:user-otp-auth'), + 'seed': seed, + 'user': self.serializer_class(user).data + }, status=300) @staticmethod def check_user_valid(request): diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 4674fe519..d64c038d8 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -232,7 +232,7 @@ class User(AbstractUser): def disable_otp(self): self.otp_level = 0 - self.otp_secret_key = '' + self.otp_secret_key = None def to_json(self): return OrderedDict({ diff --git a/apps/users/serializers.py b/apps/users/serializers.py index 450c63823..f1347b0d5 100644 --- a/apps/users/serializers.py +++ b/apps/users/serializers.py @@ -21,7 +21,7 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer): list_serializer_class = BulkListSerializer exclude = [ 'first_name', 'last_name', 'password', '_private_key', - '_public_key', 'otp_secret_key', 'user_permissions' + '_public_key', '_otp_secret_key', 'user_permissions' ] def get_field_names(self, declared_fields, info): diff --git a/apps/users/templates/users/_base_otp.html b/apps/users/templates/users/_base_otp.html index fb32fb425..cd9c43edc 100644 --- a/apps/users/templates/users/_base_otp.html +++ b/apps/users/templates/users/_base_otp.html @@ -25,7 +25,7 @@ diff --git a/apps/users/templates/users/login_otp.html b/apps/users/templates/users/login_otp.html index e8bfc75e8..80f5dc429 100644 --- a/apps/users/templates/users/login_otp.html +++ b/apps/users/templates/users/login_otp.html @@ -66,7 +66,7 @@ - {% trans "Can't provide security? Please contact the administrator" %} + {% trans "Can't provide otp code? Please contact the administrator" %} diff --git a/apps/users/templates/users/user_otp_enable_bind.html b/apps/users/templates/users/user_otp_enable_bind.html index e97f7cf9e..7e3d19cf9 100644 --- a/apps/users/templates/users/user_otp_enable_bind.html +++ b/apps/users/templates/users/user_otp_enable_bind.html @@ -7,10 +7,8 @@

使用手机 Google Authenticator 应用扫描以下二维码,获取6位验证码

-
-
{% csrf_token %} @@ -18,12 +16,13 @@
+ + + {% if 'otp_code' in form.errors %}

{{ form.otp_code.errors.as_text }}

{% endif %} - - diff --git a/apps/users/templates/users/user_password_authentication.html b/apps/users/templates/users/user_password_authentication.html index 7603a1149..773700241 100644 --- a/apps/users/templates/users/user_password_authentication.html +++ b/apps/users/templates/users/user_password_authentication.html @@ -5,6 +5,7 @@ {% block content %}
{% csrf_token %} +
@@ -13,13 +14,12 @@ + + {% if 'password' in form.errors %}

{{ form.password.errors.as_text }}

{% endif %} - - -
{% endblock %} diff --git a/apps/users/templates/users/user_profile.html b/apps/users/templates/users/user_profile.html index a924b9f02..6cda70eb9 100644 --- a/apps/users/templates/users/user_profile.html +++ b/apps/users/templates/users/user_profile.html @@ -152,8 +152,7 @@ href=" {% if request.user.otp_enabled and request.user.otp_secret_key %} {% if request.user.otp_force_enabled %} - javascript:void(0) - ">{% trans 'Disable' %} + " disabled >{% trans 'Disable' %} {% else %} {% url 'users:user-otp-disable-authentication' %} ">{% trans 'Disable' %} diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py index ce681c146..683638a4e 100644 --- a/apps/users/urls/api_urls.py +++ b/apps/users/urls/api_urls.py @@ -20,6 +20,7 @@ urlpatterns = [ url(r'^v1/connection-token/$', api.UserConnectionTokenApi.as_view(), name='connection-token'), url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'), url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'), + url(r'^v1/otp/auth/$', api.UserOtpAuthApi.as_view(), name='user-otp-auth'), url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/password/$', api.ChangeUserPasswordApi.as_view(), name='change-user-password'), url(r'^v1/users/(?P[0-9a-zA-Z\-]{36})/password/reset/$', diff --git a/apps/users/utils.py b/apps/users/utils.py index 8113ec358..94368e0c7 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -224,16 +224,14 @@ def get_ip_city(ip, timeout=10): return city -def get_user(request): - if is_login(request): - user = request.user - else: - user = cache.get(request.session.session_key) +def get_tmp_user_from_session(request): + user_id = request.session.get('tmp_user_id') + user = get_object_or_none(User, pk=user_id) return user -def is_login(request): - return isinstance(request.user, User) +def set_tmp_user_to_session(request, user): + request.session['tmp_user_id'] = str(user.id) def redirect_user_first_login_or_index(request, redirect_field_name): @@ -244,9 +242,15 @@ def redirect_user_first_login_or_index(request, redirect_field_name): request.GET.get(redirect_field_name, reverse('index'))) -def generate_otp_uri(user, issuer="Jumpserver"): - otp_secret_key = base64.b32encode(os.urandom(10)).decode('utf-8') - cache.set('otp_secret_key', otp_secret_key, 300) +def generate_otp_uri(request, issuer="Jumpserver"): + if request.user.is_authenticated: + user = request.user + else: + user = get_tmp_user_from_session(request) + otp_secret_key = cache.get(request.session.session_key+'otp_key', '') + if not otp_secret_key: + otp_secret_key = base64.b32encode(os.urandom(10)).decode('utf-8') + cache.set(request.session.session_key+'otp_key', otp_secret_key, 600) totp = pyotp.TOTP(otp_secret_key) return totp.provisioning_uri(name=user.username, issuer_name=issuer) diff --git a/apps/users/views/login.py b/apps/users/views/login.py index bb18b28da..d7a98e174 100644 --- a/apps/users/views/login.py +++ b/apps/users/views/login.py @@ -19,12 +19,12 @@ from django.views.generic.base import TemplateView from django.views.generic.edit import FormView from formtools.wizard.views import SessionWizardView from django.conf import settings -from django.core.cache import cache from common.utils import get_object_or_none from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin from ..models import User, LoginLog -from ..utils import send_reset_password_mail, check_otp_code , get_login_ip, redirect_user_first_login_or_index +from ..utils import send_reset_password_mail, check_otp_code, get_login_ip, redirect_user_first_login_or_index, \ + get_tmp_user_from_session, set_tmp_user_to_session from ..tasks import write_login_log_async from .. import forms @@ -54,11 +54,12 @@ class UserLoginView(FormView): def form_valid(self, form): if not self.request.session.test_cookie_worked(): return HttpResponse(_("Please enable cookies and try again.")) - cache.set(self.request.session.session_key, form.get_user(), 600) + + set_tmp_user_to_session(self.request, form.get_user()) return redirect(self.get_success_url()) def get_success_url(self): - user = cache.get(self.request.session.session_key) + user = get_tmp_user_from_session(self.request) if user.otp_enabled and user.otp_secret_key: # 1,2 & T @@ -94,7 +95,7 @@ class UserLoginOtpView(FormView): redirect_field_name = 'next' def form_valid(self, form): - user = cache.get(self.request.session.session_key) + user = get_tmp_user_from_session(self.request) otp_code = form.cleaned_data.get('otp_code') otp_secret_key = user.otp_secret_key diff --git a/apps/users/views/user.py b/apps/users/views/user.py index af954ba10..99c45b19c 100644 --- a/apps/users/views/user.py +++ b/apps/users/views/user.py @@ -35,7 +35,7 @@ from common.mixins import JSONResponseMixin from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen from .. import forms from ..models import User, UserGroup -from ..utils import AdminUserRequiredMixin, generate_otp_uri, check_otp_code, get_user, is_login +from ..utils import AdminUserRequiredMixin, generate_otp_uri, check_otp_code, get_tmp_user_from_session from ..signals import post_user_create from ..tasks import write_login_log_async @@ -400,20 +400,31 @@ class UserOtpEnableAuthenticationView(FormView): form_class = forms.UserCheckPasswordForm def get_form(self, form_class=None): + if self.request.user.is_authenticated: + user = self.request.user + else: + user = get_tmp_user_from_session(self.request) form = super().get_form(form_class=form_class) - form['username'].initial = get_user(self.request).username + form['username'].initial = user.username return form def get_context_data(self, **kwargs): + if self.request.user.is_authenticated: + user = self.request.user + else: + user = get_tmp_user_from_session(self.request) context = { - 'user': get_user(self.request) + 'user': user } kwargs.update(context) return super().get_context_data(**kwargs) def form_valid(self, form): + if self.request.user.is_authenticated: + user = self.request.user + else: + user = get_tmp_user_from_session(self.request) password = form.cleaned_data.get('password') - user = get_user(self.request) user = authenticate(username=user.username, password=password) if not user: form.add_error("password", _("Password invalid")) @@ -428,8 +439,12 @@ class UserOtpEnableInstallAppView(TemplateView): template_name = 'users/user_otp_enable_install_app.html' def get_context_data(self, **kwargs): + if self.request.user.is_authenticated: + user = self.request.user + else: + user = get_tmp_user_from_session(self.request) context = { - 'user': get_user(self.request) + 'user': user } kwargs.update(context) return super().get_context_data(**kwargs) @@ -441,16 +456,20 @@ class UserOtpEnableBindView(TemplateView, FormView): success_url = reverse_lazy('users:user-otp-settings-success') def get_context_data(self, **kwargs): + if self.request.user.is_authenticated: + user = self.request.user + else: + user = get_tmp_user_from_session(self.request) context = { - 'otp_uri': generate_otp_uri(user=get_user(self.request)), - 'user': get_user(self.request) + 'otp_uri': generate_otp_uri(self.request), + 'user': user } kwargs.update(context) return super().get_context_data(**kwargs) def form_valid(self, form): otp_code = form.cleaned_data.get('otp_code') - otp_secret_key = cache.get('otp_secret_key') + otp_secret_key = cache.get(self.request.session.session_key+'otp_key', '') if check_otp_code(otp_secret_key, otp_code): self.save_otp(otp_secret_key) @@ -461,7 +480,10 @@ class UserOtpEnableBindView(TemplateView, FormView): return self.form_invalid(form) def save_otp(self, otp_secret_key): - user = get_user(self.request) + if self.request.user.is_authenticated: + user = self.request.user + else: + user = get_tmp_user_from_session(self.request) user.enable_otp() user.otp_secret_key = otp_secret_key user.save() @@ -489,11 +511,8 @@ class UserOtpDisableAuthenticationView(FormView): class UserOtpSettingsSuccessView(TemplateView): template_name = 'flash_message_standalone.html' - def get(self, request, *args, **kwargs): - response = super().get(request, *args, **kwargs) - if is_login(request): - auth_logout(request) - return response + # def get(self, request, *args, **kwargs): + # return super().get(request, *args, **kwargs) def get_context_data(self, **kwargs): title, describe = self.get_title_describe() @@ -508,7 +527,11 @@ class UserOtpSettingsSuccessView(TemplateView): return super().get_context_data(**kwargs) def get_title_describe(self): - user = get_user(self.request) + if self.request.user.is_authenticated: + user = self.request.user + auth_logout(self.request) + else: + user = get_tmp_user_from_session(self.request) title = _('OTP enable success') describe = _('OTP enable success, return login page') if not user.otp_enabled: diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 790c19eb7..e0ecc634e 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -54,6 +54,7 @@ pyasn1==0.4.2 pycparser==2.18 pycrypto==2.6.1 pyldap==2.4.45 +pyotp==2.2.6 PyNaCl==1.2.1 python-dateutil==2.6.1 python-gssapi==0.6.4