mirror of https://github.com/jumpserver/jumpserver
perf: account report
parent
060ef38169
commit
5b349ae231
|
@ -14,6 +14,10 @@ class Connectivity(TextChoices):
|
|||
NTLM_ERR = 'ntlm_err', _('NTLM credentials rejected error')
|
||||
CREATE_TEMPORARY_ERR = 'create_temp_err', _('Create temporary error')
|
||||
|
||||
@classmethod
|
||||
def as_dict(cls):
|
||||
return {choice.value: choice.label for choice in cls}
|
||||
|
||||
|
||||
class AutomationTypes(TextChoices):
|
||||
ping = 'ping', _('Ping')
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from .accouts import *
|
||||
from .assets import *
|
||||
from .report import *
|
||||
from .users import *
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from .account import *
|
|
@ -0,0 +1,97 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from collections import defaultdict
|
||||
|
||||
from django.db.models import Count, Q, F, Value
|
||||
from django.db.models.functions import Concat
|
||||
from django.http import JsonResponse
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from accounts.models import Account, AccountTemplate
|
||||
from assets.const import Connectivity
|
||||
from common.permissions import IsValidLicense
|
||||
from common.utils import lazyproperty
|
||||
from rbac.permissions import RBACPermission
|
||||
from reports.api.assets.base import group_stats
|
||||
from reports.mixins import DateRangeMixin
|
||||
|
||||
__all__ = ['AccountStatisticApi']
|
||||
|
||||
|
||||
class AccountStatisticApi(DateRangeMixin, APIView):
|
||||
http_method_names = ['get']
|
||||
# TODO: Define the required RBAC permissions for this API
|
||||
rbac_perms = {
|
||||
'GET': 'accounts.view_account',
|
||||
}
|
||||
permission_classes = [RBACPermission, IsValidLicense]
|
||||
|
||||
@lazyproperty
|
||||
def base_qs(self):
|
||||
return Account.objects.all()
|
||||
|
||||
@lazyproperty
|
||||
def template_qs(self):
|
||||
return AccountTemplate.objects.all()
|
||||
|
||||
def get_change_secret_account_metrics(self):
|
||||
filtered_queryset = self.filter_by_date_range(self.base_qs, 'date_change_secret')
|
||||
|
||||
data = defaultdict(set)
|
||||
for t, _id in filtered_queryset.values_list('date_change_secret', 'id'):
|
||||
date_str = str(t.date())
|
||||
data[date_str].add(_id)
|
||||
|
||||
metrics = [len(data.get(str(d), set())) for d in self.date_range_list]
|
||||
return metrics
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
qs = self.base_qs
|
||||
|
||||
stats = qs.aggregate(
|
||||
total=Count(1),
|
||||
active=Count(1, filter=Q(is_active=True)),
|
||||
connected=Count(1, filter=Q(connectivity=Connectivity.OK)),
|
||||
su_from=Count(1, filter=Q(su_from__isnull=False)),
|
||||
date_change_secret=Count(1, filter=Q(secret_reset=True)),
|
||||
)
|
||||
|
||||
stats['template_total'] = self.template_qs.count()
|
||||
|
||||
source_pie_data = [
|
||||
{'name': str(source), 'value': total}
|
||||
for source, total in
|
||||
qs.values('source').annotate(
|
||||
total=Count(1)
|
||||
).values_list('source', 'total')
|
||||
]
|
||||
|
||||
by_connectivity = group_stats(
|
||||
qs, 'label', 'connectivity', Connectivity.as_dict(),
|
||||
)
|
||||
|
||||
top_assets = qs.values('asset__name') \
|
||||
.annotate(account_count=Count('id')) \
|
||||
.order_by('-account_count')[:10]
|
||||
|
||||
top_version_accounts = qs.annotate(
|
||||
display_key=Concat(
|
||||
F('asset__name'),
|
||||
Value('('),
|
||||
F('username'),
|
||||
Value(')')
|
||||
)
|
||||
).values('display_key', 'version').order_by('-version')[:10]
|
||||
|
||||
payload = {
|
||||
'account_stats': stats,
|
||||
'top_assets': list(top_assets),
|
||||
'top_version_accounts': list(top_version_accounts),
|
||||
'source_pie': source_pie_data,
|
||||
'by_connectivity': by_connectivity,
|
||||
'change_secret_account_metrics': {
|
||||
'dates_metrics_date': self.dates_metrics_date,
|
||||
'dates_metrics_total': self.get_change_secret_account_metrics(),
|
||||
}
|
||||
}
|
||||
return JsonResponse(payload, status=200)
|
|
@ -0,0 +1,21 @@
|
|||
from django.db.models import Count, F
|
||||
|
||||
|
||||
def group_stats(queryset, alias, key, label_map=None):
|
||||
grouped = (
|
||||
queryset
|
||||
.exclude(**{f'{key}__isnull': True})
|
||||
.values(**{alias: F(key)})
|
||||
.annotate(total=Count('id'))
|
||||
)
|
||||
|
||||
data = [
|
||||
{
|
||||
alias: val,
|
||||
'total': cnt,
|
||||
**({'label': label_map.get(val, val)} if label_map else {})
|
||||
}
|
||||
for val, cnt in grouped.values_list(alias, 'total')
|
||||
]
|
||||
|
||||
return data
|
|
@ -9,5 +9,6 @@ urlpatterns = [
|
|||
path('reports/users/', api.UserReportApi.as_view(), name='user-list'),
|
||||
path('reports/user-change-password/', api.UserChangeSecretApi.as_view(), name='user-change-password'),
|
||||
path('reports/asset-statistic/', api.AssetStatisticApi.as_view(), name='asset-statistic'),
|
||||
path('reports/asset-activity/', api.AssetActivityApi.as_view(), name='asset-activity')
|
||||
path('reports/asset-activity/', api.AssetActivityApi.as_view(), name='asset-activity'),
|
||||
path('reports/account-statistic/', api.AccountStatisticApi.as_view(), name='account-statistic'),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue