perf: Change secret record table dashboard

pull/14557/head
feng 2024-12-02 11:36:20 +08:00 committed by feng626
parent 49a811963c
commit 81daf33b16
4 changed files with 82 additions and 5 deletions

View File

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.db.models import Max, Q, Subquery, OuterRef
from rest_framework import status, mixins from rest_framework import status, mixins
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from accounts import serializers from accounts import serializers
from accounts.const import AutomationTypes from accounts.const import AutomationTypes, ChangeSecretRecordStatusChoice
from accounts.filters import ChangeSecretRecordFilterSet from accounts.filters import ChangeSecretRecordFilterSet
from accounts.models import ChangeSecretAutomation, ChangeSecretRecord from accounts.models import ChangeSecretAutomation, ChangeSecretRecord
from accounts.tasks import execute_automation_record_task from accounts.tasks import execute_automation_record_task
@ -34,7 +35,8 @@ class ChangeSecretAutomationViewSet(OrgBulkModelViewSet):
class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet): class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
filterset_class = ChangeSecretRecordFilterSet filterset_class = ChangeSecretRecordFilterSet
search_fields = ('asset__address',) search_fields = ('asset__address', 'account_username')
ordering_fields = ('date_finished',)
tp = AutomationTypes.change_secret tp = AutomationTypes.change_secret
serializer_classes = { serializer_classes = {
'default': serializers.ChangeSecretRecordSerializer, 'default': serializers.ChangeSecretRecordSerializer,
@ -43,6 +45,8 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
rbac_perms = { rbac_perms = {
'execute': 'accounts.add_changesecretexecution', 'execute': 'accounts.add_changesecretexecution',
'secret': 'accounts.view_changesecretrecord', 'secret': 'accounts.view_changesecretrecord',
'dashboard': 'accounts.view_changesecretrecord',
'ignore_fail': 'accounts.view_changesecretrecord',
} }
def get_permissions(self): def get_permissions(self):
@ -53,9 +57,35 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
] ]
return super().get_permissions() return super().get_permissions()
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
if self.action == 'dashboard':
return self.get_dashboard_queryset(queryset)
return queryset
@staticmethod
def get_dashboard_queryset(queryset):
recent_dates = queryset.values('account').annotate(
max_date_finished=Max('date_finished')
)
recent_success_accounts = queryset.filter(
account=OuterRef('account'),
date_finished=Subquery(
recent_dates.filter(account=OuterRef('account')).values('max_date_finished')[:1]
)
).filter(Q(status=ChangeSecretRecordStatusChoice.success) | Q(ignore_fail=True))
failed_records = queryset.filter(
~Q(account__in=Subquery(recent_success_accounts.values('account'))),
status=ChangeSecretRecordStatusChoice.failed
)
return failed_records
def get_queryset(self): def get_queryset(self):
qs = ChangeSecretRecord.get_valid_records() qs = ChangeSecretRecord.get_valid_records()
return qs.objects.filter( return qs.filter(
execution__automation__type=self.tp execution__automation__type=self.tp
) )
@ -78,6 +108,17 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
serializer = self.get_serializer(instance) serializer = self.get_serializer(instance)
return Response(serializer.data) return Response(serializer.data)
@action(methods=['get'], detail=False, url_path='dashboard')
def dashboard(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@action(methods=['patch'], detail=True, url_path='ignore-fail')
def ignore_fail(self, request, *args, **kwargs):
instance = self.get_object()
instance.ignore_fail = True
instance.save(update_fields=['ignore_fail'])
return Response(status=status.HTTP_200_OK)
class ChangSecretExecutionViewSet(AutomationExecutionViewSet): class ChangSecretExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = ( rbac_perms = (

View File

@ -6,6 +6,7 @@ from django_filters import rest_framework as drf_filters
from assets.models import Node from assets.models import Node
from common.drf.filters import BaseFilterSet from common.drf.filters import BaseFilterSet
from common.utils.timezone import local_zero_hour, local_now
from .models import Account, GatheredAccount, ChangeSecretRecord from .models import Account, GatheredAccount, ChangeSecretRecord
@ -28,7 +29,7 @@ class AccountFilterSet(BaseFilterSet):
latest_updated = drf_filters.BooleanFilter(method='filter_latest') latest_updated = drf_filters.BooleanFilter(method='filter_latest')
latest_secret_changed = drf_filters.BooleanFilter(method='filter_latest') latest_secret_changed = drf_filters.BooleanFilter(method='filter_latest')
latest_secret_change_failed = drf_filters.BooleanFilter(method='filter_latest') latest_secret_change_failed = drf_filters.BooleanFilter(method='filter_latest')
risk = drf_filters.CharFilter(method='filter_risk',) risk = drf_filters.CharFilter(method='filter_risk', )
long_time_no_change_secret = drf_filters.BooleanFilter(method='filter_long_time') long_time_no_change_secret = drf_filters.BooleanFilter(method='filter_long_time')
long_time_no_verified = drf_filters.BooleanFilter(method='filter_long_time') long_time_no_verified = drf_filters.BooleanFilter(method='filter_long_time')
@ -127,6 +128,17 @@ class ChangeSecretRecordFilterSet(BaseFilterSet):
account_username = drf_filters.CharFilter(field_name='account__username', lookup_expr='icontains') account_username = drf_filters.CharFilter(field_name='account__username', lookup_expr='icontains')
execution_id = drf_filters.CharFilter(field_name='execution_id', lookup_expr='exact') execution_id = drf_filters.CharFilter(field_name='execution_id', lookup_expr='exact')
days = drf_filters.NumberFilter(method='filter_days')
@staticmethod
def filter_days(queryset, name, value):
value = int(value)
dt = local_zero_hour()
if value != 1:
dt = local_now() - timezone.timedelta(days=value)
return queryset.filter(date_finished__gte=dt)
class Meta: class Meta:
model = ChangeSecretRecord model = ChangeSecretRecord
fields = ['id', 'status', 'asset_id', 'execution'] fields = ['id', 'status', 'asset_id', 'execution']

View File

@ -0,0 +1,23 @@
# Generated by Django 4.1.13 on 2024-12-02 03:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0017_serviceintegration'),
]
operations = [
migrations.AddField(
model_name='changesecretrecord',
name='ignore_fail',
field=models.BooleanField(default=False, verbose_name='Ignore fail'),
),
migrations.AlterField(
model_name='changesecretrecord',
name='date_finished',
field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Date finished'),
),
]

View File

@ -37,7 +37,8 @@ class ChangeSecretRecord(JMSBaseModel):
old_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Old secret')) old_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Old secret'))
new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('New secret')) new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('New secret'))
date_started = models.DateTimeField(blank=True, null=True, verbose_name=_('Date started')) date_started = models.DateTimeField(blank=True, null=True, verbose_name=_('Date started'))
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('Date finished')) date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('Date finished'), db_index=True)
ignore_fail = models.BooleanField(default=False, verbose_name=_('Ignore fail'))
status = models.CharField( status = models.CharField(
max_length=16, verbose_name=_('Status'), default=ChangeSecretRecordStatusChoice.pending.value max_length=16, verbose_name=_('Status'), default=ChangeSecretRecordStatusChoice.pending.value
) )