[Feature] 完成登陆日志

This commit is contained in:
ibuler 2017-12-05 12:24:31 +08:00
parent a5f9735906
commit ec8106e43d
9 changed files with 168 additions and 17 deletions

View File

@ -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):

View File

@ -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">

View File

@ -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:

View File

@ -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):
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'))

View File

@ -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 %}

View File

@ -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'),
] ]

View File

@ -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

View File

@ -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
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
__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):
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):
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)

View File

@ -1,4 +1,4 @@
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