diff --git a/apps/jumpserver/views.py b/apps/jumpserver/views.py
index 08ddbf3db76f01d1b33de1a35a31255d2c2886cb..0ddba94bfaabc90c25bdd95d95a2c17040269d0d 100644
--- a/apps/jumpserver/views.py
+++ b/apps/jumpserver/views.py
@@ -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):
diff --git a/apps/templates/_nav.html b/apps/templates/_nav.html
index d4f4311ad9f1c5262017e1fcb61c67fc3dc08763..d353be5c16ea3359a74e90e519f796c1564bd12e 100644
--- a/apps/templates/_nav.html
+++ b/apps/templates/_nav.html
@@ -11,7 +11,7 @@
diff --git a/apps/users/api.py b/apps/users/api.py
index 522a47bb546cdf070615b1d359d7d3ec4174b216..7ec00b836776a04a79b7138a75aa80612aee8b34 100644
--- a/apps/users/api.py
+++ b/apps/users/api.py
@@ -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:
diff --git a/apps/users/models/authentication.py b/apps/users/models/authentication.py
index 61f84b81f1c7da924ab28e004da2ecfbd7c5247c..ca6682172c865ed57b20ab09ff3ecadfd416b9e8 100644
--- a/apps/users/models/authentication.py
+++ b/apps/users/models/authentication.py
@@ -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'))
diff --git a/apps/users/templates/users/login_log_list.html b/apps/users/templates/users/login_log_list.html
new file mode 100644
index 0000000000000000000000000000000000000000..8d2859b45282958253b0e91c69ed56eaea6b8e38
--- /dev/null
+++ b/apps/users/templates/users/login_log_list.html
@@ -0,0 +1,92 @@
+{% extends '_base_list.html' %}
+{% load i18n %}
+{% load static %}
+{% load common_tags %}
+{% block content_left_head %}
+
+
+{% endblock %}
+
+
+{% block table_search %}
+
+{% endblock %}
+
+{% block table_head %}
+ {% trans 'ID' %} |
+ {% trans 'Username' %} |
+ {% trans 'Type' %} |
+ {% trans 'UA' %} |
+ {% trans 'IP' %} |
+ {% trans 'City' %} |
+ {% trans 'Date' %} |
+{% endblock %}
+
+{% block table_body %}
+ {% for login_log in object_list %}
+
+ {{ forloop.counter }} |
+ {{ login_log.username }} |
+ {{ login_log.get_type_display }} |
+
+ {{ login_log.user_agent | truncatechars:20 }}
+ |
+ {{ login_log.ip }} |
+ {{ login_log.city }} |
+ {{ login_log.datetime }} |
+
+ {% endfor %}
+{% endblock %}
+
+{% block custom_foot_js %}
+
+
+{% endblock %}
+
diff --git a/apps/users/urls/views_urls.py b/apps/users/urls/views_urls.py
index e2100c7273c5f31353f69a717ead1d54d2c5351d..b8196434f49c38cf1f54fee2eb88da7baa49217b 100644
--- a/apps/users/urls/views_urls.py
+++ b/apps/users/urls/views_urls.py
@@ -43,4 +43,7 @@ urlpatterns = [
url(r'^user-group/(?P[0-9a-zA-Z\-]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(), name='user-group-asset-permission'),
url(r'^user-group/(?P[0-9a-zA-Z\-]+)/asset-permission/create$', views.UserGroupAssetPermissionCreateView.as_view(), name='user-group-asset-permission-create'),
url(r'^user-group/(?P[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'),
]
diff --git a/apps/users/utils.py b/apps/users/utils.py
index c62784e91785d53418897ae91fbff6a5c68a9fb5..f5abf658f8c04b1ce97c76ef9e3d040107a129b9 100644
--- a/apps/users/utils.py
+++ b/apps/users/utils.py
@@ -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
diff --git a/apps/users/views/login.py b/apps/users/views/login.py
index a09125c0287be2599d300367ff05a1662947f875..ee391de58965b4d0c151ef8928d4d844de76a113 100644
--- a/apps/users/views/login.py
+++ b/apps/users/views/login.py
@@ -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
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
index 952b041d6935bc4f985363f9cdcd89321d44c29b..c85da3b766694ab6befb1c6e1b8658ff80889652 100644
--- a/requirements/requirements.txt
+++ b/requirements/requirements.txt
@@ -1,4 +1,4 @@
-Django>=1.11
+Django==1.11
django-bootstrap3>=8.2.2
Pillow>=4.1.0
djangorestframework>=3.6.2