ZAO*Yxj9cU%QD)piXFp#c?dw$Ni`YQoQm1ducBVHYDDKsW91F
zXHJ|=Tmd!V6)b`eF*jy;=boWDn1Z+i=G1S%VN|{(u@2K>`hVSkB~VvX7j+BTqfYFH
zRd6y^#0#j0IPAUK;t14&b7K!IfroJ&&c&Ypxt)vufj5Wwy$C90u_2bjd8jMDin^i#
zUV#4(BFC^bar%G&?+YA(jqwVW#3Hc*+%3RQh{s@3oNsPGJp+4D3p|Ye_y4RVezy(}
zQSbQ+)cg9z^6ydaePC=iPzY*=Qs5wr#Duug+P_8Z%y#nts{aXVzaBflz5joa&=x;2
zKcH3^66j6{L&bSf{YqgNR=4&}sE29@s^0=kj%!f2bg$(vn|I8Yfj)OaK%4;o)1DYL
zKz_5dS3QsD(5{t-Ld;UmtTcYRhL@ybjfGm&J$7b6A}GpUAvEFMWKsfSjoJv>0mO_NWi0
ze%3zM@~co2ZMFD>c?C7$1JpuaVLFVLz>Ob)$`?e9UkQWs{x_ha6Pux)_6`|Ir;^kf+<^L^2}tTz-V}9(?NR3qM18W2we}3j0{nlHio{UzHBejL
z3K`yi)Q-h};2evM8Av7OC_;TMgiQSV{mAdIjsvZI4bCQi1}j?I8f?xs
z+^{~D_QqL!oBlCJA3lG)`!t-O9pS*`j{R~
zUEd>m{kY}+|8}gjo!^1B+?2Y+t!W=i{hHeJV9c~m?>LDsD=)vrnW@Jd5tdV&fs=<2
zU$RAXAQwU|<`_&{a>^%^f9P`#Ga$d}yz$iMQKE_KQhY->F)@h;lp8j}eXM8$-ijF&
zS8+lnY)9XA)L&B{LA?uor{gP1bM?n^gmAVeSntX}*yz`dGOoz54
z^U`SuCLrb;^Zy<#sa>IMz4bp!eFuG(kk3K=e7E{rY7>j;1$QY}?r}Hz)b=S@7iN$DJfKxc<67_Y+FDn1>J@LPk?d0@3
zK%e_Mx>ItJZ$iF?&LQ?Cq*9)Qj;#)_81Y;tyhOZ&`cL>H{rXUJOm%o8EsiFxO~1d1
z|D)ujUdP(hbs}ROri78x*LKXYMgJR|=|r(OAtxQCSj*;*PX3u(N=gyROY+6&r=tyJ
zBj@QzYmTB#A4G-h{6*B8QeTdFtWN{fhglyyrTecVfKDqYt4S7TkZu(Hzam_utsMEn
z)VJ8go6MR16Zu#0xQsqAN5PnWoTDQaZTz0`K2X1HZOW&mp514i))Ca9v{DNPKMTA{
zl!tbYvmd4!zow}=0@WyYa@YBKz6^dJ41qayX_
z|IOv|W)YkwdBl?Wu#Qc%mij%)BToN=K7Zn8l%mwL&?h}5Htm}zg~<=c@9kVd|FMOC
z_9NGfwlti#(emkuOU35?w;&ORKu2dfuEz$HrPPaXLRo85duHlm$fu`1h_-W-?}(!*
zb0`6tfYOQj7pUVqOvHIf$bC!E&pI7PC=L1j=|4(-G|A_5($Sh+0QJSV6Mv+yzIL}!
z*Y5-!Kak5!sZV_wUhrq#KL*fGzXP^&{sY=_5r0a3IrSRUd!zsPr|v((Nt7mNM5hh-
zH`x@F5!5eZ5B!WaE&4hJQMys|X9E3wVJP*OqaT$L7APIexn(#n=E&hHKL1~Z9Hhrv
z$^<&>rTj``GfZm(zM}q()i+b$Pdz0im5sKL!F7C2+kcdC)W4!#zkXtl>Ev6`Zyu#T
z$>GF{b^SZ2?4xsK8{{i>AXk!3|B#=Dt0*VQ@ypA9e1dza6{7E1`joVD)p*T=%eGR!}|dz)BdwQ|94SRTL-oGqV%B5r@UdJqqLQv=r^2>pUK_9ER@y^(ugw6
z7RIk?Zvy#n`hSjFEnkYbEhQd(e?c9asr%~kkNYGZQuL?V!IZV+a;gW%C)DFpZqPoA
zxGCi>gZ82fAg2o`M(!rY9A~IcA}Bi<$EW05QvM{@)AIWJXKy#j
z#+-bDo>#4*JDwuWLq3;{u$sD#NOK2$3Q@m;(y?^*l0Y?IJw+p?-#A`WeDdkt{wXv4VNjX4!d2CD35rKo4bO=R%
zJkLdYO>&PYzA+@GQGO*@LP+JQk0UBTnTc!7@!5VAbvs7(UV+B>z@rXl3PLU
z7&fBl=wR)tccFw5&!*2SU4JzOiXu6Ny{i2M}*2lsy>K^YnoQgkFGeoJF3NZd7RQ%{8_DEb>z9ba-T*U#@e
zlGQk27WI}SS5kB=#)7oP9BZi8q@ND`IdMGoTjZ7z$H(Qw)yS769!cp!y&?JZ7;~f{
zj{0Bh^A?fNv5@3S2Do5xQtG3~C&zlUZL^d9BKJ4-=hm+P?Hj4rr!=H(Jf%Ikp7g0s
zJ)F{&I0(Nb*NyrQ6#WU>fBkzoal1co|0qv9;G_7WO&pt(_=EBPcT}hUPvn26WT*8R
zv5uz>FB5Tf+P)#y&%JV3&c>Oe{hvaI&X^VJ(P1+6_LQS^T0q=~dL^7l8Ehw#@|MuQ
z1Eu~)xhUeS7N4U}8|rz;=}5!*FNuGlzKz^p)P2JV`q~LYF_2D`u>w}4({b8EsfS=Y
zJdAlMIu6nP1?3cF7w4>^{ZE`joEm?l&w7kG_EGi)+9bm(Q&}?
z-{W`WCXzc!xu!;rPpv(Q^M=|v$|oT|m3Y3jPr__A@owskC`E|N==uLZ(3{SYHb6bh
zYxNnljb*?+6#n+aD@*x*kAVzS`=ex2;uXY=ap*_+Fk&6|7$Xt&vXnpc^*N2oY?2*q
z@Gr@Aq_n5hB6pXQ#^Xt19XF|0;he5`oU)1B9Q+>V;W+v|rVOAyiheqpQ=dV-A>{~l
z9nGlcQKdMQ5m><{m_mnA#PKY?OuU{x4Pwn$RxwnMY&emQ{-S#A>5Et9&!kMHKAECp17#8QW#oN>2!>P1NFo__BmTfZE39)0
zat*BhJ8fBsV~)7gR}<(WX3^(7Mv^;=s~KZ7xt)~!#4*PP;!q2+x>I?ZNfw|yr^FmP
z8DI~kBZ(|{kn`$`JqWn6a^9+!^0i
z3{=r&tfXXOq;ZrNwwms^mi#f!+-kFYZEa65hvhz}{WszsloU2gu=Uf7d#s=8I-Xdc
zYSar*FER3?4h7VM_+QC!h%$jr*(eXF*P#5u;J*`J4~X7WJ0xfHgTbv5MXw(JYhd&r
zlOF^|U;Vmh!Wm;0r=C%1@rUT4OAe)oTQp<-q9qCzjIOfnefH=MzaLH*-TT4L!016w
X&jv;}c(pzLjC3DvZp{`@Kl%Rwe\n"
"Language-Team: Jumpserver team\n"
@@ -172,7 +172,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
#: settings/templates/settings/terminal_setting.html:102 terminal/models.py:22
#: terminal/models.py:241 terminal/templates/terminal/terminal_detail.html:43
#: terminal/templates/terminal/terminal_list.html:29 users/models/group.py:14
-#: users/models/user.py:54 users/templates/users/_select_user_modal.html:13
+#: users/models/user.py:61 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_detail.html:63
#: users/templates/users/user_group_detail.html:55
#: users/templates/users/user_group_list.html:12
@@ -208,7 +208,7 @@ msgstr "名称"
#: 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:37 users/forms.py:13
-#: users/models/user.py:52 users/templates/users/_select_user_modal.html:14
+#: users/models/user.py:59 users/templates/users/_select_user_modal.html:14
#: users/templates/users/user_detail.html:67
#: users/templates/users/user_list.html:24
#: users/templates/users/user_profile.html:47
@@ -246,7 +246,7 @@ msgid "Password"
msgstr "密码"
#: assets/forms/user.py:29 assets/serializers/asset_user.py:27
-#: users/models/user.py:81
+#: users/models/user.py:88
msgid "Private key"
msgstr "ssh私钥"
@@ -437,7 +437,7 @@ msgstr "标签管理"
#: assets/templates/assets/system_user_detail.html:100
#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:15 perms/models.py:36
#: perms/models.py:89 perms/templates/perms/asset_permission_detail.html:98
-#: users/models/user.py:95 users/templates/users/user_detail.html:111
+#: users/models/user.py:102 users/templates/users/user_detail.html:111
#: xpack/plugins/change_auth_plan/models.py:103
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
#: xpack/plugins/cloud/models.py:55 xpack/plugins/cloud/models.py:127
@@ -482,7 +482,7 @@ msgstr "创建日期"
#: orgs/models.py:17 perms/models.py:38 perms/models.py:91
#: perms/templates/perms/asset_permission_detail.html:102 settings/models.py:34
#: terminal/models.py:32 terminal/templates/terminal/terminal_detail.html:63
-#: users/models/group.py:15 users/models/user.py:87
+#: users/models/group.py:15 users/models/user.py:94
#: users/templates/users/user_detail.html:127
#: users/templates/users/user_group_detail.html:67
#: users/templates/users/user_group_list.html:14
@@ -554,7 +554,7 @@ msgstr "带宽"
msgid "Contact"
msgstr "联系人"
-#: assets/models/cluster.py:22 users/models/user.py:73
+#: assets/models/cluster.py:22 users/models/user.py:80
#: users/templates/users/user_detail.html:76
msgid "Phone"
msgstr "手机"
@@ -580,7 +580,7 @@ msgid "Default"
msgstr "默认"
#: assets/models/cluster.py:36 assets/models/label.py:14
-#: users/models/user.py:457
+#: users/models/user.py:473
msgid "System"
msgstr "系统"
@@ -728,7 +728,7 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:283
-#: users/models/user.py:32 users/models/user.py:445
+#: users/models/user.py:36 users/models/user.py:461
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:13 users/views/user.py:386
#: xpack/plugins/orgs/forms.py:26
@@ -827,7 +827,7 @@ msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
#: assets/serializers/asset_user.py:23 users/forms.py:230
-#: users/models/user.py:84 users/templates/users/first_login.html:42
+#: users/models/user.py:91 users/templates/users/first_login.html:42
#: users/templates/users/user_password_update.html:46
#: users/templates/users/user_profile.html:68
#: users/templates/users/user_profile_update.html:43
@@ -1026,7 +1026,7 @@ msgstr "其它"
#: settings/templates/settings/command_storage_create.html:79
#: settings/templates/settings/email_setting.html:62
#: settings/templates/settings/ldap_setting.html:61
-#: settings/templates/settings/replay_storage_create.html:151
+#: settings/templates/settings/replay_storage_create.html:152
#: settings/templates/settings/security_setting.html:70
#: settings/templates/settings/terminal_setting.html:68
#: terminal/templates/terminal/terminal_update.html:45
@@ -1062,7 +1062,7 @@ msgstr "重置"
#: settings/templates/settings/command_storage_create.html:80
#: settings/templates/settings/email_setting.html:63
#: settings/templates/settings/ldap_setting.html:64
-#: settings/templates/settings/replay_storage_create.html:152
+#: settings/templates/settings/replay_storage_create.html:153
#: settings/templates/settings/security_setting.html:71
#: settings/templates/settings/terminal_setting.html:70
#: terminal/templates/terminal/command_list.html:103
@@ -1076,7 +1076,7 @@ msgstr "重置"
#: 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:73
+#: xpack/plugins/interface/templates/interface/interface.html:74
msgid "Submit"
msgstr "提交"
@@ -1161,7 +1161,7 @@ msgstr "更新认证"
#: assets/templates/assets/system_user_asset.html:350
#: users/templates/users/user_detail.html:307
#: users/templates/users/user_detail.html:334
-#: xpack/plugins/interface/views.py:31
+#: xpack/plugins/interface/views.py:34
msgid "Update successfully!"
msgstr "更新成功"
@@ -1271,6 +1271,7 @@ msgstr "选择节点"
#: users/templates/users/user_profile.html:238
#: xpack/plugins/cloud/templates/cloud/account_create_update.html:34
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:36
+#: xpack/plugins/interface/templates/interface/interface.html:103
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
msgid "Confirm"
msgstr "确认"
@@ -1500,6 +1501,7 @@ msgstr "重命名失败,不能更改root节点的名称"
#: users/templates/users/user_detail.html:476
#: users/templates/users/user_group_list.html:82
#: users/templates/users/user_list.html:202
+#: xpack/plugins/interface/templates/interface/interface.html:97
msgid "Are you sure?"
msgstr "你确认吗?"
@@ -1516,6 +1518,7 @@ msgstr "删除选择资产"
#: users/templates/users/user_group_create_update.html:31
#: users/templates/users/user_group_list.html:86
#: users/templates/users/user_list.html:206
+#: xpack/plugins/interface/templates/interface/interface.html:101
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:32
msgid "Cancel"
msgstr "取消"
@@ -1938,7 +1941,7 @@ msgid "User agent"
msgstr "Agent"
#: audits/models.py:99 audits/templates/audits/login_log_list.html:56
-#: users/forms.py:142 users/models/user.py:76
+#: users/forms.py:142 users/models/user.py:83
#: users/templates/users/first_login.html:45
msgid "MFA"
msgstr "MFA"
@@ -2125,7 +2128,17 @@ msgstr ""
msgid "Invalid token or cache refreshed."
msgstr ""
-#: authentication/forms.py:29 users/forms.py:21
+#: authentication/forms.py:19
+msgid ""
+"Please enter a correct username and password. Note that both fields may be "
+"case-sensitive."
+msgstr "请输入正确的用户名和密码. 注意它们是区分大小写."
+
+#: authentication/forms.py:22
+msgid "This account is inactive."
+msgstr "此账户无效"
+
+#: authentication/forms.py:37 users/forms.py:21
msgid "MFA code"
msgstr "MFA 验证码"
@@ -2687,7 +2700,7 @@ msgstr "组织管理"
#: perms/templates/perms/asset_permission_list.html:55
#: perms/templates/perms/asset_permission_list.html:75
#: perms/templates/perms/asset_permission_list.html:122 templates/_nav.html:14
-#: users/forms.py:253 users/models/group.py:26 users/models/user.py:60
+#: users/forms.py:253 users/models/group.py:26 users/models/user.py:67
#: users/templates/users/_select_user_modal.html:16
#: users/templates/users/user_detail.html:213
#: users/templates/users/user_list.html:26
@@ -2705,7 +2718,7 @@ msgstr "资产和节点至少选一个"
#: perms/models.py:35 perms/models.py:88
#: perms/templates/perms/asset_permission_detail.html:90
-#: users/models/user.py:92 users/templates/users/user_detail.html:107
+#: users/models/user.py:99 users/templates/users/user_detail.html:107
#: users/templates/users/user_profile.html:116
msgid "Date expired"
msgstr "失效日期"
@@ -3123,7 +3136,7 @@ msgid "Please submit the LDAP configuration before import"
msgstr "请先提交LDAP配置再进行导入"
#: settings/templates/settings/_ldap_list_users_modal.html:39
-#: users/models/user.py:56 users/templates/users/user_detail.html:71
+#: users/models/user.py:63 users/templates/users/user_detail.html:71
#: users/templates/users/user_profile.html:59
msgid "Email"
msgstr "邮件"
@@ -3431,7 +3444,7 @@ msgstr ""
#: templates/_nav.html:10 users/views/group.py:27 users/views/group.py:43
#: users/views/group.py:59 users/views/group.py:75 users/views/group.py:91
-#: users/views/login.py:151 users/views/user.py:68 users/views/user.py:83
+#: users/views/login.py:153 users/views/user.py:68 users/views/user.py:83
#: users/views/user.py:113 users/views/user.py:194 users/views/user.py:355
#: users/views/user.py:405 users/views/user.py:445
msgid "Users"
@@ -3871,7 +3884,7 @@ msgstr "你可以使用ssh客户端工具连接终端"
msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
-#: users/forms.py:32 users/models/user.py:64
+#: users/forms.py:32 users/models/user.py:71
#: users/templates/users/_select_user_modal.html:15
#: users/templates/users/user_detail.html:87
#: users/templates/users/user_list.html:25
@@ -3964,53 +3977,53 @@ msgstr "复制你的公钥到这里"
msgid "Select users"
msgstr "选择用户"
-#: users/models/user.py:31 users/models/user.py:453
+#: users/models/user.py:35 users/models/user.py:469
msgid "Administrator"
msgstr "管理员"
-#: users/models/user.py:33
+#: users/models/user.py:37
msgid "Application"
msgstr "应用程序"
-#: users/models/user.py:36 users/templates/users/user_profile.html:92
+#: users/models/user.py:40 users/templates/users/user_profile.html:92
#: users/templates/users/user_profile.html:159
#: users/templates/users/user_profile.html:162
msgid "Disable"
msgstr "禁用"
-#: users/models/user.py:37 users/templates/users/user_profile.html:90
+#: users/models/user.py:41 users/templates/users/user_profile.html:90
#: users/templates/users/user_profile.html:166
msgid "Enable"
msgstr "启用"
-#: users/models/user.py:38 users/templates/users/user_profile.html:88
+#: users/models/user.py:42 users/templates/users/user_profile.html:88
msgid "Force enable"
msgstr "强制启用"
-#: users/models/user.py:67
+#: users/models/user.py:74
msgid "Avatar"
msgstr "头像"
-#: users/models/user.py:70 users/templates/users/user_detail.html:82
+#: users/models/user.py:77 users/templates/users/user_detail.html:82
msgid "Wechat"
msgstr "微信"
-#: users/models/user.py:99 users/templates/users/user_detail.html:103
+#: users/models/user.py:106 users/templates/users/user_detail.html:103
#: users/templates/users/user_list.html:27
#: users/templates/users/user_profile.html:100
msgid "Source"
msgstr "用户来源"
-#: users/models/user.py:103
+#: users/models/user.py:110
msgid "Date password last updated"
msgstr "最后更新密码日期"
-#: users/models/user.py:129 users/templates/users/user_update.html:22
-#: users/views/login.py:45 users/views/login.py:104 users/views/user.py:418
+#: users/models/user.py:136 users/templates/users/user_update.html:22
+#: users/views/login.py:46 users/views/login.py:107 users/views/user.py:418
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
-#: users/models/user.py:456
+#: users/models/user.py:472
msgid "Administrator is the super user of system"
msgstr "Administrator是初始的超级管理员"
@@ -4658,40 +4671,40 @@ msgstr "更新用户组"
msgid "User group granted asset"
msgstr "用户组授权资产"
-#: users/views/login.py:42
+#: users/views/login.py:43
msgid "Email address invalid, please input again"
msgstr "邮箱地址错误,重新输入"
-#: users/views/login.py:58
+#: users/views/login.py:59
msgid "Send reset password message"
msgstr "发送重置密码邮件"
-#: users/views/login.py:59
+#: users/views/login.py:60
msgid "Send reset password mail success, login your mail box and follow it "
msgstr ""
"发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)"
-#: users/views/login.py:72
+#: users/views/login.py:73
msgid "Reset password success"
msgstr "重置密码成功"
-#: users/views/login.py:73
+#: users/views/login.py:74
msgid "Reset password success, return to login page"
msgstr "重置密码成功,返回到登录页面"
-#: users/views/login.py:88 users/views/login.py:107
+#: users/views/login.py:89 users/views/login.py:105
msgid "Token invalid or expired"
msgstr "Token错误或失效"
-#: users/views/login.py:100
+#: users/views/login.py:101
msgid "Password not same"
msgstr "密码不一致"
-#: users/views/login.py:113 users/views/user.py:128 users/views/user.py:428
+#: users/views/login.py:114 users/views/user.py:128 users/views/user.py:428
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
-#: users/views/login.py:151
+#: users/views/login.py:153
msgid "First login"
msgstr "首次登录"
@@ -5217,14 +5230,40 @@ msgid "Interface settings"
msgstr "界面设置"
#: xpack/plugins/interface/templates/interface/interface.html:15
-#: xpack/plugins/interface/views.py:21
+#: xpack/plugins/interface/views.py:24
msgid "Interface setting"
msgstr "界面设置"
-#: xpack/plugins/interface/views.py:20
+#: xpack/plugins/interface/templates/interface/interface.html:73
+#: xpack/plugins/interface/templates/interface/interface.html:108
+#: xpack/plugins/interface/templates/interface/interface.html:115
+msgid "Restore Default"
+msgstr "恢复默认"
+
+#: xpack/plugins/interface/templates/interface/interface.html:98
+msgid "This will restore default Settings of the interface !!!"
+msgstr "您确定要恢复默认初始化吗?"
+
+#: xpack/plugins/interface/templates/interface/interface.html:107
+msgid "Restore default successfully."
+msgstr "恢复默认成功!"
+
+#: xpack/plugins/interface/templates/interface/interface.html:114
+msgid "Restore default failed."
+msgstr "恢复默认失败!"
+
+#: xpack/plugins/interface/views.py:23
msgid "Interface"
msgstr "界面"
+#: xpack/plugins/interface/views.py:49
+msgid "It is already in the default setting state!"
+msgstr "当前已经是初始化状态!"
+
+#: xpack/plugins/interface/views.py:53
+msgid "Restore default successfully!"
+msgstr "恢复默认成功!"
+
#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:94
#: xpack/plugins/license/templates/license/license_detail.html:50
#: xpack/plugins/license/views.py:31
@@ -5233,7 +5272,7 @@ msgstr "许可证"
#: xpack/plugins/license/models.py:74
msgid "Standard edition"
-msgstr ""
+msgstr "标准版"
#: xpack/plugins/license/models.py:76
msgid "Enterprise edition"
diff --git a/apps/users/models/user.py b/apps/users/models/user.py
index 86751e359..9d2fc1a08 100644
--- a/apps/users/models/user.py
+++ b/apps/users/models/user.py
@@ -3,24 +3,28 @@
#
import uuid
import base64
+import string
+import random
from collections import OrderedDict
from django.conf import settings
from django.contrib.auth.hashers import make_password
from django.contrib.auth.models import AbstractUser
-from django.core import signing
from django.core.cache import cache
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.shortcuts import reverse
-from common.utils import get_signer, date_expired_default
+from common.utils import get_signer, date_expired_default, get_logger
__all__ = ['User']
+
signer = get_signer()
+logger = get_logger(__file__)
+
class User(AbstractUser):
ROLE_ADMIN = 'Admin'
@@ -47,6 +51,9 @@ class User(AbstractUser):
(SOURCE_OPENID, 'OpenID'),
(SOURCE_RADIUS, 'Radius'),
)
+
+ CACHE_KEY_USER_RESET_PASSWORD_PREFIX = "_KEY_USER_RESET_PASSWORD_{}"
+
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
username = models.CharField(
max_length=128, unique=True, verbose_name=_('Username')
@@ -346,9 +353,32 @@ class User(AbstractUser):
return user_default
def generate_reset_token(self):
- return signer.sign_t(
- {'reset': str(self.id), 'email': self.email}, expires_in=3600
- )
+ letter = string.ascii_letters + string.digits
+ token =''.join([random.choice(letter) for _ in range(50)])
+ self.set_cache(token)
+ return token
+
+ def set_cache(self, token):
+ key = self.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
+ cache.set(key, {'id': self.id, 'email': self.email}, 3600)
+
+ @classmethod
+ def validate_reset_password_token(cls, token):
+ try:
+ key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
+ value = cache.get(key)
+ user_id = value.get('id', '')
+ email = value.get('email', '')
+ user = cls.objects.get(id=user_id, email=email)
+ except (AttributeError, cls.DoesNotExist) as e:
+ logger.error(e, exc_info=True)
+ user = None
+ return user
+
+ @classmethod
+ def expired_reset_password_token(cls, token):
+ key = cls.CACHE_KEY_USER_RESET_PASSWORD_PREFIX.format(token)
+ cache.delete(key)
@property
def otp_enabled(self):
@@ -400,18 +430,6 @@ class User(AbstractUser):
access_key = app.create_access_key()
return app, access_key
- @classmethod
- def validate_reset_token(cls, token):
- try:
- data = signer.unsign_t(token)
- user_id = data.get('reset', None)
- user_email = data.get('email', '')
- user = cls.objects.get(id=user_id, email=user_email)
-
- except (signing.BadSignature, cls.DoesNotExist):
- user = None
- return user
-
def reset_password(self, new_password):
self.set_password(new_password)
self.date_password_last_updated = timezone.now()
diff --git a/apps/users/views/login.py b/apps/users/views/login.py
index ca8bbf1ac..8f79b6f43 100644
--- a/apps/users/views/login.py
+++ b/apps/users/views/login.py
@@ -1,6 +1,7 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
+from django.core.cache import cache
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import RedirectView
@@ -84,7 +85,7 @@ class UserResetPasswordView(TemplateView):
def get(self, request, *args, **kwargs):
token = request.GET.get('token', '')
- user = User.validate_reset_token(token)
+ user = User.validate_reset_password_token(token)
if not user:
kwargs.update({'errors': _('Token invalid or expired')})
else:
@@ -100,12 +101,12 @@ class UserResetPasswordView(TemplateView):
if password != password_confirm:
return self.get(request, errors=_('Password not same'))
- user = User.validate_reset_token(token)
+ user = User.validate_reset_password_token(token)
+ if not user:
+ return self.get(request, errors=_('Token invalid or expired'))
if not user.can_update_password():
error = _('User auth from {}, go there change password'.format(user.source))
return self.get(request, errors=error)
- if not user:
- return self.get(request, errors=_('Token invalid or expired'))
is_ok = check_password_rules(password)
if not is_ok:
@@ -115,6 +116,7 @@ class UserResetPasswordView(TemplateView):
)
user.reset_password(password)
+ User.expired_reset_password_token(token)
return HttpResponseRedirect(reverse('users:reset-password-success'))