Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
ee35ca36
Commit
ee35ca36
authored
Jun 05, 2018
by
BaiJiangJie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Feature] 增加功能,安全设置(全局MFA设置,密码强度校验)
parent
526943a0
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
556 additions
and
20 deletions
+556
-20
forms.py
apps/common/forms.py
+45
-0
basic_setting.html
apps/common/templates/common/basic_setting.html
+3
-0
email_setting.html
apps/common/templates/common/email_setting.html
+3
-0
ldap_setting.html
apps/common/templates/common/ldap_setting.html
+3
-0
security_setting.html
apps/common/templates/common/security_setting.html
+115
-0
terminal_setting.html
apps/common/templates/common/terminal_setting.html
+5
-0
view_urls.py
apps/common/urls/view_urls.py
+1
-0
views.py
apps/common/views.py
+25
-1
django.mo
apps/i18n/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/i18n/zh/LC_MESSAGES/django.po
+0
-0
settings.py
apps/jumpserver/settings.py
+3
-0
jumpserver.js
apps/static/js/jumpserver.js
+88
-0
pwstrength-bootstrap.js
apps/static/js/pwstrength-bootstrap.js
+0
-0
_base_create_update.html
apps/templates/_base_create_update.html
+1
-0
forms.py
apps/users/forms.py
+1
-1
_user.html
apps/users/templates/users/_user.html
+1
-0
reset_password.html
apps/users/templates/users/reset_password.html
+43
-3
user_password_update.html
apps/users/templates/users/user_password_update.html
+41
-0
user_profile.html
apps/users/templates/users/user_profile.html
+4
-1
user_update.html
apps/users/templates/users/user_update.html
+45
-0
utils.py
apps/users/utils.py
+61
-0
login.py
apps/users/views/login.py
+31
-12
user.py
apps/users/views/user.py
+37
-2
No files found.
apps/common/forms.py
View file @
ee35ca36
...
@@ -168,3 +168,48 @@ class TerminalSettingForm(BaseForm):
...
@@ -168,3 +168,48 @@ class TerminalSettingForm(BaseForm):
)
)
)
)
class
SecuritySettingForm
(
BaseForm
):
# MFA全局设置
SECURITY_MFA_AUTH
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"MFA Secondary certification"
),
help_text
=
_
(
'After opening, the user login must use MFA secondary '
'authentication (valid for all users, including administrators)'
)
)
# 最小长度
SECURITY_PASSWORD_MIN_LENGTH
=
forms
.
IntegerField
(
initial
=
6
,
label
=
_
(
"Password minimum length"
),
)
# 大写字母
SECURITY_PASSWORD_UPPER_CASE
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain capital letters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain uppercase letters'
)
)
# 小写字母
SECURITY_PASSWORD_LOWER_CASE
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain lowercase letters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain lowercase letters'
)
)
# 数字
SECURITY_PASSWORD_NUMBER
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain numeric characters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain numeric characters'
)
)
# 特殊字符
SECURITY_PASSWORD_SPECIAL_CHAR
=
forms
.
BooleanField
(
initial
=
False
,
required
=
False
,
label
=
_
(
"Must contain special characters"
),
help_text
=
_
(
'After opening, the user password changes '
'and resets must contain special characters'
)
)
apps/common/templates/common/basic_setting.html
View file @
ee35ca36
...
@@ -23,6 +23,9 @@
...
@@ -23,6 +23,9 @@
<li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</ul>
</div>
</div>
<div
class=
"tab-content"
>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/email_setting.html
View file @
ee35ca36
...
@@ -23,6 +23,9 @@
...
@@ -23,6 +23,9 @@
<li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</ul>
</div>
</div>
<div
class=
"tab-content"
>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/ldap_setting.html
View file @
ee35ca36
...
@@ -23,6 +23,9 @@
...
@@ -23,6 +23,9 @@
<li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</ul>
</div>
</div>
<div
class=
"tab-content"
>
<div
class=
"tab-content"
>
...
...
apps/common/templates/common/security_setting.html
0 → 100644
View file @
ee35ca36
{% extends 'base.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% load common_tags %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-sm-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"panel-options"
>
<ul
class=
"nav nav-tabs"
>
<li>
<a
href=
""
class=
"text-center"
><i
class=
"fa fa-cubes"
></i>
{% trans 'Basic setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:email-setting' %}"
class=
"text-center"
><i
class=
"fa fa-envelope"
></i>
{% trans 'Email setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:ldap-setting' %}"
class=
"text-center"
><i
class=
"fa fa-archive"
></i>
{% trans 'LDAP setting' %}
</a>
</li>
<li>
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
<li
class=
"active"
>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</div>
<div
class=
"tab-content"
>
<div
class=
"col-sm-12"
style=
"padding-left:0"
>
<div
class=
"ibox-content"
style=
"border-width: 0;padding-top: 40px;"
>
<form
action=
""
method=
"post"
class=
"form-horizontal"
>
{% if form.non_field_errors %}
<div
class=
"alert alert-danger"
>
{{ form.non_field_errors }}
</div>
{% endif %}
{% csrf_token %}
<h3>
{% trans "MFA setting" %}
</h3>
{% for field in form %}
{% if forloop.counter == 2 %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans "Password check rule" %}
</h3>
{% endif %}
{% if not field.field|is_bool_field %}
{% bootstrap_field field layout="horizontal" %}
{% else %}
<div
class=
"form-group"
>
<label
for=
"{{ field.id_for_label }}"
class=
"col-sm-2 control-label"
>
{{ field.label }}
</label>
<div
class=
"col-sm-8"
>
<div
class=
"col-sm-1"
>
{{ field }}
</div>
<div
class=
"col-sm-9"
>
<span
class=
"help-block"
>
{{ field.help_text }}
</span>
</div>
</div>
</div>
{% endif %}
{% endfor %}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"form-group"
>
<div
class=
"col-sm-4 col-sm-offset-2"
>
<button
class=
"btn btn-default"
type=
"reset"
>
{% trans 'Reset' %}
</button>
<button
id=
"submit_button"
class=
"btn btn-primary"
type=
"submit"
>
{% trans 'Submit' %}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
()
{
})
.
on
(
"click"
,
".btn-test"
,
function
()
{
var
data
=
{};
var
form
=
$
(
"form"
).
serializeArray
();
$
.
each
(
form
,
function
(
i
,
field
)
{
data
[
field
.
name
]
=
field
.
value
;
});
var
the_url
=
"{% url 'api-common:mail-testing' %}"
;
function
error
(
message
)
{
toastr
.
error
(
message
)
}
function
success
(
message
)
{
toastr
.
success
(
message
.
msg
)
}
APIUpdateAttr
({
url
:
the_url
,
body
:
JSON
.
stringify
(
data
),
method
:
"POST"
,
flash_message
:
false
,
success
:
success
,
error
:
error
});
})
</script>
{% endblock %}
apps/common/templates/common/terminal_setting.html
View file @
ee35ca36
...
@@ -27,6 +27,9 @@
...
@@ -27,6 +27,9 @@
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
<a
href=
"{% url 'settings:terminal-setting' %}"
class=
"text-center"
><i
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
class=
"fa fa-hdd-o"
></i>
{% trans 'Terminal setting' %}
</a>
</li>
</li>
<li>
<a
href=
"{% url 'settings:security-setting' %}"
class=
"text-center"
><i
class=
"fa fa-lock"
></i>
{% trans 'Security setting' %}
</a>
</li>
</ul>
</ul>
</div>
</div>
<div
class=
"tab-content"
>
<div
class=
"tab-content"
>
...
@@ -39,6 +42,7 @@
...
@@ -39,6 +42,7 @@
</div>
</div>
{% endif %}
{% endif %}
{% csrf_token %}
{% csrf_token %}
<h3>
{% trans "Basic setting" %}
</h3>
<h3>
{% trans "Basic setting" %}
</h3>
{% for field in form %}
{% for field in form %}
{% if not field.field|is_bool_field %}
{% if not field.field|is_bool_field %}
...
@@ -60,6 +64,7 @@
...
@@ -60,6 +64,7 @@
{% endfor %}
{% endfor %}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans "Command storage" %}
</h3>
<h3>
{% trans "Command storage" %}
</h3>
<table
class=
"table table-hover "
id=
"task-history-list-table"
>
<table
class=
"table table-hover "
id=
"task-history-list-table"
>
<thead>
<thead>
...
...
apps/common/urls/view_urls.py
View file @
ee35ca36
...
@@ -11,4 +11,5 @@ urlpatterns = [
...
@@ -11,4 +11,5 @@ urlpatterns = [
url
(
r'^email/$'
,
views
.
EmailSettingView
.
as_view
(),
name
=
'email-setting'
),
url
(
r'^email/$'
,
views
.
EmailSettingView
.
as_view
(),
name
=
'email-setting'
),
url
(
r'^ldap/$'
,
views
.
LDAPSettingView
.
as_view
(),
name
=
'ldap-setting'
),
url
(
r'^ldap/$'
,
views
.
LDAPSettingView
.
as_view
(),
name
=
'ldap-setting'
),
url
(
r'^terminal/$'
,
views
.
TerminalSettingView
.
as_view
(),
name
=
'terminal-setting'
),
url
(
r'^terminal/$'
,
views
.
TerminalSettingView
.
as_view
(),
name
=
'terminal-setting'
),
url
(
r'^security/$'
,
views
.
SecuritySettingView
.
as_view
(),
name
=
'security-setting'
),
]
]
apps/common/views.py
View file @
ee35ca36
...
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
...
@@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
from
django.conf
import
settings
from
django.conf
import
settings
from
.forms
import
EmailSettingForm
,
LDAPSettingForm
,
BasicSettingForm
,
\
from
.forms
import
EmailSettingForm
,
LDAPSettingForm
,
BasicSettingForm
,
\
TerminalSettingForm
TerminalSettingForm
,
SecuritySettingForm
from
.mixins
import
AdminUserRequiredMixin
from
.mixins
import
AdminUserRequiredMixin
from
.signals
import
ldap_auth_enable
from
.signals
import
ldap_auth_enable
...
@@ -122,3 +122,27 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
...
@@ -122,3 +122,27 @@ class TerminalSettingView(AdminUserRequiredMixin, TemplateView):
return
render
(
request
,
self
.
template_name
,
context
)
return
render
(
request
,
self
.
template_name
,
context
)
class
SecuritySettingView
(
AdminUserRequiredMixin
,
TemplateView
):
form_class
=
SecuritySettingForm
template_name
=
"common/security_setting.html"
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Settings'
),
'action'
:
_
(
'Security setting'
),
'form'
:
self
.
form_class
(),
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
def
post
(
self
,
request
):
form
=
self
.
form_class
(
request
.
POST
)
if
form
.
is_valid
():
form
.
save
()
msg
=
_
(
"Update setting successfully, please restart program"
)
messages
.
success
(
request
,
msg
)
return
redirect
(
'settings:security-setting'
)
else
:
context
=
self
.
get_context_data
()
context
.
update
({
"form"
:
form
})
return
render
(
request
,
self
.
template_name
,
context
)
apps/i18n/zh/LC_MESSAGES/django.mo
View file @
ee35ca36
No preview for this file type
apps/i18n/zh/LC_MESSAGES/django.po
View file @
ee35ca36
This diff is collapsed.
Click to expand it.
apps/jumpserver/settings.py
View file @
ee35ca36
...
@@ -401,6 +401,9 @@ TERMINAL_REPLAY_STORAGE = {
...
@@ -401,6 +401,9 @@ TERMINAL_REPLAY_STORAGE = {
},
},
}
}
DEFAULT_PASSWORD_MIN_LENGTH
=
6
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3
=
{
BOOTSTRAP3
=
{
'horizontal_label_class'
:
'col-md-2'
,
'horizontal_label_class'
:
'col-md-2'
,
...
...
apps/static/js/jumpserver.js
View file @
ee35ca36
...
@@ -609,3 +609,91 @@ function setUrlParam(url, name, value) {
...
@@ -609,3 +609,91 @@ function setUrlParam(url, name, value) {
}
}
return
url
return
url
}
}
// 校验密码-改变规则颜色
function
checkPasswordRules
(
password
,
minLength
)
{
if
(
wordMinLength
(
password
,
minLength
))
{
$
(
'#rule_SECURITY_PASSWORD_MIN_LENGTH'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_MIN_LENGTH'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordUpperCase
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_UPPER_CASE'
).
css
(
'color'
,
'green'
);
}
else
{
$
(
'#rule_SECURITY_PASSWORD_UPPER_CASE'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordLowerCase
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_LOWER_CASE'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_LOWER_CASE'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordNumber
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_NUMBER'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_NUMBER'
).
css
(
'color'
,
'#908a8a'
)
}
if
(
wordSpecialChar
(
password
))
{
$
(
'#rule_SECURITY_PASSWORD_SPECIAL_CHAR'
).
css
(
'color'
,
'green'
)
}
else
{
$
(
'#rule_SECURITY_PASSWORD_SPECIAL_CHAR'
).
css
(
'color'
,
'#908a8a'
)
}
}
// 最小长度
function
wordMinLength
(
word
,
minLength
)
{
//var minLength = {{ min_length }};
var
re
=
new
RegExp
(
"^(.{"
+
minLength
+
",})$"
);
return
word
.
match
(
re
)
}
// 大写字母
function
wordUpperCase
(
word
)
{
return
word
.
match
(
/
([
A-Z
]
+
)
/
)
}
// 小写字母
function
wordLowerCase
(
word
)
{
return
word
.
match
(
/
([
a-z
]
+
)
/
)
}
// 数字字符
function
wordNumber
(
word
)
{
return
word
.
match
(
/
([\d]
+
)
/
)
}
// 特殊字符
function
wordSpecialChar
(
word
)
{
return
word
.
match
(
/
[
`,~,!,@,#,
\$
,%,
\^
,&,
\*
,
\(
,
\)
,
\-
,_,=,
\+
,
\{
,
\}
,
\[
,
\]
,
\|
,
\\
,;,',:,",
\,
,
\.
,<,>,
\/
,
\?]
+/
)
}
// 显示弹窗密码规则
function
popoverPasswordRules
(
password_check_rules
,
$el
)
{
var
message
=
""
;
jQuery
.
each
(
password_check_rules
,
function
(
idx
,
rules
)
{
message
+=
"<li id="
+
rules
.
id
+
" style='list-style-type:none;'> <i class='fa fa-check-circle-o' style='margin-right:10px;' ></i>"
+
rules
.
label
+
"</li>"
;
});
//$('#id_password_rules').html(message);
$el
.
html
(
message
)
}
// 初始化弹窗popover
function
initPopover
(
$container
,
$progress
,
$idPassword
,
$el
,
password_check_rules
){
options
=
{};
// User Interface
options
.
ui
=
{
container
:
$container
,
viewports
:
{
progress
:
$progress
//errors: $('.popover-content')
},
showProgressbar
:
true
,
showVerdictsInsideProgressBar
:
true
};
$idPassword
.
pwstrength
(
options
);
popoverPasswordRules
(
password_check_rules
,
$el
);
}
apps/static/js/pwstrength-bootstrap.js
0 → 100755
View file @
ee35ca36
This diff is collapsed.
Click to expand it.
apps/templates/_base_create_update.html
View file @
ee35ca36
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
{% block custom_head_css_js %}
{% block custom_head_css_js %}
<link
href=
"{% static 'css/plugins/select2/select2.min.css' %}"
rel=
"stylesheet"
>
<link
href=
"{% static 'css/plugins/select2/select2.min.css' %}"
rel=
"stylesheet"
>
<script
src=
"{% static 'js/plugins/select2/select2.full.min.js' %}"
></script>
<script
src=
"{% static 'js/plugins/select2/select2.full.min.js' %}"
></script>
<script
type=
"text/javascript"
src=
"{% static 'js/pwstrength-bootstrap.js' %}"
></script>
{% block custom_head_css_js_create %} {% endblock %}
{% block custom_head_css_js_create %} {% endblock %}
{% endblock %}
{% endblock %}
...
...
apps/users/forms.py
View file @
ee35ca36
...
@@ -72,7 +72,7 @@ class UserCreateUpdateForm(forms.ModelForm):
...
@@ -72,7 +72,7 @@ class UserCreateUpdateForm(forms.ModelForm):
'data-placeholder'
:
_
(
'Join user groups'
)
'data-placeholder'
:
_
(
'Join user groups'
)
}
}
),
),
'otp_level'
:
forms
.
RadioSelect
()
'otp_level'
:
forms
.
RadioSelect
()
,
}
}
def
clean_public_key
(
self
):
def
clean_public_key
(
self
):
...
...
apps/users/templates/users/_user.html
View file @
ee35ca36
...
@@ -48,6 +48,7 @@
...
@@ -48,6 +48,7 @@
</div>
</div>
</div>
</div>
</form>
</form>
{% endblock %}
{% endblock %}
{% block custom_foot_js %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
...
...
apps/users/templates/users/reset_password.html
View file @
ee35ca36
...
@@ -11,6 +11,7 @@
...
@@ -11,6 +11,7 @@
{% include '_head_css_js.html' %}
{% include '_head_css_js.html' %}
<link
href=
"{% static "
css
/
jumpserver
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
jumpserver
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
jumpserver
.
js
"
%}"
></script>
<script
src=
"{% static "
js
/
jumpserver
.
js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static 'js/pwstrength-bootstrap.js' %}"
></script>
</head>
</head>
...
@@ -49,10 +50,20 @@
...
@@ -49,10 +50,20 @@
<p
class=
"red-fonts"
>
{{ errors }}
</p>
<p
class=
"red-fonts"
>
{{ errors }}
</p>
{% endif %}
{% endif %}
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"password"
placeholder=
"{% trans 'Password' %}"
required=
""
>
<input
type=
"password"
id=
"id_new_password"
class=
"form-control"
name=
"password"
placeholder=
"{% trans 'Password' %}"
required=
""
>
{# 密码popover #}
<div
id=
"container"
>
<div
class=
"popover fade bottom in"
role=
"tooltip"
id=
"popover777"
style=
" display: none; width:260px;"
>
<div
class=
"arrow"
style=
"left: 50%;"
></div>
<h3
class=
"popover-title"
style=
"display: none;"
></h3>
<h4>
{% trans 'Your password must satisfy' %}
</h4><div
id=
"id_password_rules"
style=
"color: #908a8a; margin-left:20px; font-size:15px;"
></div>
<h4
style=
"margin-top: 10px;"
>
{% trans 'Password strength' %}
</h4><div
id=
"id_progress"
></div>
<div
class=
"popover-content"
></div>
</div>
</div>
</div>
</div>
<div
class=
"form-group"
>
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"password-confirm"
placeholder=
"{% trans 'Password again' %}"
required=
""
>
<input
type=
"password"
id=
"id_confirm_password"
class=
"form-control"
name=
"password-confirm"
placeholder=
"{% trans 'Password again' %}"
required=
""
>
</div>
</div>
<button
type=
"submit"
class=
"btn btn-primary block full-width m-b"
>
{% trans "Setting" %}
</button>
<button
type=
"submit"
class=
"btn btn-primary block full-width m-b"
>
{% trans "Setting" %}
</button>
...
@@ -79,4 +90,33 @@
...
@@ -79,4 +90,33 @@
</body>
</body>
</html>
</html>
<script>
$
(
document
).
ready
(
function
()
{
// 密码强度校验
var
el
=
$
(
'#id_password_rules'
),
idPassword
=
$
(
'#id_new_password'
),
idPopover
=
$
(
'#popover777'
),
container
=
$
(
'#container'
),
progress
=
$
(
'#id_progress'
),
password_check_rules
=
{{
password_check_rules
|
safe
}},
minLength
=
{{
min_length
}},
top
=
146
,
left
=
170
;
// 初始化popover
initPopover
(
container
,
progress
,
idPassword
,
el
,
password_check_rules
);
// 监听事件
idPassword
.
on
(
'focus'
,
function
()
{
idPopover
.
css
(
'top'
,
top
);
idPopover
.
css
(
'left'
,
left
);
idPopover
.
css
(
'display'
,
'block'
);
});
idPassword
.
on
(
'blur'
,
function
()
{
idPopover
.
css
(
'display'
,
'none'
);
});
idPassword
.
on
(
'keyup'
,
function
(){
var
password
=
idPassword
.
val
();
checkPasswordRules
(
password
,
minLength
);
})
})
</script>
apps/users/templates/users/user_password_update.html
View file @
ee35ca36
...
@@ -7,6 +7,8 @@
...
@@ -7,6 +7,8 @@
<link
href=
"{% static "
css
/
plugins
/
cropper
/
cropper
.
min
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
plugins
/
cropper
/
cropper
.
min
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
plugins
/
sweetalert
/
sweetalert
.
css
"
%}"
rel=
"stylesheet"
>
<link
href=
"{% static "
css
/
plugins
/
sweetalert
/
sweetalert
.
css
"
%}"
rel=
"stylesheet"
>
<script
src=
"{% static "
js
/
plugins
/
sweetalert
/
sweetalert
.
min
.
js
"
%}"
></script>
<script
src=
"{% static "
js
/
plugins
/
sweetalert
/
sweetalert
.
min
.
js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static 'js/pwstrength-bootstrap.js' %}"
></script>
<script
src=
"{% static "
js
/
jumpserver
.
js
"
%}"
></script>
<style>
<style>
.crop
{
.crop
{
...
@@ -50,6 +52,16 @@
...
@@ -50,6 +52,16 @@
{% csrf_token %}
{% csrf_token %}
{% bootstrap_field form.old_password layout="horizontal" %}
{% bootstrap_field form.old_password layout="horizontal" %}
{% bootstrap_field form.new_password layout="horizontal" %}
{% bootstrap_field form.new_password layout="horizontal" %}
{# 密码popover #}
<div
id=
"container"
>
<div
class=
"popover fade bottom in"
role=
"tooltip"
id=
"popover777"
style=
" display: none; width:260px;"
>
<div
class=
"arrow"
style=
"left: 50%;"
></div>
<h3
class=
"popover-title"
style=
"display: none;"
></h3>
<h4>
{% trans 'Your password must satisfy' %}
</h4><div
id=
"id_password_rules"
style=
"color: #908a8a; margin-left:20px; font-size:15px;"
></div>
<h4
style=
"margin-top: 10px;"
>
{% trans 'Password strength' %}
</h4><div
id=
"id_progress"
></div>
<div
class=
"popover-content"
></div>
</div>
</div>
{% bootstrap_field form.confirm_password layout="horizontal" %}
{% bootstrap_field form.confirm_password layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
<div
class=
"hr-line-dashed"
></div>
...
@@ -71,5 +83,34 @@
...
@@ -71,5 +83,34 @@
{% block custom_foot_js %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/cropper/cropper.min.js' %}"
></script>
<script
src=
"{% static 'js/plugins/cropper/cropper.min.js' %}"
></script>
<script>
<script>
$
(
document
).
ready
(
function
()
{
var
el
=
$
(
'#id_password_rules'
),
idPassword
=
$
(
'#id_new_password'
),
idPopover
=
$
(
'#popover777'
),
container
=
$
(
'#container'
),
progress
=
$
(
'#id_progress'
),
password_check_rules
=
{{
password_check_rules
|
safe
}},
minLength
=
{{
min_length
}},
top
=
idPassword
.
offset
().
top
-
$
(
'.navbar'
).
outerHeight
(
true
)
-
$
(
'.page-heading'
).
outerHeight
(
true
)
-
10
+
34
,
left
=
377
;
// 初始化popover
initPopover
(
container
,
progress
,
idPassword
,
el
,
password_check_rules
);
// 监听事件
idPassword
.
on
(
'focus'
,
function
()
{
idPopover
.
css
(
'top'
,
top
);
idPopover
.
css
(
'left'
,
left
);
idPopover
.
css
(
'display'
,
'block'
);
});
idPassword
.
on
(
'blur'
,
function
()
{
idPopover
.
css
(
'display'
,
'none'
);
});
idPassword
.
on
(
'keyup'
,
function
(){
var
password
=
idPassword
.
val
();
checkPasswordRules
(
password
,
minLength
);
});
})
</script>
</script>
{% endblock %}
{% endblock %}
apps/users/templates/users/user_profile.html
View file @
ee35ca36
...
@@ -91,6 +91,9 @@
...
@@ -91,6 +91,9 @@
{% else %}
{% else %}
{% trans 'Disable' %}
{% trans 'Disable' %}
{% endif %}
{% endif %}
{% if mfa_setting %}
( {% trans 'Administrator Settings force MFA login' %} )
{% endif %}
</td>
</td>
</tr>
</tr>
<tr>
<tr>
...
@@ -152,7 +155,7 @@
...
@@ -152,7 +155,7 @@
<a
type=
"button"
class=
"btn btn-primary btn-xs"
style=
"width: 54px"
id=
""
<a
type=
"button"
class=
"btn btn-primary btn-xs"
style=
"width: 54px"
id=
""
href=
"
href=
"
{% if request.user.otp_enabled and request.user.otp_secret_key %}
{% if request.user.otp_enabled and request.user.otp_secret_key %}
{% if request.user.otp_force_enabled %}
{% if request.user.otp_force_enabled
or mfa_setting
%}
"
disabled
>
{% trans 'Disable' %}
"
disabled
>
{% trans 'Disable' %}
{% else %}
{% else %}
{% url 'users:user-otp-disable-authentication' %}
{% url 'users:user-otp-disable-authentication' %}
...
...
apps/users/templates/users/user_update.html
View file @
ee35ca36
...
@@ -4,5 +4,50 @@
...
@@ -4,5 +4,50 @@
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block password %}
{% block password %}
{% bootstrap_field form.password layout="horizontal" %}
{% bootstrap_field form.password layout="horizontal" %}
{# 密码popover #}
<div
id=
"container"
>
<div
class=
"popover fade bottom in"
role=
"tooltip"
id=
"popover777"
style=
" display: none; width:260px;"
>
<div
class=
"arrow"
style=
"left: 50%;"
></div>
<h3
class=
"popover-title"
style=
"display: none;"
></h3>
<h4>
{% trans 'Your password must satisfy' %}
</h4><div
id=
"id_password_rules"
style=
"color: #908a8a; margin-left:20px; font-size:15px;"
></div>
<h4
style=
"margin-top: 10px;"
>
{% trans 'Password strength' %}
</h4><div
id=
"id_progress"
></div>
<div
class=
"popover-content"
></div>
</div>
</div>
{% bootstrap_field form.public_key layout="horizontal" %}
{% bootstrap_field form.public_key layout="horizontal" %}
{% endblock %}
{% endblock %}
{% block custom_foot_js %}
{{ block.super }}
<script>
$
(
document
).
ready
(
function
(){
var
el
=
$
(
'#id_password_rules'
),
idPassword
=
$
(
'#id_password'
),
idPopover
=
$
(
'#popover777'
),
container
=
$
(
'#container'
),
progress
=
$
(
'#id_progress'
),
password_check_rules
=
{{
password_check_rules
|
safe
}},
minLength
=
{{
min_length
}},
top
=
idPassword
.
offset
().
top
-
$
(
'.navbar'
).
outerHeight
(
true
)
-
$
(
'.page-heading'
).
outerHeight
(
true
)
-
10
+
34
,
left
=
377
;
// 初始化popover
initPopover
(
container
,
progress
,
idPassword
,
el
,
password_check_rules
);
// 监听事件
idPassword
.
on
(
'focus'
,
function
()
{
idPopover
.
css
(
'top'
,
top
);
idPopover
.
css
(
'left'
,
left
);
idPopover
.
css
(
'display'
,
'block'
);
});
idPassword
.
on
(
'blur'
,
function
()
{
idPopover
.
css
(
'display'
,
'none'
);
});
idPassword
.
on
(
'keyup'
,
function
(){
var
password
=
idPassword
.
val
();
checkPasswordRules
(
password
,
minLength
);
});
})
</script>
{% endblock %}
apps/users/utils.py
View file @
ee35ca36
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
#
#
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
import
os
import
os
import
re
import
pyotp
import
pyotp
import
base64
import
base64
import
logging
import
logging
...
@@ -18,8 +19,11 @@ from django.core.cache import cache
...
@@ -18,8 +19,11 @@ from django.core.cache import cache
from
common.tasks
import
send_mail_async
from
common.tasks
import
send_mail_async
from
common.utils
import
reverse
,
get_object_or_none
from
common.utils
import
reverse
,
get_object_or_none
from
common.models
import
Setting
from
common.forms
import
SecuritySettingForm
from
.models
import
User
,
LoginLog
from
.models
import
User
,
LoginLog
logger
=
logging
.
getLogger
(
'jumpserver'
)
logger
=
logging
.
getLogger
(
'jumpserver'
)
...
@@ -271,3 +275,60 @@ def generate_otp_uri(request, issuer="Jumpserver"):
...
@@ -271,3 +275,60 @@ def generate_otp_uri(request, issuer="Jumpserver"):
def
check_otp_code
(
otp_secret_key
,
otp_code
):
def
check_otp_code
(
otp_secret_key
,
otp_code
):
totp
=
pyotp
.
TOTP
(
otp_secret_key
)
totp
=
pyotp
.
TOTP
(
otp_secret_key
)
return
totp
.
verify
(
otp_code
)
return
totp
.
verify
(
otp_code
)
def
get_password_check_rules
():
check_rules
=
[]
min_length
=
settings
.
DEFAULT_PASSWORD_MIN_LENGTH
min_name
=
'SECURITY_PASSWORD_MIN_LENGTH'
base_filed
=
SecuritySettingForm
.
base_fields
password_setting
=
Setting
.
objects
.
filter
(
name__startswith
=
'SECURITY_PASSWORD'
)
if
not
password_setting
:
# 用户还没有设置过密码校验规则
label
=
base_filed
.
get
(
min_name
)
.
label
label
+=
' '
+
str
(
min_length
)
+
_
(
'Bit'
)
id
=
'rule_'
+
min_name
rules
=
{
'id'
:
id
,
'label'
:
label
}
check_rules
.
append
(
rules
)
for
setting
in
password_setting
:
if
setting
.
cleaned_value
:
id
=
'rule_'
+
setting
.
name
label
=
base_filed
.
get
(
setting
.
name
)
.
label
if
setting
.
name
==
min_name
:
label
+=
str
(
setting
.
cleaned_value
)
+
_
(
'Bit'
)
min_length
=
setting
.
cleaned_value
rules
=
{
'id'
:
id
,
'label'
:
label
}
check_rules
.
append
(
rules
)
return
check_rules
,
min_length
def
check_password_rules
(
password
):
min_field_name
=
'SECURITY_PASSWORD_MIN_LENGTH'
upper_field_name
=
'SECURITY_PASSWORD_UPPER_CASE'
lower_field_name
=
'SECURITY_PASSWORD_LOWER_CASE'
number_field_name
=
'SECURITY_PASSWORD_NUMBER'
special_field_name
=
'SECURITY_PASSWORD_SPECIAL_CHAR'
min_length_setting
=
Setting
.
objects
.
filter
(
name
=
min_field_name
)
.
first
()
min_length
=
min_length_setting
.
value
if
min_length_setting
else
settings
.
DEFAULT_PASSWORD_MIN_LENGTH
password_setting
=
Setting
.
objects
.
filter
(
name__startswith
=
'SECURITY_PASSWORD'
)
if
not
password_setting
:
pattern
=
r"^.{"
+
str
(
min_length
)
+
",}$"
else
:
pattern
=
r"^"
for
setting
in
password_setting
:
if
setting
.
cleaned_value
and
setting
.
name
==
upper_field_name
:
pattern
+=
'(?=.*[A-Z])'
elif
setting
.
cleaned_value
and
setting
.
name
==
lower_field_name
:
pattern
+=
'(?=.*[a-z])'
elif
setting
.
cleaned_value
and
setting
.
name
==
number_field_name
:
pattern
+=
'(?=.*
\
d)'
elif
setting
.
cleaned_value
and
setting
.
name
==
special_field_name
:
pattern
+=
'(?=.*[`~!@#
\
$
%
\
^&
\
*
\
(
\
)-=_
\
+
\
[
\
]
\
{
\
}
\
|;:
\'
",
\
.<>
\
/
\
?])'
pattern
+=
'[a-zA-Z
\
d`~!@#
\
$
%
\
^&
\
*
\
(
\
)-=_
\
+
\
[
\
]
\
{
\
}
\
|;:
\'
",
\
.<>
\
/
\
?]'
match_obj
=
re
.
match
(
pattern
,
password
)
return
bool
(
match_obj
)
apps/users/views/login.py
View file @
ee35ca36
...
@@ -23,9 +23,10 @@ from django.conf import settings
...
@@ -23,9 +23,10 @@ from django.conf import settings
from
common.utils
import
get_object_or_none
from
common.utils
import
get_object_or_none
from
common.mixins
import
DatetimeSearchMixin
,
AdminUserRequiredMixin
from
common.mixins
import
DatetimeSearchMixin
,
AdminUserRequiredMixin
from
common.models
import
Setting
from
..models
import
User
,
LoginLog
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_user_or_tmp_user
,
set_tmp_user_to_cache
get_user_or_tmp_user
,
set_tmp_user_to_cache
,
get_password_check_rules
,
check_password_rules
from
..tasks
import
write_login_log_async
from
..tasks
import
write_login_log_async
from
..
import
forms
from
..
import
forms
...
@@ -81,17 +82,24 @@ class UserLoginView(FormView):
...
@@ -81,17 +82,24 @@ class UserLoginView(FormView):
def
get_success_url
(
self
):
def
get_success_url
(
self
):
user
=
get_user_or_tmp_user
(
self
.
request
)
user
=
get_user_or_tmp_user
(
self
.
request
)
if
user
.
otp_enabled
and
user
.
otp_secret_key
:
mfa_setting
=
Setting
.
objects
.
filter
(
name
=
'SECURITY_MFA_AUTH'
)
.
first
()
# 1,2 & T
if
mfa_setting
and
mfa_setting
.
cleaned_value
:
return
reverse
(
'users:login-otp'
)
if
user
.
otp_enabled
and
user
.
otp_secret_key
:
elif
user
.
otp_enabled
and
not
user
.
otp_secret_key
:
return
reverse
(
'users:login-otp'
)
# 1,2 & F
else
:
return
reverse
(
'users:user-otp-enable-authentication'
)
return
reverse
(
'users:user-otp-enable-authentication'
)
elif
not
user
.
otp_enabled
:
else
:
# 0 & T,F
if
user
.
otp_enabled
and
user
.
otp_secret_key
:
auth_login
(
self
.
request
,
user
)
# 1,2 & T
self
.
write_login_log
()
return
reverse
(
'users:login-otp'
)
return
redirect_user_first_login_or_index
(
self
.
request
,
self
.
redirect_field_name
)
elif
user
.
otp_enabled
and
not
user
.
otp_secret_key
:
# 1,2 & F
return
reverse
(
'users:user-otp-enable-authentication'
)
elif
not
user
.
otp_enabled
:
# 0 & T,F
auth_login
(
self
.
request
,
user
)
self
.
write_login_log
()
return
redirect_user_first_login_or_index
(
self
.
request
,
self
.
redirect_field_name
)
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
context
=
{
...
@@ -211,6 +219,10 @@ class UserResetPasswordView(TemplateView):
...
@@ -211,6 +219,10 @@ class UserResetPasswordView(TemplateView):
token
=
request
.
GET
.
get
(
'token'
)
token
=
request
.
GET
.
get
(
'token'
)
user
=
User
.
validate_reset_token
(
token
)
user
=
User
.
validate_reset_token
(
token
)
check_rules
,
min_length
=
get_password_check_rules
()
password_rules
=
{
'password_check_rules'
:
check_rules
,
'min_length'
:
min_length
}
kwargs
.
update
(
password_rules
)
if
not
user
:
if
not
user
:
kwargs
.
update
({
'errors'
:
_
(
'Token invalid or expired'
)})
kwargs
.
update
({
'errors'
:
_
(
'Token invalid or expired'
)})
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
return
super
()
.
get
(
request
,
*
args
,
**
kwargs
)
...
@@ -227,6 +239,13 @@ class UserResetPasswordView(TemplateView):
...
@@ -227,6 +239,13 @@ class UserResetPasswordView(TemplateView):
if
not
user
:
if
not
user
:
return
self
.
get
(
request
,
errors
=
_
(
'Token invalid or expired'
))
return
self
.
get
(
request
,
errors
=
_
(
'Token invalid or expired'
))
is_ok
=
check_password_rules
(
password
)
if
not
is_ok
:
return
self
.
get
(
request
,
errors
=
_
(
'* Your password does not meet the requirements'
)
)
user
.
reset_password
(
password
)
user
.
reset_password
(
password
)
return
HttpResponseRedirect
(
reverse
(
'users:reset-password-success'
))
return
HttpResponseRedirect
(
reverse
(
'users:reset-password-success'
))
...
...
apps/users/views/user.py
View file @
ee35ca36
...
@@ -33,9 +33,10 @@ from django.contrib.auth import logout as auth_logout
...
@@ -33,9 +33,10 @@ from django.contrib.auth import logout as auth_logout
from
common.const
import
create_success_msg
,
update_success_msg
from
common.const
import
create_success_msg
,
update_success_msg
from
common.mixins
import
JSONResponseMixin
from
common.mixins
import
JSONResponseMixin
from
common.utils
import
get_logger
,
get_object_or_none
,
is_uuid
,
ssh_key_gen
from
common.utils
import
get_logger
,
get_object_or_none
,
is_uuid
,
ssh_key_gen
from
common.models
import
Setting
from
..
import
forms
from
..
import
forms
from
..models
import
User
,
UserGroup
from
..models
import
User
,
UserGroup
from
..utils
import
AdminUserRequiredMixin
,
generate_otp_uri
,
check_otp_code
,
get_user_or_tmp_user
from
..utils
import
AdminUserRequiredMixin
,
generate_otp_uri
,
check_otp_code
,
get_user_or_tmp_user
,
get_password_check_rules
,
check_password_rules
from
..signals
import
post_user_create
from
..signals
import
post_user_create
from
..tasks
import
write_login_log_async
from
..tasks
import
write_login_log_async
...
@@ -96,10 +97,27 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
...
@@ -96,10 +97,27 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
success_message
=
update_success_msg
success_message
=
update_success_msg
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Update user'
)}
check_rules
,
min_length
=
get_password_check_rules
()
context
=
{
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Update user'
),
'password_check_rules'
:
check_rules
,
'min_length'
:
min_length
}
kwargs
.
update
(
context
)
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
return
super
()
.
get_context_data
(
**
kwargs
)
def
form_valid
(
self
,
form
):
password
=
form
.
cleaned_data
.
get
(
'password'
)
is_ok
=
check_password_rules
(
password
)
if
not
is_ok
:
form
.
add_error
(
"password"
,
_
(
"* Your password does not meet the requirements"
)
)
return
self
.
form_invalid
(
form
)
return
super
()
.
form_valid
(
form
)
class
UserBulkUpdateView
(
AdminUserRequiredMixin
,
TemplateView
):
class
UserBulkUpdateView
(
AdminUserRequiredMixin
,
TemplateView
):
model
=
User
model
=
User
...
@@ -318,8 +336,11 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
...
@@ -318,8 +336,11 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
template_name
=
'users/user_profile.html'
template_name
=
'users/user_profile.html'
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
mfa_setting
=
Setting
.
objects
.
filter
(
name
=
'SECURITY_MFA_AUTH'
)
.
first
()
context
=
{
context
=
{
'action'
:
_
(
'Profile'
),
'action'
:
_
(
'Profile'
),
'mfa_setting'
:
mfa_setting
.
cleaned_value
if
mfa_setting
else
False
,
}
}
kwargs
.
update
(
context
)
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
@@ -353,9 +374,12 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
...
@@ -353,9 +374,12 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
return
self
.
request
.
user
return
self
.
request
.
user
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
check_rules
,
min_length
=
get_password_check_rules
()
context
=
{
context
=
{
'app'
:
_
(
'Users'
),
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Password update'
),
'action'
:
_
(
'Password update'
),
'password_check_rules'
:
check_rules
,
'min_length'
:
min_length
,
}
}
kwargs
.
update
(
context
)
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
return
super
()
.
get_context_data
(
**
kwargs
)
...
@@ -364,6 +388,17 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
...
@@ -364,6 +388,17 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
auth_logout
(
self
.
request
)
auth_logout
(
self
.
request
)
return
super
()
.
get_success_url
()
return
super
()
.
get_success_url
()
def
form_valid
(
self
,
form
):
password
=
form
.
cleaned_data
.
get
(
'new_password'
)
is_ok
=
check_password_rules
(
password
)
if
not
is_ok
:
form
.
add_error
(
"new_password"
,
_
(
"* Your password does not meet the requirements"
)
)
return
self
.
form_invalid
(
form
)
return
super
()
.
form_valid
(
form
)
class
UserPublicKeyUpdateView
(
LoginRequiredMixin
,
UpdateView
):
class
UserPublicKeyUpdateView
(
LoginRequiredMixin
,
UpdateView
):
template_name
=
'users/user_pubkey_update.html'
template_name
=
'users/user_pubkey_update.html'
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment