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 %} +
    +
    +
    + + + to + +
    +
    +
    + +
    +
    + +
    +
    +
    + +
    +
    +
    +{% 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