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
Sep 10, 2016
by
xiaokong1937@gmail.com
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
#8 user first login view
parent
d8fe59de
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/
...
...
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
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 %}
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 %}
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
()])
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
)
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