mirror of https://github.com/jumpserver/jumpserver
perf: Change secret record table dashboard
parent
49a811963c
commit
81daf33b16
|
@ -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 = (
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue