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
5c17b1a7
Unverified
Commit
5c17b1a7
authored
Apr 25, 2018
by
老广
Committed by
GitHub
Apr 25, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1240 from jumpserver/dev
首次登陆免验证码,首次登录信息变更
parents
e865484a
ea2863a5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
330 additions
and
89 deletions
+330
-89
__init__.py
apps/__init__.py
+2
-2
asset_create.html
apps/assets/templates/assets/asset_create.html
+13
-1
asset.py
apps/assets/views/asset.py
+4
-1
django.mo
apps/i18n/zh/LC_MESSAGES/django.mo
+0
-0
django.po
apps/i18n/zh/LC_MESSAGES/django.po
+0
-0
jquery.steps.css
apps/static/css/plugins/steps/jquery.steps.css
+0
-1
_footer.html
apps/templates/_footer.html
+2
-3
_message.html
apps/templates/_message.html
+5
-0
api.py
apps/users/api.py
+2
-2
forms.py
apps/users/forms.py
+53
-3
user.py
apps/users/models/user.py
+45
-14
_base_otp.html
apps/users/templates/users/_base_otp.html
+5
-7
_user.html
apps/users/templates/users/_user.html
+4
-1
first_login.html
apps/users/templates/users/first_login.html
+100
-41
first_login_done.html
apps/users/templates/users/first_login_done.html
+4
-1
login.html
apps/users/templates/users/login.html
+1
-1
user_create.html
apps/users/templates/users/user_create.html
+3
-4
user_list.html
apps/users/templates/users/user_list.html
+3
-0
user_otp_authentication.html
apps/users/templates/users/user_otp_authentication.html
+0
-1
user_update.html
apps/users/templates/users/user_update.html
+0
-1
login.py
apps/users/views/login.py
+29
-5
upgrade.sh
utils/upgrade.sh
+55
-0
No files found.
apps/__init__.py
View file @
5c17b1a7
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
#
__version__
=
"1.2.
0
"
__version__
=
"1.2.
1
"
apps/assets/templates/assets/asset_create.html
View file @
5c17b1a7
...
...
@@ -15,9 +15,9 @@
{% csrf_token %}
<h3>
{% trans 'Basic' %}
</h3>
{% bootstrap_field form.hostname layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.ip layout="horizontal" %}
{% bootstrap_field form.port layout="horizontal" %}
{% bootstrap_field form.platform layout="horizontal" %}
{% bootstrap_field form.public_ip layout="horizontal" %}
{% bootstrap_field form.domain layout="horizontal" %}
...
...
@@ -85,6 +85,17 @@ $(document).ready(function () {
allowClear
:
true
,
templateSelection
:
format
});
$
(
"#id_platform"
).
change
(
function
(){
var
platform
=
$
(
"#id_platform option:selected"
).
text
();
var
port
=
22
;
if
(
platform
===
'Windows'
){
port
=
3389
;
}
if
(
platform
===
'Other'
){
port
=
null
;
}
$
(
"#id_port"
).
val
(
port
);
});
})
</script>
{% endblock %}
\ No newline at end of file
apps/assets/views/asset.py
View file @
5c17b1a7
...
...
@@ -304,7 +304,10 @@ class BulkImportAssetView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
if
v
!=
''
:
asset_dict
[
k
]
=
v
asset
=
get_object_or_none
(
Asset
,
id
=
asset_dict
.
pop
(
'id'
,
0
))
asset
=
None
asset_id
=
asset_dict
.
pop
(
'id'
,
None
)
if
asset_id
:
asset
=
get_object_or_none
(
Asset
,
id
=
asset_id
)
if
not
asset
:
try
:
if
len
(
Asset
.
objects
.
filter
(
hostname
=
asset_dict
.
get
(
'hostname'
))):
...
...
apps/i18n/zh/LC_MESSAGES/django.mo
View file @
5c17b1a7
No preview for this file type
apps/i18n/zh/LC_MESSAGES/django.po
View file @
5c17b1a7
This diff is collapsed.
Click to expand it.
apps/static/css/plugins/steps/jquery.steps.css
View file @
5c17b1a7
...
...
@@ -220,7 +220,6 @@
position
:
relative
;
display
:
block
;
text-align
:
right
;
width
:
100%
;
}
.wizard.vertical
>
.actions
...
...
apps/templates/_footer.html
View file @
5c17b1a7
<div
class=
"footer fixed"
>
<div
class=
"pull-right"
>
Version
<strong>
1.2.
0
-{% include '_build.html' %}
</strong>
GPLv2.
Version
<strong>
1.2.
1
-{% include '_build.html' %}
</strong>
GPLv2.
<img
style=
"display: none"
src=
"http://www.jumpserver.org/img/evaluate_avatar1.jpg"
>
</div>
<div>
<strong>
Copyright
</strong>
北京堆栈科技有限公司
©
2014-2018
</div>
</div>
\ No newline at end of file
</div>
apps/templates/_message.html
View file @
5c17b1a7
...
...
@@ -6,9 +6,12 @@
{% blocktrans %}
Your information was incomplete. Please click
<a
href=
"{{ first_login_url }}"
>
this link
</a>
to complete your information.
{% endblocktrans %}
<button
aria-hidden=
"true"
data-dismiss=
"alert"
class=
"close"
type=
"button"
style=
"outline: none;"
>
×
</button>
</div>
{% endif %}
{% endblock %}
{% block update_public_key_message %}
{% if request.user.is_authenticated and not request.user.is_public_key_valid and not request.COOKIE.close_public_key_msg != '1' %}
<div
class=
"alert alert-danger help-message alert-dismissable"
>
...
...
@@ -25,7 +28,9 @@
{% for message in messages %}
<div
class=
"alert alert-{{ message.tags }} help-message"
>
{{ message|safe }}
<button
aria-hidden=
"true"
data-dismiss=
"alert"
class=
"close"
type=
"button"
style=
"outline: none;"
>
×
</button>
</div>
{% endfor %}
{% endif %}
...
...
apps/users/api.py
View file @
5c17b1a7
...
...
@@ -153,7 +153,7 @@ class UserOtpAuthApi(APIView):
return
Response
({
'msg'
:
'请先进行用户名和密码验证'
},
status
=
401
)
if
not
check_otp_code
(
user
.
otp_secret_key
,
otp_code
):
return
Response
({
'msg'
:
'
otp
认证失败'
},
status
=
401
)
return
Response
({
'msg'
:
'
MFA
认证失败'
},
status
=
401
)
token
=
generate_token
(
request
,
user
)
self
.
write_login_log
(
request
,
user
)
...
...
@@ -204,7 +204,7 @@ class UserAuthApi(APIView):
return
Response
(
{
'code'
:
101
,
'msg'
:
'请携带seed值,进行
OTP
二次认证'
,
'msg'
:
'请携带seed值,进行
MFA
二次认证'
,
'otp_url'
:
reverse
(
'api-users:user-otp-auth'
),
'seed'
:
seed
,
'user'
:
self
.
serializer_class
(
user
)
.
data
...
...
apps/users/forms.py
View file @
5c17b1a7
...
...
@@ -15,6 +15,14 @@ class UserLoginForm(AuthenticationForm):
label
=
_
(
'Password'
),
widget
=
forms
.
PasswordInput
,
max_length
=
128
,
strip
=
False
)
class
UserLoginCaptchaForm
(
AuthenticationForm
):
username
=
forms
.
CharField
(
label
=
_
(
'Username'
),
max_length
=
100
)
password
=
forms
.
CharField
(
label
=
_
(
'Password'
),
widget
=
forms
.
PasswordInput
,
max_length
=
128
,
strip
=
False
)
captcha
=
CaptchaField
()
...
...
@@ -27,7 +35,7 @@ class UserCheckPasswordForm(forms.Form):
class
UserCheckOtpCodeForm
(
forms
.
Form
):
otp_code
=
forms
.
CharField
(
label
=
_
(
'MFA
_
code'
),
max_length
=
6
)
otp_code
=
forms
.
CharField
(
label
=
_
(
'MFA
code'
),
max_length
=
6
)
class
UserCreateUpdateForm
(
forms
.
ModelForm
):
...
...
@@ -36,7 +44,10 @@ class UserCreateUpdateForm(forms.ModelForm):
label
=
_
(
'Password'
),
widget
=
forms
.
PasswordInput
,
max_length
=
128
,
strip
=
False
,
required
=
False
,
)
role
=
forms
.
ChoiceField
(
choices
=
role_choices
,
required
=
True
,
initial
=
User
.
ROLE_USER
,
label
=
_
(
"Role"
))
role
=
forms
.
ChoiceField
(
choices
=
role_choices
,
required
=
True
,
initial
=
User
.
ROLE_USER
,
label
=
_
(
"Role"
)
)
public_key
=
forms
.
CharField
(
label
=
_
(
'ssh public key'
),
max_length
=
5000
,
required
=
False
,
widget
=
forms
.
Textarea
(
attrs
=
{
'placeholder'
:
_
(
'ssh-rsa AAAA...'
)}),
...
...
@@ -47,7 +58,7 @@ class UserCreateUpdateForm(forms.ModelForm):
model
=
User
fields
=
[
'username'
,
'name'
,
'email'
,
'groups'
,
'wechat'
,
'phone'
,
'role'
,
'date_expired'
,
'comment'
,
'phone'
,
'role'
,
'date_expired'
,
'comment'
,
'otp_level'
]
help_texts
=
{
'username'
:
'* required'
,
...
...
@@ -61,6 +72,7 @@ class UserCreateUpdateForm(forms.ModelForm):
'data-placeholder'
:
_
(
'Join user groups'
)
}
),
'otp_level'
:
forms
.
RadioSelect
()
}
def
clean_public_key
(
self
):
...
...
@@ -77,11 +89,15 @@ class UserCreateUpdateForm(forms.ModelForm):
def
save
(
self
,
commit
=
True
):
password
=
self
.
cleaned_data
.
get
(
'password'
)
otp_level
=
self
.
cleaned_data
.
get
(
'otp_level'
)
public_key
=
self
.
cleaned_data
.
get
(
'public_key'
)
user
=
super
()
.
save
(
commit
=
commit
)
if
password
:
user
.
set_password
(
password
)
user
.
save
()
if
otp_level
:
user
.
otp_level
=
otp_level
user
.
save
()
if
public_key
:
user
.
public_key
=
public_key
user
.
save
()
...
...
@@ -105,6 +121,39 @@ class UserProfileForm(forms.ModelForm):
UserProfileForm
.
verbose_name
=
_
(
"Profile"
)
class
UserMFAForm
(
forms
.
ModelForm
):
mfa_description
=
_
(
'Tip: when enabled, '
'you will enter the MFA binding process the next time you log in. '
'you can also directly bind in '
'"personal information -> quick modification -> change MFA Settings"!'
)
class
Meta
:
model
=
User
fields
=
[
'otp_level'
]
widgets
=
{
'otp_level'
:
forms
.
RadioSelect
()}
help_texts
=
{
'otp_level'
:
_
(
'* Enable MFA authentication '
'to make the account more secure.'
),
}
UserMFAForm
.
verbose_name
=
_
(
"MFA"
)
class
UserFirstLoginFinishForm
(
forms
.
Form
):
finish_description
=
_
(
'In order to protect you and your company, '
'please keep your account, '
'password and key sensitive information properly. '
'(for example: setting complex password, enabling MFA authentication)'
)
UserFirstLoginFinishForm
.
verbose_name
=
_
(
"Finish"
)
class
UserPasswordForm
(
forms
.
Form
):
old_password
=
forms
.
CharField
(
max_length
=
128
,
widget
=
forms
.
PasswordInput
,
...
...
@@ -147,6 +196,7 @@ class UserPasswordForm(forms.Form):
class
UserPublicKeyForm
(
forms
.
Form
):
pubkey_description
=
_
(
'Automatically configure and download the SSH key'
)
public_key
=
forms
.
CharField
(
label
=
_
(
'ssh public key'
),
max_length
=
5000
,
required
=
False
,
widget
=
forms
.
Textarea
(
attrs
=
{
'placeholder'
:
_
(
'ssh-rsa AAAA...'
)}),
...
...
apps/users/models/user.py
View file @
5c17b1a7
...
...
@@ -36,23 +36,52 @@ class User(AbstractUser):
(
2
,
_
(
"Force enable"
)),
)
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
username
=
models
.
CharField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Username'
))
username
=
models
.
CharField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Username'
)
)
name
=
models
.
CharField
(
max_length
=
128
,
verbose_name
=
_
(
'Name'
))
email
=
models
.
EmailField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Email'
))
groups
=
models
.
ManyToManyField
(
'users.UserGroup'
,
related_name
=
'users'
,
blank
=
True
,
verbose_name
=
_
(
'User group'
))
role
=
models
.
CharField
(
choices
=
ROLE_CHOICES
,
default
=
'User'
,
max_length
=
10
,
blank
=
True
,
verbose_name
=
_
(
'Role'
))
avatar
=
models
.
ImageField
(
upload_to
=
"avatar"
,
null
=
True
,
verbose_name
=
_
(
'Avatar'
))
wechat
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Wechat'
))
phone
=
models
.
CharField
(
max_length
=
20
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Phone'
))
otp_level
=
models
.
SmallIntegerField
(
default
=
0
,
choices
=
OTP_LEVEL_CHOICES
,
verbose_name
=
_
(
'Enable OTP'
))
email
=
models
.
EmailField
(
max_length
=
128
,
unique
=
True
,
verbose_name
=
_
(
'Email'
)
)
groups
=
models
.
ManyToManyField
(
'users.UserGroup'
,
related_name
=
'users'
,
blank
=
True
,
verbose_name
=
_
(
'User group'
)
)
role
=
models
.
CharField
(
choices
=
ROLE_CHOICES
,
default
=
'User'
,
max_length
=
10
,
blank
=
True
,
verbose_name
=
_
(
'Role'
)
)
avatar
=
models
.
ImageField
(
upload_to
=
"avatar"
,
null
=
True
,
verbose_name
=
_
(
'Avatar'
)
)
wechat
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
verbose_name
=
_
(
'Wechat'
)
)
phone
=
models
.
CharField
(
max_length
=
20
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Phone'
)
)
otp_level
=
models
.
SmallIntegerField
(
default
=
0
,
choices
=
OTP_LEVEL_CHOICES
,
verbose_name
=
_
(
'MFA'
)
)
_otp_secret_key
=
models
.
CharField
(
max_length
=
128
,
blank
=
True
,
null
=
True
)
# Todo: Auto generate key, let user download
_private_key
=
models
.
CharField
(
max_length
=
5000
,
blank
=
True
,
verbose_name
=
_
(
'Private key'
))
_public_key
=
models
.
CharField
(
max_length
=
5000
,
blank
=
True
,
verbose_name
=
_
(
'Public key'
))
comment
=
models
.
TextField
(
max_length
=
200
,
blank
=
True
,
verbose_name
=
_
(
'Comment'
))
_private_key
=
models
.
CharField
(
max_length
=
5000
,
blank
=
True
,
verbose_name
=
_
(
'Private key'
)
)
_public_key
=
models
.
CharField
(
max_length
=
5000
,
blank
=
True
,
verbose_name
=
_
(
'Public key'
)
)
comment
=
models
.
TextField
(
max_length
=
200
,
blank
=
True
,
verbose_name
=
_
(
'Comment'
)
)
is_first_login
=
models
.
BooleanField
(
default
=
True
)
date_expired
=
models
.
DateTimeField
(
default
=
date_expired_default
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Date expired'
))
created_by
=
models
.
CharField
(
max_length
=
30
,
default
=
''
,
verbose_name
=
_
(
'Created by'
))
date_expired
=
models
.
DateTimeField
(
default
=
date_expired_default
,
blank
=
True
,
null
=
True
,
verbose_name
=
_
(
'Date expired'
)
)
created_by
=
models
.
CharField
(
max_length
=
30
,
default
=
''
,
verbose_name
=
_
(
'Created by'
)
)
def
__str__
(
self
):
return
'{0.name}({0.username})'
.
format
(
self
)
...
...
@@ -213,7 +242,9 @@ 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
)
return
signer
.
sign_t
(
{
'reset'
:
str
(
self
.
id
),
'email'
:
self
.
email
},
expires_in
=
3600
)
@property
def
otp_enabled
(
self
):
...
...
apps/users/templates/users/_base_otp.html
View file @
5c17b1a7
...
...
@@ -74,13 +74,11 @@
</article>
<footer>
<div
class=
""
style=
"margin-top: 100px;"
>
{% include '_copyright.html' %}
</div>
</footer>
<footer>
<div
class=
""
style=
"margin-top: 100px;"
>
{% include '_copyright.html' %}
</div>
</footer>
</body>
</html>
...
...
apps/users/templates/users/_user.html
View file @
5c17b1a7
...
...
@@ -17,7 +17,10 @@
{% bootstrap_field form.groups layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
{% block password %} {% endblock %}
<h3>
{% trans 'Auth' %}
</h3>
{% block password %}{% endblock %}
{% bootstrap_field form.otp_level layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Security and Role' %}
</h3>
...
...
apps/users/templates/users/first_login.html
View file @
5c17b1a7
...
...
@@ -9,6 +9,7 @@
<link
href=
"{% static 'css/plugins/steps/jquery.steps.css' %}"
rel=
"stylesheet"
>
{% endblock %}
{% block first_login_message %}{% endblock %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
...
...
@@ -27,58 +28,116 @@
</div>
<div
class=
"ibox-content"
>
<div
class=
"wizard"
>
<div
class=
"steps clearfix"
>
<ul
role=
"tablist"
>
{% for step in wizard.steps.all %}
<li
role=
"tab"
class=
"{% ifequal step wizard.steps.first %}first{% endifequal %} {% ifequal step wizard.steps.current %}current{% else %}disabled{% endifequal %} {% ifequal step wizard.steps.last %}last{% endifequal %}"
aria-disabled=
"false"
aria-selected=
"true"
>
<a
href=
"javascript:void(0)"
><span
class=
"number"
>
{% trans 'Step' %} {{ step }}
</span></a>
</li>
{% endfor >%}
</ul>
</div>
<div
class=
"content clearfix"
>
<form
action=
""
method=
"post"
class=
"form col-lg-8 p-m"
id=
"fl_form"
>
{% csrf_token %}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{% bootstrap_form form %}
{% endfor %}
{% else %}
{% bootstrap_form wizard.form %}
{% endif %}
</form>
</div>
<div
class=
"steps clearfix"
>
<ul
role=
"tablist"
>
{% for step in wizard.steps.all %}
<li
role=
"tab"
class=
"{% ifequal step wizard.steps.first %}first{% endifequal %} {% ifequal step wizard.steps.current %}current{% else %}disabled{% endifequal %} {% ifequal step wizard.steps.last %}last{% endifequal %}"
aria-disabled=
"false"
aria-selected=
"true"
>
<a
class=
"fl_goto"
name=
"wizard_goto_step"
data-goto=
"{{ step }}"
>
<span
class=
"number"
>
{% ifequal step '0' %}
1. {% trans "Profile" %}
{% endifequal %}
{% ifequal step '1' %}
2. {% trans "Public key" %}
{% endifequal %}
{% ifequal step '2' %}
3. {% trans "MFA" %}
{% endifequal %}
{% ifequal step '3' %}
4. {% trans "Finish" %}
{% endifequal %}
</span>
</a>
</li>
{% endfor >%}
</ul>
</div>
<div
class=
"content clearfix"
style=
"background-color: #eee; border-radius:5px;"
>
<div
class=
"row"
>
<form
action=
""
method=
"post"
class=
"form col-lg-8 p-m"
id=
"fl_form"
style=
"padding-left: 40px;"
>
{% csrf_token %}
{{ wizard.management_form }}
{#{% if wizard.form.forms %}#}
{#{{ wizard.form.management_form }}#}
{#{% for form in wizard.form %}#}
{#{% bootstrap_form form %}#}
{#{% endfor %}#}
{#{% else %}#}
{#{% endif %}#}
{% if form.finish_description %}
<b>
{{ form.finish_description }}
</b>
<br>
<input
type=
"checkbox"
id=
"acceptTerms"
>
<label
for=
"acceptTerms"
style=
"margin-top:20px"
>
{% trans "I agree with the terms and conditions." %}
</label>
{% endif %}
{% bootstrap_form wizard.form %}
{% if form.mfa_description %}
<b>
{{ form.mfa_description }}
</b>
{% endif %}
{% if form.pubkey_description %}
<span>
或者:
</span>
<a
type=
"button"
id=
"btn-reset-pubkey"
>
{{ form.pubkey_description }}
</a>
{% endif %}
</form>
<div
class=
"col-lg-4"
>
<div
class=
"text-center"
>
<div
style=
"margin-top: 20px"
>
<i
class=
"fa fa-sign-in"
style=
"font-size: 180px;color: #e5e5e5 "
></i>
</div>
</div>
</div>
</div>
</div>
<div
class=
"actions clearfix"
>
<ul>
{% if wizard.steps.prev %}
<li><a
class=
"fl_goto"
name=
"wizard_goto_step"
data-goto=
"{{ wizard.steps.prev }}"
>
{% trans "Previous" %}
</a></li>
{% endif %}
{% if wizard.steps.next %}
<li><a
class=
"fl_goto"
name=
"wizard_goto_step"
data-goto=
"{{ wizard.steps.next }}"
>
{% trans "Next" %}
</a></li>
{% endif %}
<li><a
id=
"fl_submit"
>
{% trans "Submit" %}
</a></li>
</ul>
<ul>
{% if wizard.steps.prev %}
<li><a
class=
"fl_goto"
name=
"wizard_goto_step"
data-goto=
"{{ wizard.steps.prev }}"
>
{% trans "Previous" %}
</a></li>
{% endif %}
{#{% if wizard.steps.next %}#}
{#
<li><a
class=
"fl_goto"
name=
"wizard_goto_step"
data-goto=
"{{ wizard.steps.next }}"
>
{% trans "Next" %}
</a></li>
#}
{#{% endif %}#}
{#
<li><a
id=
"fl_submit"
>
{% trans "Submit" %}
</a></li>
#}
{#将原来的下一页-替换为提交;修复 每页都提交,最后才能成功问题#}
{% if wizard.steps.next %}
<li><a
id=
"fl_submit"
>
{% trans "Next" %}
</a></li>
{% else %}
<li><a
id=
"fl_submit"
style=
"width:66px;text-align: center;"
>
{% trans "Finish" %}
</a></li>
{% endif %}
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
on
(
'click'
,
".fl_goto"
,
function
(){
var
$form
=
$
(
'#fl_form'
);
$
(
'<input />'
,
{
'name'
:
'wizard_goto_step'
,
'value'
:
$
(
this
).
data
(
'goto'
),
'type'
:
'hidden'
}).
appendTo
(
$form
);
$form
.
submit
();
return
false
;
}).
on
(
'click'
,
'#fl_submit'
,
function
(){
$
(
'#fl_form'
).
submit
();
return
false
;
$
(
'#id_2-otp_level div'
).
eq
(
2
).
css
(
'display'
,
'none'
);
$
(
document
).
on
(
'click'
,
".fl_goto"
,
function
(){
var
$form
=
$
(
'#fl_form'
);
$
(
'<input />'
,
{
'name'
:
'wizard_goto_step'
,
'value'
:
$
(
this
).
data
(
'goto'
),
'type'
:
'hidden'
}).
appendTo
(
$form
);
$form
.
submit
();
return
false
;
}).
on
(
'click'
,
'#fl_submit'
,
function
(){
$
(
'#fl_form'
).
submit
();
return
false
;
}).
on
(
'click'
,
'#btn-reset-pubkey'
,
function
()
{
var
the_url
=
'{% url "users:user-pubkey-generate" %}'
;
window
.
open
(
the_url
,
"_blank"
)
})
</script>
{% endblock %}
apps/users/templates/users/first_login_done.html
View file @
5c17b1a7
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% load bootstrap3 %}
{% load bootstrap3 %}
{% block custom_head_css_js %}
...
...
@@ -9,6 +9,7 @@
<link
href=
"{% static 'css/plugins/steps/jquery.steps.css' %}"
rel=
"stylesheet"
>
{% endblock %}
{% block first_login_message %}{% endblock %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
...
...
@@ -36,6 +37,8 @@
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
on
(
'click'
,
".fl_goto"
,
function
(){
...
...
apps/users/templates/users/login.html
View file @
5c17b1a7
...
...
@@ -53,7 +53,7 @@
{% endif %}
{% endif %}
<div
class=
"form-group"
>
<input
type=
"text"
class=
"form-control"
name=
"{{ form.username.html_name }}"
placeholder=
"{% trans 'Username' %}"
required=
""
>
<input
type=
"text"
class=
"form-control"
name=
"{{ form.username.html_name }}"
placeholder=
"{% trans 'Username' %}"
required=
""
value=
"{% if form.username.value %}{{ form.username.value }}{% endif %}"
>
</div>
<div
class=
"form-group"
>
<input
type=
"password"
class=
"form-control"
name=
"{{ form.password.html_name }}"
placeholder=
"{% trans 'Password' %}"
required=
""
>
...
...
apps/users/templates/users/user_create.html
View file @
5c17b1a7
...
...
@@ -6,11 +6,11 @@
{# {% bootstrap_field form.username layout="horizontal" %}#}
{#{% endblock %}#}
{% block password %}
<h3>
{% trans 'Auth' %}
</h3>
<div
class=
"form-group"
>
<label
class=
"col-sm-2 control-label"
>
{% trans 'Password' %}
</label>
<div
class=
"col-sm-8 controls"
>
<div
class=
"col-sm-8 controls"
style=
"margin-top: 8px;"
>
{% trans 'Reset link will be generated and sent to the user. ' %}
</div>
</div>
{% endblock %}
\ No newline at end of file
{% endblock %}
apps/users/templates/users/user_list.html
View file @
5c17b1a7
...
...
@@ -107,6 +107,9 @@ $(document).ready(function(){
$
(
'.btn_export'
).
click
(
function
()
{
var
users
=
[];
var
rows
=
table
.
rows
(
'.selected'
).
data
();
if
(
rows
.
length
===
0
){
rows
=
table
.
rows
().
data
();
}
$
.
each
(
rows
,
function
(
index
,
obj
)
{
users
.
push
(
obj
.
id
)
});
...
...
apps/users/templates/users/user_otp_authentication.html
View file @
5c17b1a7
...
...
@@ -27,7 +27,6 @@
$
(
function
(){
$
(
'.change-color li'
).
eq
(
2
).
remove
();
$
(
'.change-color li:eq(1) div'
).
eq
(
1
).
html
(
'解绑MFA'
)
})
...
...
apps/users/templates/users/user_update.html
View file @
5c17b1a7
...
...
@@ -3,7 +3,6 @@
{% load bootstrap3 %}
{% block user_template_title %}{% trans "Update user" %}{% endblock %}
{% block password %}
<h3>
{% trans 'Auth' %}
</h3>
{% bootstrap_field form.password layout="horizontal" %}
{% bootstrap_field form.public_key layout="horizontal" %}
{% endblock %}
apps/users/views/login.py
View file @
5c17b1a7
...
...
@@ -2,6 +2,7 @@
from
__future__
import
unicode_literals
import
os
from
django.core.cache
import
cache
from
django.shortcuts
import
render
from
django.contrib.auth
import
login
as
auth_login
,
logout
as
auth_logout
from
django.contrib.auth.mixins
import
LoginRequiredMixin
...
...
@@ -43,7 +44,9 @@ __all__ = [
class
UserLoginView
(
FormView
):
template_name
=
'users/login.html'
form_class
=
forms
.
UserLoginForm
form_class_captcha
=
forms
.
UserLoginCaptchaForm
redirect_field_name
=
'next'
key_prefix
=
"_LOGIN_INVALID_{}"
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
if
request
.
user
.
is_staff
:
...
...
@@ -58,6 +61,21 @@ class UserLoginView(FormView):
set_tmp_user_to_cache
(
self
.
request
,
form
.
get_user
())
return
redirect
(
self
.
get_success_url
())
def
form_invalid
(
self
,
form
):
ip
=
get_login_ip
(
self
.
request
)
cache
.
set
(
self
.
key_prefix
.
format
(
ip
),
1
,
3600
)
old_form
=
form
form
=
self
.
form_class_captcha
(
data
=
form
.
data
)
form
.
_errors
=
old_form
.
errors
return
super
()
.
form_invalid
(
form
)
def
get_form_class
(
self
):
ip
=
get_login_ip
(
self
.
request
)
if
cache
.
get
(
self
.
key_prefix
.
format
(
ip
)):
return
self
.
form_class_captcha
else
:
return
self
.
form_class
def
get_success_url
(
self
):
user
=
get_user_or_tmp_user
(
self
.
request
)
...
...
@@ -166,8 +184,7 @@ class UserForgotPasswordSendmailSuccessView(TemplateView):
'redirect_url'
:
reverse
(
'users:login'
),
}
kwargs
.
update
(
context
)
return
super
()
\
.
get_context_data
(
**
kwargs
)
return
super
()
.
get_context_data
(
**
kwargs
)
class
UserResetPasswordSuccessView
(
TemplateView
):
...
...
@@ -214,7 +231,12 @@ class UserResetPasswordView(TemplateView):
class
UserFirstLoginView
(
LoginRequiredMixin
,
SessionWizardView
):
template_name
=
'users/first_login.html'
form_list
=
[
forms
.
UserProfileForm
,
forms
.
UserPublicKeyForm
]
form_list
=
[
forms
.
UserProfileForm
,
forms
.
UserPublicKeyForm
,
forms
.
UserMFAForm
,
forms
.
UserFirstLoginFinishForm
]
file_storage
=
default_storage
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
...
...
@@ -255,7 +277,6 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
def
get_form
(
self
,
step
=
None
,
data
=
None
,
files
=
None
):
form
=
super
()
.
get_form
(
step
,
data
,
files
)
form
.
instance
=
self
.
request
.
user
return
form
...
...
@@ -293,7 +314,9 @@ class LoginLogListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
'date_to'
:
self
.
date_to
,
'user'
:
self
.
user
,
'keyword'
:
self
.
keyword
,
'user_list'
:
set
(
LoginLog
.
objects
.
all
()
.
values_list
(
'username'
,
flat
=
True
))
'user_list'
:
set
(
LoginLog
.
objects
.
all
()
.
values_list
(
'username'
,
flat
=
True
)
)
}
kwargs
.
update
(
context
)
return
super
()
.
get_context_data
(
**
kwargs
)
\ No newline at end of file
utils/upgrade.sh
0 → 100644
View file @
5c17b1a7
#!/bin/bash
if
[
!
-d
"/opt/py3"
]
;
then
echo
-e
"
\0
33[31m python3虚拟环境不是默认路径
\0
33[0m"
ps
-ef
|
grep
jumpserver/tmp/beat.pid |
grep
-v
grep
if
[
$?
-ne
0
]
then
echo
-e
"
\0
33[31m jumpserver未运行,请到jumpserver目录使用 ./jms start all -d 启动
\0
33[0m"
exit
0
else
echo
-e
"
\0
33[31m 正在计算python3虚拟环境路径
\0
33[0m"
fi
py3pid
=
`
ps
-ef
|
grep
jumpserver/tmp/beat.pid |
grep
-v
grep
|
awk
'{print $2}'
`
py3file
=
`
cat
/proc/
$py3pid
/cmdline
`
py3even
=
`
echo
${
py3file
%/bin/python3*
}
`
echo
-e
"
\0
33[31m python3虚拟环境路径为
$py3even
\0
33[0m"
source
$py3even
/bin/activate
else
source
/opt/py3/bin/activate
fi
cd
`
dirname
$0
`
/
&&
cd
..
&&
./jms stop
jumpserver_backup
=
/tmp/jumpserver_backup
$(
date
-d
"today"
+
"%Y%m%d_%H%M%S"
)
mkdir
-p
$jumpserver_backup
cp
-r
./
*
$jumpserver_backup
echo
-e
"
\0
33[31m 是否需要备份Jumpserver数据库
\0
33[0m"
stty
erase ^H
read
-p
"确认备份请按Y,否则按其他键跳过备份 "
a
if
[
"
$a
"
==
y
-o
"
$a
"
==
Y
]
;
then
echo
-e
"
\0
33[31m 正在备份数据库
\0
33[0m"
echo
-e
"
\0
33[31m 请手动输入数据库信息
\0
33[0m"
read
-p
'请输入Jumpserver数据库ip:'
DB_HOST
read
-p
'请输入Jumpserver数据库端口:'
DB_PORT
read
-p
'请输入Jumpserver数据库名称:'
DB_NAME
read
-p
'请输入有权限导出数据库的用户:'
DB_USER
read
-p
'请输入该用户的密码:'
DB_PASSWORD
mysqldump
-h
$DB_HOST
-P
$DB_PORT
-u
$DB_USER
-p
$DB_PASSWORD
$DB_NAME
>
/
$jumpserver_backup
/
$DB_NAME
$(
date
-d
"today"
+
"%Y%m%d_%H%M%S"
)
.sql
||
{
echo
-e
"
\0
33[31m 备份数据库失败,请检查输入是否有误
\0
33[0m"
exit
1
}
echo
-e
"
\0
33[31m 备份数据库完成
\0
33[0m"
else
echo
-e
"
\0
33[31m 已取消备份数据库操作
\0
33[0m"
fi
git pull
&&
pip
install
-r
requirements/requirements.txt
&&
cd
utils
&&
sh make_migrations.sh
cd
..
&&
./jms start all
-d
echo
-e
"
\0
33[31m 请检查jumpserver是否启动成功
\0
33[0m"
echo
-e
"
\0
33[31m 备份文件存放于
$jumpserver_backup
目录
\0
33[0m"
exit
0
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