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
ec8106e4
Commit
ec8106e4
authored
Dec 05, 2017
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[Feature] 完成登陆日志
parent
a5f97359
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
169 additions
and
17 deletions
+169
-17
views.py
apps/jumpserver/views.py
+0
-1
_nav.html
apps/templates/_nav.html
+1
-1
api.py
apps/users/api.py
+2
-3
authentication.py
apps/users/models/authentication.py
+2
-4
login_log_list.html
apps/users/templates/users/login_log_list.html
+92
-0
views_urls.py
apps/users/urls/views_urls.py
+3
-0
utils.py
apps/users/utils.py
+1
-1
login.py
apps/users/views/login.py
+67
-6
requirements.txt
requirements/requirements.txt
+1
-1
No files found.
apps/jumpserver/views.py
View file @
ec8106e4
...
@@ -94,7 +94,6 @@ class IndexView(LoginRequiredMixin, TemplateView):
...
@@ -94,7 +94,6 @@ class IndexView(LoginRequiredMixin, TemplateView):
for
asset
in
assets
:
for
asset
in
assets
:
last_login
=
self
.
session_week
.
filter
(
asset
=
asset
[
"asset"
])
.
order_by
(
'date_start'
)
.
last
()
last_login
=
self
.
session_week
.
filter
(
asset
=
asset
[
"asset"
])
.
order_by
(
'date_start'
)
.
last
()
asset
[
'last'
]
=
last_login
asset
[
'last'
]
=
last_login
print
(
asset
)
return
assets
return
assets
def
get_week_top10_user
(
self
):
def
get_week_top10_user
(
self
):
...
...
apps/templates/_nav.html
View file @
ec8106e4
...
@@ -11,7 +11,7 @@
...
@@ -11,7 +11,7 @@
<ul
class=
"nav nav-second-level active"
>
<ul
class=
"nav nav-second-level active"
>
<li
id=
"user"
><a
href=
"{% url 'users:user-list' %}"
>
{% trans 'User' %}
</a></li>
<li
id=
"user"
><a
href=
"{% url 'users:user-list' %}"
>
{% trans 'User' %}
</a></li>
<li
id=
"user-group"
><a
href=
"{% url 'users:user-group-list' %}"
>
{% trans 'User group' %}
</a></li>
<li
id=
"user-group"
><a
href=
"{% url 'users:user-group-list' %}"
>
{% trans 'User group' %}
</a></li>
<li
id=
"
user-group"
><a
href=
"{% url 'users:user-group
-list' %}"
>
{% trans 'Login logs' %}
</a></li>
<li
id=
"
login-log"
><a
href=
"{% url 'users:login-log
-list' %}"
>
{% trans 'Login logs' %}
</a></li>
</ul>
</ul>
</li>
</li>
<li
id=
"assets"
>
<li
id=
"assets"
>
...
...
apps/users/api.py
View file @
ec8106e4
...
@@ -164,9 +164,8 @@ class UserAuthApi(APIView):
...
@@ -164,9 +164,8 @@ class UserAuthApi(APIView):
if
user
:
if
user
:
token
=
generate_token
(
request
,
user
)
token
=
generate_token
(
request
,
user
)
write_login_log_async
.
delay
(
write_login_log_async
.
delay
(
user
.
username
,
name
=
user
.
name
,
user
.
username
,
ip
=
login_ip
,
user_agent
=
user_agent
,
login_ip
=
login_ip
,
type
=
login_type
,
user_agent
=
user_agent
,
login_type
=
login_type
)
)
return
Response
({
'token'
:
token
,
'user'
:
user
.
to_json
()})
return
Response
({
'token'
:
token
,
'user'
:
user
.
to_json
()})
else
:
else
:
...
...
apps/users/models/authentication.py
View file @
ec8106e4
...
@@ -16,8 +16,7 @@ class AccessKey(models.Model):
...
@@ -16,8 +16,7 @@ class AccessKey(models.Model):
default
=
uuid
.
uuid4
,
editable
=
False
)
default
=
uuid
.
uuid4
,
editable
=
False
)
secret
=
models
.
UUIDField
(
verbose_name
=
'AccessKeySecret'
,
secret
=
models
.
UUIDField
(
verbose_name
=
'AccessKeySecret'
,
default
=
uuid
.
uuid4
,
editable
=
False
)
default
=
uuid
.
uuid4
,
editable
=
False
)
user
=
models
.
ForeignKey
(
User
,
verbose_name
=
'User'
,
user
=
models
.
ForeignKey
(
User
,
verbose_name
=
'User'
,
on_delete
=
models
.
CASCADE
,
related_name
=
'access_key'
)
related_name
=
'access_key'
)
def
get_id
(
self
):
def
get_id
(
self
):
return
str
(
self
.
id
)
return
str
(
self
.
id
)
...
@@ -39,8 +38,7 @@ class PrivateToken(Token):
...
@@ -39,8 +38,7 @@ class PrivateToken(Token):
class
LoginLog
(
models
.
Model
):
class
LoginLog
(
models
.
Model
):
LOGIN_TYPE_CHOICE
=
(
LOGIN_TYPE_CHOICE
=
(
(
'W'
,
'Web'
),
(
'W'
,
'Web'
),
(
'ST'
,
'SSH Terminal'
),
(
'T'
,
'Terminal'
),
(
'WT'
,
'Web Terminal'
)
)
)
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
id
=
models
.
UUIDField
(
default
=
uuid
.
uuid4
,
primary_key
=
True
)
username
=
models
.
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'Username'
))
username
=
models
.
CharField
(
max_length
=
20
,
verbose_name
=
_
(
'Username'
))
...
...
apps/users/templates/users/login_log_list.html
0 → 100644
View file @
ec8106e4
{% extends '_base_list.html' %}
{% load i18n %}
{% load static %}
{% load common_tags %}
{% block content_left_head %}
<link
href=
"{% static 'css/plugins/datepicker/datepicker3.css' %}"
rel=
"stylesheet"
>
<style>
#search_btn
{
margin-bottom
:
0
;
}
</style>
{% endblock %}
{% block table_search %}
<form
id=
"search_form"
method=
"get"
action=
""
class=
"pull-right form-inline"
>
<div
class=
"form-group"
id=
"date"
>
<div
class=
"input-daterange input-group"
id=
"datepicker"
>
<span
class=
"input-group-addon"
><i
class=
"fa fa-calendar"
></i></span>
<input
type=
"text"
class=
"input-sm form-control"
style=
"width: 100px;"
name=
"date_from"
value=
"{{ date_from }}"
>
<span
class=
"input-group-addon"
>
to
</span>
<input
type=
"text"
class=
"input-sm form-control"
style=
"width: 100px;"
name=
"date_to"
value=
"{{ date_to }}"
>
</div>
</div>
<div
class=
"input-group"
>
<select
class=
"select2 form-control"
name=
"username"
>
<option
value=
""
>
{% trans 'Select user' %}
</option>
{% for u in user_list %}
<option
value=
"{{ u }}"
{%
if
u =
=
username
%}
selected
{%
endif
%}
>
{{ u }}
</option>
{% endfor %}
</select>
</div>
<div
class=
"input-group"
>
<input
type=
"text"
class=
"form-control input-sm"
name=
"keyword"
placeholder=
"Search"
value=
"{{ keyword }}"
>
</div>
<div
class=
"input-group"
>
<div
class=
"input-group-btn"
>
<button
id=
'search_btn'
type=
"submit"
class=
"btn btn-sm btn-primary"
>
搜索
</button>
</div>
</div>
</form>
{% endblock %}
{% block table_head %}
<th
class=
"text-center"
>
{% trans 'ID' %}
</th>
<th
class=
"text-center"
>
{% trans 'Username' %}
</th>
<th
class=
"text-center"
>
{% trans 'Type' %}
</th>
<th
class=
"text-center"
>
{% trans 'UA' %}
</th>
<th
class=
"text-center"
>
{% trans 'IP' %}
</th>
<th
class=
"text-center"
>
{% trans 'City' %}
</th>
<th
class=
"text-center"
>
{% trans 'Date' %}
</th>
{% endblock %}
{% block table_body %}
{% for login_log in object_list %}
<tr
class=
"gradeX"
>
<td
class=
"text-center"
>
{{ forloop.counter }}
</td>
<td
class=
"text-center"
>
{{ login_log.username }}
</td>
<td
class=
"text-center"
>
{{ login_log.get_type_display }}
</td>
<td
class=
"text-center"
>
<span
href=
"javascript:void(0);"
data-toggle=
"tooltips"
title=
"{{ login_log.user_agent }}"
>
{{ login_log.user_agent | truncatechars:20 }}
</span>
</td>
<td
class=
"text-center"
>
{{ login_log.ip }}
</td>
<td
class=
"text-center"
>
{{ login_log.city }}
</td>
<td
class=
"text-center"
>
{{ login_log.datetime }}
</td>
</tr>
{% endfor %}
{% endblock %}
{% block custom_foot_js %}
<script
src=
"{% static 'js/plugins/datepicker/bootstrap-datepicker.js' %}"
></script>
<script>
$
(
document
).
ready
(
function
()
{
$
(
'table'
).
DataTable
({
"searching"
:
false
,
"bInfo"
:
false
,
"paging"
:
false
,
"order"
:
[]
});
$
(
'#date .input-daterange'
).
datepicker
({
dateFormat
:
'mm/dd/yy'
,
keyboardNavigation
:
false
,
forceParse
:
false
,
autoclose
:
true
});
$
(
'.select2'
).
select2
();
})
</script>
{% endblock %}
apps/users/urls/views_urls.py
View file @
ec8106e4
...
@@ -43,4 +43,7 @@ urlpatterns = [
...
@@ -43,4 +43,7 @@ urlpatterns = [
url
(
r'^user-group/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission$'
,
views
.
UserGroupAssetPermissionView
.
as_view
(),
name
=
'user-group-asset-permission'
),
url
(
r'^user-group/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission$'
,
views
.
UserGroupAssetPermissionView
.
as_view
(),
name
=
'user-group-asset-permission'
),
url
(
r'^user-group/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission/create$'
,
views
.
UserGroupAssetPermissionCreateView
.
as_view
(),
name
=
'user-group-asset-permission-create'
),
url
(
r'^user-group/(?P<pk>[0-9a-zA-Z\-]+)/asset-permission/create$'
,
views
.
UserGroupAssetPermissionCreateView
.
as_view
(),
name
=
'user-group-asset-permission-create'
),
url
(
r'^user-group/(?P<pk>[0-9a-zA-Z\-]+)/assets'
,
views
.
UserGroupGrantedAssetView
.
as_view
(),
name
=
'user-group-granted-asset'
),
url
(
r'^user-group/(?P<pk>[0-9a-zA-Z\-]+)/assets'
,
views
.
UserGroupGrantedAssetView
.
as_view
(),
name
=
'user-group-granted-asset'
),
# Login log
url
(
r'^login-log/$'
,
views
.
LoginLogListView
.
as_view
(),
name
=
'login-log-list'
),
]
]
apps/users/utils.py
View file @
ec8106e4
...
@@ -9,7 +9,7 @@ import requests
...
@@ -9,7 +9,7 @@ import requests
import
ipaddress
import
ipaddress
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.mixins
import
UserPassesTestMixin
from
django.contrib.auth.mixins
import
UserPassesTestMixin
from
django.contrib.auth
import
authenticate
from
django.contrib.auth
import
authenticate
,
login
as
auth_login
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
django.core.cache
import
cache
from
django.core.cache
import
cache
...
...
apps/users/views/login.py
View file @
ec8106e4
...
@@ -5,7 +5,9 @@ from django import forms
...
@@ -5,7 +5,9 @@ from django import forms
from
django.shortcuts
import
render
from
django.shortcuts
import
render
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.auth.mixins
import
LoginRequiredMixin
from
django.views.generic
import
ListView
from
django.core.files.storage
import
default_storage
from
django.core.files.storage
import
default_storage
from
django.db.models
import
Q
from
django.http
import
HttpResponseRedirect
from
django.http
import
HttpResponseRedirect
from
django.shortcuts
import
reverse
,
redirect
from
django.shortcuts
import
reverse
,
redirect
from
django.utils.decorators
import
method_decorator
from
django.utils.decorators
import
method_decorator
...
@@ -17,9 +19,10 @@ from django.views.generic.base import TemplateView
...
@@ -17,9 +19,10 @@ from django.views.generic.base import TemplateView
from
django.views.generic.edit
import
FormView
from
django.views.generic.edit
import
FormView
from
formtools.wizard.views
import
SessionWizardView
from
formtools.wizard.views
import
SessionWizardView
from
django.conf
import
settings
from
django.conf
import
settings
from
django.utils
import
timezone
from
common.utils
import
get_object_or_none
from
common.utils
import
get_object_or_none
from
..models
import
User
from
..models
import
User
,
LoginLog
from
..utils
import
send_reset_password_mail
from
..utils
import
send_reset_password_mail
from
..tasks
import
write_login_log_async
from
..tasks
import
write_login_log_async
from
..
import
forms
from
..
import
forms
...
@@ -28,7 +31,7 @@ from .. import forms
...
@@ -28,7 +31,7 @@ from .. import forms
__all__
=
[
'UserLoginView'
,
'UserLogoutView'
,
__all__
=
[
'UserLoginView'
,
'UserLogoutView'
,
'UserForgotPasswordView'
,
'UserForgotPasswordSendmailSuccessView'
,
'UserForgotPasswordView'
,
'UserForgotPasswordSendmailSuccessView'
,
'UserResetPasswordView'
,
'UserResetPasswordSuccessView'
,
'UserResetPasswordView'
,
'UserResetPasswordSuccessView'
,
'UserFirstLoginView'
]
'UserFirstLoginView'
,
'LoginLogListView'
]
@method_decorator
(
sensitive_post_parameters
(),
name
=
'dispatch'
)
@method_decorator
(
sensitive_post_parameters
(),
name
=
'dispatch'
)
...
@@ -48,10 +51,10 @@ class UserLoginView(FormView):
...
@@ -48,10 +51,10 @@ class UserLoginView(FormView):
auth_login
(
self
.
request
,
form
.
get_user
())
auth_login
(
self
.
request
,
form
.
get_user
())
login_ip
=
self
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
''
)
login_ip
=
self
.
request
.
META
.
get
(
'REMOTE_ADDR'
,
''
)
user_agent
=
self
.
request
.
META
.
get
(
'HTTP_USER_AGENT'
,
''
)
user_agent
=
self
.
request
.
META
.
get
(
'HTTP_USER_AGENT'
,
''
)
write_login_log_async
.
delay
(
self
.
request
.
user
.
username
,
write_login_log_async
.
delay
(
self
.
request
.
user
.
name
,
self
.
request
.
user
.
username
,
type
=
'W'
,
login_type
=
'W'
,
login_ip
=
login_ip
,
ip
=
login_ip
,
user_agent
=
user_agent
user_agent
=
user_agent
)
)
return
redirect
(
self
.
get_success_url
())
return
redirect
(
self
.
get_success_url
())
def
get_success_url
(
self
):
def
get_success_url
(
self
):
...
@@ -202,3 +205,60 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
...
@@ -202,3 +205,60 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
form
.
instance
=
self
.
request
.
user
form
.
instance
=
self
.
request
.
user
return
form
return
form
class
LoginLogListView
(
ListView
):
template_name
=
'users/login_log_list.html'
model
=
LoginLog
paginate_by
=
settings
.
CONFIG
.
DISPLAY_PER_PAGE
username
=
keyword
=
date_from_s
=
date_to_s
=
""
date_format
=
'
%
m/
%
d/
%
Y'
def
get_queryset
(
self
):
date_to_default
=
timezone
.
now
()
date_from_default
=
timezone
.
now
()
-
timezone
.
timedelta
(
7
)
date_to_default_s
=
date_to_default
.
strftime
(
self
.
date_format
)
date_from_default_s
=
date_from_default
.
strftime
(
self
.
date_format
)
self
.
username
=
self
.
request
.
GET
.
get
(
'username'
,
''
)
self
.
keyword
=
self
.
request
.
GET
.
get
(
"keyword"
,
''
)
self
.
date_from_s
=
self
.
request
.
GET
.
get
(
'date_from'
,
date_from_default_s
)
self
.
date_to_s
=
self
.
request
.
GET
.
get
(
'date_to'
,
date_to_default_s
)
self
.
queryset
=
super
()
.
get_queryset
()
if
self
.
username
:
self
.
queryset
=
self
.
queryset
.
filter
(
username
=
self
.
username
)
if
self
.
date_from_s
:
date_from
=
timezone
.
datetime
.
strptime
(
self
.
date_from_s
,
'
%
m/
%
d/
%
Y'
)
date_from
=
date_from
.
replace
(
tzinfo
=
timezone
.
get_current_timezone
()
)
self
.
queryset
=
self
.
queryset
.
filter
(
datetime__gt
=
date_from
)
if
self
.
date_to_s
:
date_to
=
timezone
.
datetime
.
strptime
(
self
.
date_to_s
+
' 23:59:59'
,
'
%
m/
%
d/
%
Y
%
H:
%
M:
%
S'
)
date_to
=
date_to
.
replace
(
tzinfo
=
timezone
.
get_current_timezone
()
)
self
.
queryset
=
self
.
queryset
.
filter
(
datetime__lt
=
date_to
)
if
self
.
keyword
:
self
.
queryset
=
self
.
queryset
.
filter
(
Q
(
ip__contains
=
self
.
keyword
)
|
Q
(
city__contains
=
self
.
keyword
)
|
Q
(
username__contains
=
self
.
keyword
)
)
return
self
.
queryset
def
get_context_data
(
self
,
**
kwargs
):
context
=
{
'app'
:
_
(
'Users'
),
'action'
:
_
(
'Login log list'
),
'date_from'
:
self
.
date_from_s
,
'date_to'
:
self
.
date_to_s
,
'username'
:
self
.
username
,
'keyword'
:
self
.
keyword
,
'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
requirements/requirements.txt
View file @
ec8106e4
Django
>
=1.11
Django
=
=1.11
django-bootstrap3>=8.2.2
django-bootstrap3>=8.2.2
Pillow>=4.1.0
Pillow>=4.1.0
djangorestframework>=3.6.2
djangorestframework>=3.6.2
...
...
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