mirror of https://github.com/jumpserver/jumpserver
perf: Add user report api
parent
f685ea457a
commit
ae881022dc
|
@ -167,10 +167,7 @@ class UserLoginLogViewSet(UserLoginCommonMixin, OrgReadonlyModelViewSet):
|
|||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
if current_org.is_root() or not settings.XPACK_ENABLED:
|
||||
return queryset
|
||||
users = self.get_org_member_usernames()
|
||||
queryset = queryset.filter(username__in=users)
|
||||
queryset = queryset.model.filter_login_queryset_by_org(queryset)
|
||||
return queryset
|
||||
|
||||
|
||||
|
|
|
@ -255,6 +255,15 @@ class UserLoginLog(models.Model):
|
|||
reason = old_reason_choices.get(self.reason, self.reason)
|
||||
return reason
|
||||
|
||||
@staticmethod
|
||||
def filter_login_queryset_by_org(queryset):
|
||||
from audits.utils import construct_userlogin_usernames
|
||||
if current_org.is_root() or not settings.XPACK_ENABLED:
|
||||
return queryset
|
||||
user_queryset = current_org.get_members()
|
||||
users = construct_userlogin_usernames(user_queryset)
|
||||
return queryset.filter(username__in=users)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-datetime", "username"]
|
||||
verbose_name = _("User login log")
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from collections import defaultdict
|
||||
|
||||
from django.db.models import Count
|
||||
from django.http.response import JsonResponse
|
||||
from django.utils import timezone
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from audits.const import LoginStatusChoices
|
||||
from audits.models import UserLoginLog
|
||||
from common.permissions import IsValidLicense
|
||||
from common.utils import lazyproperty
|
||||
from common.utils.timezone import local_zero_hour, local_now
|
||||
from rbac.permissions import RBACPermission
|
||||
|
||||
__all__ = ['UserReportApi']
|
||||
|
||||
|
||||
class UserReportApi(APIView):
|
||||
http_method_names = ['get']
|
||||
# TODO: Define the required RBAC permissions for this API
|
||||
rbac_perms = {
|
||||
}
|
||||
permission_classes = [RBACPermission, IsValidLicense]
|
||||
|
||||
@lazyproperty
|
||||
def days(self):
|
||||
count = self.request.query_params.get('days', 1)
|
||||
return int(count)
|
||||
|
||||
@property
|
||||
def days_to_datetime(self):
|
||||
if self.days == 1:
|
||||
return local_zero_hour()
|
||||
return local_now() - timezone.timedelta(days=self.days)
|
||||
|
||||
@lazyproperty
|
||||
def date_range_list(self):
|
||||
return [
|
||||
(local_now() - timezone.timedelta(days=i)).date()
|
||||
for i in range(self.days - 1, -1, -1)
|
||||
]
|
||||
|
||||
def filter_by_date_range(self, queryset, field_name):
|
||||
date_range_bounds = self.days_to_datetime.date(), (local_now() + timezone.timedelta(days=1)).date()
|
||||
return queryset.filter(**{f'{field_name}__range': date_range_bounds})
|
||||
|
||||
def get_user_login_metrics(self):
|
||||
filtered_queryset = self.filter_by_date_range(self.user_login_log_queryset, 'datetime')
|
||||
|
||||
data = defaultdict(set)
|
||||
for t, username in filtered_queryset.values_list('datetime', 'username'):
|
||||
date_str = str(t.date())
|
||||
data[date_str].add(username)
|
||||
|
||||
metrics = [len(v) for __, v in data]
|
||||
return metrics
|
||||
|
||||
def get_user_login_method_metrics(self):
|
||||
filtered_queryset = self.filter_by_date_range(self.user_login_log_queryset, 'datetime')
|
||||
|
||||
backends = set()
|
||||
data = defaultdict(lambda: defaultdict(set))
|
||||
for t, username, backend in filtered_queryset.values_list('datetime', 'username', 'backend'):
|
||||
backends.add(backend)
|
||||
date_str = str(t.date())
|
||||
data[date_str][backend].add(username)
|
||||
|
||||
metrics = defaultdict(list)
|
||||
for backend in backends:
|
||||
for date_str, usernames in data.items():
|
||||
metrics[backend].append(len(usernames.get(backend, set())))
|
||||
return metrics
|
||||
|
||||
def get_user_login_region_distribution(self):
|
||||
filtered_queryset = self.filter_by_date_range(self.user_login_log_queryset, 'datetime')
|
||||
|
||||
data = filtered_queryset.values('city').annotate(
|
||||
user_count=Count('username', distinct=True)
|
||||
).order_by('-user_count')
|
||||
return list(data)
|
||||
|
||||
@lazyproperty
|
||||
def user_login_log_queryset(self):
|
||||
queryset = UserLoginLog.objects.filter(status=LoginStatusChoices.success)
|
||||
return UserLoginLog.filter_login_queryset_by_org(queryset)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
query_params = self.request.query_params
|
||||
data = {}
|
||||
|
||||
_all = query_params.get('all')
|
||||
|
||||
if _all or query_params.get('user_login_log_metrics'):
|
||||
metrics = self.get_user_login_metrics()
|
||||
data.update({
|
||||
'dates_metrics_date': [date.strftime('%m-%d') for date in self.date_range_list] or ['0'],
|
||||
'dates_metrics_total': metrics,
|
||||
})
|
||||
|
||||
if _all or query_params.get('user_login_method_metrics'):
|
||||
metrics = self.get_user_login_method_metrics()
|
||||
data.update({
|
||||
'dates_metrics_date': [date.strftime('%m-%d') for date in self.date_range_list] or ['0'],
|
||||
'dates_metrics_total': metrics,
|
||||
})
|
||||
|
||||
if _all or query_params.get('user_login_region_distribution'):
|
||||
data.update({
|
||||
'user_login_region_distribution': self.get_user_login_region_distribution(),
|
||||
})
|
||||
|
||||
return JsonResponse(data, status=200)
|
|
@ -1,19 +1,19 @@
|
|||
import base64
|
||||
import io
|
||||
import urllib.parse
|
||||
from io import BytesIO
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.http import FileResponse, HttpResponseBadRequest
|
||||
from django.http import JsonResponse
|
||||
from django.utils import timezone
|
||||
from django.conf import settings
|
||||
from django.http import FileResponse, HttpResponseBadRequest
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from playwright.sync_api import sync_playwright
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from pdf2image import convert_from_bytes
|
||||
import base64
|
||||
from io import BytesIO
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
charts_map = {
|
||||
"UserActivity": {
|
||||
|
|
Loading…
Reference in New Issue