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
a7e3f9c4
Commit
a7e3f9c4
authored
8 years ago
by
xiaokong1937@gmail.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
#8 user first login view
parent
d8fe59de
master
auditor_jym
audits
dev
dev_beta
dev_beta_db
gengmei
lagacy-0.4.0
node_service
password
rbac
restrict_access
test
v52
wph
1.5.2
1.5.1
1.5.0
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.3
1.3.2
1.3.1
1.3.0
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
v1.4.10
v1.4.7
v1.4.4
No related merge requests found
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
134 additions
and
113 deletions
+134
-113
settings.py
apps/jumpserver/settings.py
+2
-1
forms.py
apps/users/forms.py
+14
-5
first_login.html
apps/users/templates/users/first_login.html
+34
-22
first_login.old.html
apps/users/templates/users/first_login.old.html
+0
-77
utils.py
apps/users/utils.py
+55
-4
views.py
apps/users/views.py
+29
-4
No files found.
apps/jumpserver/settings.py
View file @
a7e3f9c4
...
@@ -108,6 +108,7 @@ TEMPLATES = [
...
@@ -108,6 +108,7 @@ TEMPLATES = [
# WSGI_APPLICATION = 'jumpserver.wsgi.application'
# WSGI_APPLICATION = 'jumpserver.wsgi.application'
LOGIN_REDIRECT_URL
=
reverse_lazy
(
'index'
)
LOGIN_REDIRECT_URL
=
reverse_lazy
(
'index'
)
LOGIN_URL
=
reverse_lazy
(
'users:login'
)
# Database
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
...
@@ -227,7 +228,7 @@ USE_L10N = True
...
@@ -227,7 +228,7 @@ USE_L10N = True
USE_TZ
=
True
USE_TZ
=
True
# I18N translation
# I18N translation
LOCALE_PATHS
=
[
os
.
path
.
join
(
BASE_DIR
,
'locale'
),]
LOCALE_PATHS
=
[
os
.
path
.
join
(
BASE_DIR
,
'locale'
),
]
# Static files (CSS, JavaScript, Images)
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
# https://docs.djangoproject.com/en/1.10/howto/static-files/
...
...
This diff is collapsed.
Click to expand it.
apps/users/forms.py
View file @
a7e3f9c4
...
@@ -71,11 +71,20 @@ class UserGroupForm(forms.ModelForm):
...
@@ -71,11 +71,20 @@ class UserGroupForm(forms.ModelForm):
class
UserInfoForm
(
forms
.
Form
):
class
UserInfoForm
(
forms
.
Form
):
name
=
forms
.
CharField
(
max_length
=
20
)
name
=
forms
.
CharField
(
max_length
=
20
,
label
=
_
(
'name'
))
wechat
=
forms
.
CharField
(
max_length
=
30
)
avatar
=
forms
.
ImageField
(
label
=
_
(
'avatar'
),
required
=
False
)
phone
=
forms
.
CharField
(
max_length
=
20
)
wechat
=
forms
.
CharField
(
max_length
=
30
,
label
=
_
(
'wechat'
),
required
=
False
)
enable_otp
=
forms
.
BooleanField
()
phone
=
forms
.
CharField
(
max_length
=
20
,
label
=
_
(
'phone'
),
required
=
False
)
enable_otp
=
forms
.
BooleanField
(
required
=
False
,
label
=
_
(
'enable otp'
))
class
UserKeyForm
(
forms
.
Form
):
class
UserKeyForm
(
forms
.
Form
):
private_key
=
forms
.
CharField
(
max_length
=
5000
,
widget
=
forms
.
Textarea
)
private_key
=
forms
.
CharField
(
max_length
=
5000
,
widget
=
forms
.
Textarea
,
label
=
_
(
'private key'
))
def
clean_private_key
(
self
):
from
users.utils
import
validate_ssh_pk
ssh_pk
=
self
.
cleaned_data
[
'private_key'
]
checked
,
reason
=
validate_ssh_pk
(
ssh_pk
)
if
not
checked
:
raise
forms
.
ValidationError
(
_
(
'Not a valid ssh private key.'
))
return
ssh_pk
This diff is collapsed.
Click to expand it.
apps/users/templates/users/first_login.html
View file @
a7e3f9c4
...
@@ -13,7 +13,7 @@
...
@@ -13,7 +13,7 @@
<div
class=
"col-lg-12"
>
<div
class=
"col-lg-12"
>
<div
class=
"ibox"
>
<div
class=
"ibox"
>
<div
class=
"ibox-title"
>
<div
class=
"ibox-title"
>
<h5>
Basic Wizzard
</h5>
<h5>
{% trans 'First Login' %}
</h5>
<div
class=
"ibox-tools"
>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
<i
class=
"fa fa-chevron-up"
></i>
...
@@ -21,9 +21,6 @@
...
@@ -21,9 +21,6 @@
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
<i
class=
"fa fa-wrench"
></i>
</a>
</a>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
</div>
</div>
<div
class=
"ibox-content"
>
<div
class=
"ibox-content"
>
...
@@ -33,32 +30,34 @@
...
@@ -33,32 +30,34 @@
{% for step in wizard.steps.all %}
{% 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 %}"
<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"
>
aria-disabled=
"false"
aria-selected=
"true"
>
<a
aria-controls=
"wizard-p-{{ step }}"
href=
"#wizard-h-{{ step }}"
id=
"wizard-t-{{ step }}
"
><span
class=
"number"
>
{% trans 'Step' %} {{ step }}
</span></a>
<a
href=
"javascript:void(0)
"
><span
class=
"number"
>
{% trans 'Step' %} {{ step }}
</span></a>
</li>
</li>
{% endfor %}
{% endfor %}
</ul>
</ul>
</div>
</div>
<div
class=
"content clearfix"
>
<div
class=
"content clearfix"
>
<form
action=
""
method=
"post"
class=
"form col-lg-8 p-m"
>
<form
action=
""
method=
"post"
class=
"form col-lg-8 p-m"
id=
"fl_form"
>
{% csrf_token %}
{% csrf_token %}
{{ wizard.management_form }}
{{ wizard.management_form }}
{% if wizard.form.forms %}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{% for form in wizard.form.forms %}
{{ form|bootstrap }}
{{ form|bootstrap }}
{% endfor %}
{% endfor %}
{% else %}
{% else %}
{{ wizard.form|bootstrap }}
{{ wizard.form|bootstrap }}
{% endif %}
{% endif %}
</div>
</div>
</form>
</form>
</div>
</div>
{% if wizard.steps.prev %}
<div
class=
"actions clearfix"
>
<button
name=
"wizard_goto_step"
type=
"submit"
value=
"{{ wizard.steps.first }}"
>
{% trans "first step" %}
</button>
<ul>
<button
name=
"wizard_goto_step"
type=
"submit"
value=
"{{ wizard.steps.prev }}"
>
{% trans "prev step" %}
</button>
{% if wizard.steps.prev %}
{% endif %}
<li><a
class=
"fl_goto"
data-goto=
"{{ wizard.steps.first }}"
>
{% trans "first step" %}
</a></li>
<input
type=
"submit"
value=
"{% trans "
submit
"
%}"
/>
<li><a
class=
"fl_goto"
name=
"wizard_goto_step"
data-goto=
"{{ wizard.steps.prev }}"
>
{% trans "prev step" %}
</a></li>
{% endif %}
<li><a
id=
"fl_submit"
>
{% trans "submit" %}
</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
@@ -66,3 +65,16 @@
...
@@ -66,3 +65,16 @@
</div>
</div>
</div>
</div>
{% endblock %}
{% 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
;
})
</script>
{% endblock %}
This diff is collapsed.
Click to expand it.
apps/users/templates/users/first_login.old.html
deleted
100644 → 0
View file @
d8fe59de
{% extends 'base.html' %}
{% load static %}
{% load i18n %}
{% block custom_head_css_js %}
<link
href=
"{% static 'css/plugins/steps/jquery.steps.css' %}"
rel=
"stylesheet"
>
<script
src=
"{% static 'js/plugins/steps/jquery.steps.min.js' %}"
></script>
{% endblock %}
{% block content %}
<div
class=
"wrapper wrapper-content animated fadeInRight"
>
<div
class=
"row"
>
<div
class=
"col-lg-12"
>
<div
class=
"ibox float-e-margins"
>
<div
class=
"ibox-title"
>
<h5>
Basic Wizzard
</h5>
<div
class=
"ibox-tools"
>
<a
class=
"collapse-link"
>
<i
class=
"fa fa-chevron-up"
></i>
</a>
<a
class=
"dropdown-toggle"
data-toggle=
"dropdown"
href=
"#"
>
<i
class=
"fa fa-wrench"
></i>
</a>
<a
class=
"close-link"
>
<i
class=
"fa fa-times"
></i>
</a>
</div>
</div>
<div
class=
"ibox-content"
>
<p>
This is basic example of Step
</p>
<div
id=
"wizard"
>
<h1>
First Step
</h1>
<div
class=
"step-content"
>
<div
class=
"text-center m-t-md"
>
<h2>
Hello in Step 1
</h2>
<p>
This is the first content.
</p>
</div>
</div>
<h1>
Second Step
</h1>
<div
class=
"step-content"
>
<div
class=
"text-center m-t-md"
>
<h2>
This is step 2
</h2>
<p>
This content is diferent than the first one.
</p>
</div>
</div>
<h1>
Third Step
</h1>
<div
class=
"step-content"
>
<div
class=
"text-center m-t-md"
>
<h2>
This is step 3
</h2>
<p>
This is last content.
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block custom_foot_js %}
<script>
$
(
document
).
ready
(
function
(){
$
(
"#wizard"
).
steps
();
})
</script>
{% endblock %}
This diff is collapsed.
Click to expand it.
apps/users/utils.py
View file @
a7e3f9c4
# ~*~ coding: utf-8 ~*~
# ~*~ coding: utf-8 ~*~
#
#
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
import
os
import
logging
import
logging
import
os
import
re
from
paramiko.rsakey
import
RSAKey
from
django.contrib.auth.mixins
import
UserPassesTestMixin
from
django.contrib.auth.mixins
import
UserPassesTestMixin
from
django.urls
import
reverse_lazy
from
django.urls
import
reverse_lazy
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
paramiko.rsakey
import
RSAKey
from
common.tasks
import
send_mail_async
from
common.tasks
import
send_mail_async
from
common.utils
import
reverse
from
common.utils
import
reverse
from
users.models
import
User
try
:
try
:
...
@@ -125,5 +125,56 @@ def send_reset_password_mail(user):
...
@@ -125,5 +125,56 @@ def send_reset_password_mail(user):
send_mail_async
.
delay
(
subject
,
message
,
recipient_list
,
html_message
=
message
)
send_mail_async
.
delay
(
subject
,
message
,
recipient_list
,
html_message
=
message
)
def
validate_ssh_pk
(
text
):
"""
Expects a SSH private key as string.
Returns a boolean and a error message.
If the text is parsed as private key successfully,
(True,'') is returned. Otherwise,
(False, <message describing the error>) is returned.
from https://github.com/githubnemo/SSH-private-key-validator/blob/master/validate.py
"""
if
not
text
:
return
False
,
'No text given'
startPattern
=
re
.
compile
(
"^-----BEGIN [A-Z]+ PRIVATE KEY-----"
)
optionPattern
=
re
.
compile
(
"^.+: .+"
)
contentPattern
=
re
.
compile
(
"^([a-zA-Z0-9+/]{64}|[a-zA-Z0-9+/]{1,64}[=]{0,2})$"
)
endPattern
=
re
.
compile
(
"^-----END [A-Z]+ PRIVATE KEY-----"
)
def
contentState
(
text
):
for
i
in
range
(
0
,
len
(
text
)):
line
=
text
[
i
]
if
endPattern
.
match
(
line
):
if
i
==
len
(
text
)
-
1
or
len
(
text
[
i
+
1
])
==
0
:
return
True
,
''
else
:
return
False
,
'At end but content coming'
elif
not
contentPattern
.
match
(
line
):
return
False
,
'Wrong string in content section'
return
False
,
'No content or missing end line'
def
optionState
(
text
):
for
i
in
range
(
0
,
len
(
text
)):
line
=
text
[
i
]
if
line
[
-
1
:]
==
'
\\
'
:
return
optionState
(
text
[
i
+
2
:])
if
not
optionPattern
.
match
(
line
):
return
contentState
(
text
[
i
+
1
:])
return
False
,
'Expected option, found nothing'
def
startState
(
text
):
if
len
(
text
)
==
0
or
not
startPattern
.
match
(
text
[
0
]):
return
False
,
'Header is wrong'
return
optionState
(
text
[
1
:])
return
startState
([
n
.
strip
()
for
n
in
text
.
splitlines
()])
This diff is collapsed.
Click to expand it.
apps/users/views.py
View file @
a7e3f9c4
...
@@ -6,11 +6,12 @@ import logging
...
@@ -6,11 +6,12 @@ import logging
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth
import
login
as
auth_login
,
logout
as
auth_logout
from
django.contrib.auth
import
login
as
auth_login
,
logout
as
auth_logout
from
django.contrib.auth.mixins
import
LoginRequiredMixin
from
django.contrib.messages.views
import
SuccessMessageMixin
from
django.contrib.messages.views
import
SuccessMessageMixin
from
django.core.files.storage
import
default_storage
from
django.core.files.storage
import
default_storage
from
django.db.models
import
Q
from
django.db.models
import
Q
from
django.http
import
HttpResponseRedirect
from
django.http
import
HttpResponseRedirect
from
django.shortcuts
import
get_object_or_404
,
reverse
,
redirect
,
render
from
django.shortcuts
import
get_object_or_404
,
reverse
,
redirect
from
django.utils.decorators
import
method_decorator
from
django.utils.decorators
import
method_decorator
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
django.urls
import
reverse_lazy
from
django.urls
import
reverse_lazy
...
@@ -53,7 +54,7 @@ class UserLoginView(FormView):
...
@@ -53,7 +54,7 @@ class UserLoginView(FormView):
def
get_success_url
(
self
):
def
get_success_url
(
self
):
if
self
.
request
.
user
.
is_first_login
:
if
self
.
request
.
user
.
is_first_login
:
return
'/firstlogin'
return
reverse
(
'users:user-first-login'
)
return
self
.
request
.
POST
.
get
(
return
self
.
request
.
POST
.
get
(
self
.
redirect_field_name
,
self
.
redirect_field_name
,
...
@@ -300,16 +301,40 @@ class UserResetPasswordView(TemplateView):
...
@@ -300,16 +301,40 @@ class UserResetPasswordView(TemplateView):
return
HttpResponseRedirect
(
reverse
(
'users:reset-password-success'
))
return
HttpResponseRedirect
(
reverse
(
'users:reset-password-success'
))
class
UserFirstLoginView
(
SessionWizardView
):
class
UserFirstLoginView
(
LoginRequiredMixin
,
SessionWizardView
):
template_name
=
'users/first_login.html'
template_name
=
'users/first_login.html'
form_list
=
[
UserInfoForm
,
UserKeyForm
]
form_list
=
[
UserInfoForm
,
UserKeyForm
]
file_storage
=
default_storage
file_storage
=
default_storage
def
dispatch
(
self
,
request
,
*
args
,
**
kwargs
):
if
request
.
user
.
is_authenticated
()
and
not
request
.
user
.
is_first_login
:
return
redirect
(
reverse
(
'index'
))
return
super
(
UserFirstLoginView
,
self
)
.
dispatch
(
request
,
*
args
,
**
kwargs
)
def
done
(
self
,
form_list
,
form_dict
,
**
kwargs
):
def
done
(
self
,
form_list
,
form_dict
,
**
kwargs
):
print
form_list
user
=
self
.
request
.
user
for
form
in
form_list
:
for
field
in
form
:
if
field
.
value
():
setattr
(
user
,
field
.
name
,
field
.
value
())
if
field
.
name
==
'enable_otp'
:
user
.
enable_otp
=
field
.
value
()
user
.
is_first_login
=
False
user
.
save
()
return
redirect
(
reverse
(
'index'
))
return
redirect
(
reverse
(
'index'
))
def
get_context_data
(
self
,
**
kwargs
):
def
get_context_data
(
self
,
**
kwargs
):
context
=
super
(
UserFirstLoginView
,
self
)
.
get_context_data
(
**
kwargs
)
context
=
super
(
UserFirstLoginView
,
self
)
.
get_context_data
(
**
kwargs
)
context
.
update
({
'app'
:
_
(
'Users'
),
'action'
:
_
(
'First Login'
)})
context
.
update
({
'app'
:
_
(
'Users'
),
'action'
:
_
(
'First Login'
)})
return
context
return
context
def
get_form_initial
(
self
,
step
):
user
=
self
.
request
.
user
if
step
==
'0'
:
return
{
'name'
:
user
.
name
or
user
.
username
,
'enable_otp'
:
user
.
enable_otp
or
True
,
'wechat'
:
user
.
wechat
or
''
,
'phone'
:
user
.
phone
or
''
}
return
super
(
UserFirstLoginView
,
self
)
.
get_form_initial
(
step
)
This diff is collapsed.
Click to expand it.
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