Commit ec8106e4 authored by ibuler's avatar ibuler

[Feature] 完成登陆日志

parent a5f97359
......@@ -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'))
......
{% 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)
\ No newline at end of file
Django>=1.11
Django==1.11
django-bootstrap3>=8.2.2
Pillow>=4.1.0
djangorestframework>=3.6.2
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment