mirror of https://github.com/jumpserver/jumpserver
[Feature] 完成登陆日志
parent
a5f9735906
commit
ec8106e43d
|
@ -94,7 +94,6 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
|||
for asset in assets:
|
||||
last_login = self.session_week.filter(asset=asset["asset"]).order_by('date_start').last()
|
||||
asset['last'] = last_login
|
||||
print(asset)
|
||||
return assets
|
||||
|
||||
def get_week_top10_user(self):
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<ul class="nav nav-second-level active">
|
||||
<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 'Login logs' %}</a></li>
|
||||
<li id="login-log"><a href="{% url 'users:login-log-list' %}">{% trans 'Login logs' %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li id="assets">
|
||||
|
|
|
@ -164,9 +164,8 @@ class UserAuthApi(APIView):
|
|||
if user:
|
||||
token = generate_token(request, user)
|
||||
write_login_log_async.delay(
|
||||
user.username, name=user.name,
|
||||
user_agent=user_agent, login_ip=login_ip,
|
||||
login_type=login_type
|
||||
user.username, ip=login_ip,
|
||||
type=login_type, user_agent=user_agent,
|
||||
)
|
||||
return Response({'token': token, 'user': user.to_json()})
|
||||
else:
|
||||
|
|
|
@ -16,8 +16,7 @@ class AccessKey(models.Model):
|
|||
default=uuid.uuid4, editable=False)
|
||||
secret = models.UUIDField(verbose_name='AccessKeySecret',
|
||||
default=uuid.uuid4, editable=False)
|
||||
user = models.ForeignKey(User, verbose_name='User',
|
||||
related_name='access_key')
|
||||
user = models.ForeignKey(User, verbose_name='User', on_delete=models.CASCADE, related_name='access_key')
|
||||
|
||||
def get_id(self):
|
||||
return str(self.id)
|
||||
|
@ -39,8 +38,7 @@ class PrivateToken(Token):
|
|||
class LoginLog(models.Model):
|
||||
LOGIN_TYPE_CHOICE = (
|
||||
('W', 'Web'),
|
||||
('ST', 'SSH Terminal'),
|
||||
('WT', 'Web Terminal')
|
||||
('T', 'Terminal'),
|
||||
)
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
username = models.CharField(max_length=20, verbose_name=_('Username'))
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
{% 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 %}
|
||||
|
|
@ -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/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'),
|
||||
|
||||
# Login log
|
||||
url(r'^login-log/$', views.LoginLogListView.as_view(), name='login-log-list'),
|
||||
]
|
||||
|
|
|
@ -9,7 +9,7 @@ import requests
|
|||
import ipaddress
|
||||
from django.conf import settings
|
||||
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.core.cache import cache
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ from django import forms
|
|||
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
|
||||
from django.views.generic import ListView
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import reverse, redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
|
@ -17,9 +19,10 @@ from django.views.generic.base import TemplateView
|
|||
from django.views.generic.edit import FormView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
|
||||
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 ..tasks import write_login_log_async
|
||||
from .. import forms
|
||||
|
@ -28,7 +31,7 @@ from .. import forms
|
|||
__all__ = ['UserLoginView', 'UserLogoutView',
|
||||
'UserForgotPasswordView', 'UserForgotPasswordSendmailSuccessView',
|
||||
'UserResetPasswordView', 'UserResetPasswordSuccessView',
|
||||
'UserFirstLoginView']
|
||||
'UserFirstLoginView', 'LoginLogListView']
|
||||
|
||||
|
||||
@method_decorator(sensitive_post_parameters(), name='dispatch')
|
||||
|
@ -48,10 +51,10 @@ class UserLoginView(FormView):
|
|||
auth_login(self.request, form.get_user())
|
||||
login_ip = self.request.META.get('REMOTE_ADDR', '')
|
||||
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
|
||||
write_login_log_async.delay(self.request.user.username,
|
||||
self.request.user.name,
|
||||
login_type='W', login_ip=login_ip,
|
||||
user_agent=user_agent)
|
||||
write_login_log_async.delay(
|
||||
self.request.user.username, type='W',
|
||||
ip=login_ip, user_agent=user_agent
|
||||
)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
|
@ -202,3 +205,60 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
|||
|
||||
form.instance = self.request.user
|
||||
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)
|
|
@ -1,4 +1,4 @@
|
|||
Django>=1.11
|
||||
Django==1.11
|
||||
django-bootstrap3>=8.2.2
|
||||
Pillow>=4.1.0
|
||||
djangorestframework>=3.6.2
|
||||
|
|
Loading…
Reference in New Issue