perf: Pam dashboard

pull/14760/head
feng 2025-01-03 17:47:18 +08:00 committed by feng626
parent afc1d6610f
commit 83dc15d441
8 changed files with 76 additions and 26 deletions

View File

@ -1,5 +1,6 @@
from .account import * from .account import *
from .application import *
from .pam_dashboard import *
from .task import * from .task import *
from .template import * from .template import *
from .virtual import * from .virtual import *
from .application import *

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
#
from django.http.response import JsonResponse
from rest_framework.views import APIView
from accounts.models import Account, RiskChoice
__all__ = ['PamDashboardApi']
class PamDashboardApi(APIView):
http_method_names = ['get']
rbac_perms = {
'GET': 'accounts.view_account',
}
def get(self, request, *args, **kwargs):
query_params = self.request.query_params
data = {}
account_count = Account.objects.count()
privileged_account_count = Account.objects.filter(privileged=True).count()
if query_params.get('total_privileged_accounts'):
data['total_privileged_accounts'] = privileged_account_count
if query_params.get('total_ordinary_accounts'):
data['total_ordinary_accounts'] = account_count - privileged_account_count
if query_params.get('total_unmanaged_accounts'):
data['total_unmanaged_accounts'] = Account.get_risks(RiskChoice.new_found).count()
if query_params.get('total_unavailable_accounts'):
data['total_unavailable_accounts'] = Account.objects.filter(is_active=False).count()
if query_params.get('total_weak_password_accounts'):
data['total_weak_password_accounts'] = Account.get_risks(RiskChoice.weak_password)
if query_params.get('total_long_time_change_password_accounts'):
data['total_long_time_change_password_accounts'] = Account.get_risks(RiskChoice.long_time_password)
return JsonResponse(data, status=200)

View File

@ -3,7 +3,7 @@ from collections import defaultdict
from django.utils import timezone from django.utils import timezone
from accounts.models import Account, AccountRisk from accounts.models import Account, AccountRisk, RiskChoice
from assets.automations.base.manager import BaseManager from assets.automations.base.manager import BaseManager
from common.decorators import bulk_create_decorator, bulk_update_decorator from common.decorators import bulk_create_decorator, bulk_update_decorator
from common.utils.strings import color_fmt from common.utils.strings import color_fmt
@ -69,12 +69,12 @@ def check_account_secrets(accounts, assets):
if is_weak_password(account.secret): if is_weak_password(account.secret):
print(tmpl % (account, color_fmt("weak", "red"))) print(tmpl % (account, color_fmt("weak", "red")))
summary["weak_password"] += 1 summary[RiskChoice.weak_password] += 1
result["weak_password"].append(result_item) result[RiskChoice.weak_password].append(result_item)
risks.append( risks.append(
{ {
"account": account, "account": account,
"risk": "weak_password", "risk": RiskChoice.weak_password,
} }
) )
else: else:
@ -143,7 +143,7 @@ class CheckAccountManager(BaseManager):
"\n---\nSummary: \nok: %s, weak password: %s, no secret: %s, using time: %ss" "\n---\nSummary: \nok: %s, weak password: %s, no secret: %s, using time: %ss"
% ( % (
self.summary["ok"], self.summary["ok"],
self.summary["weak_password"], self.summary[RiskChoice.weak_password],
self.summary["no_secret"], self.summary["no_secret"],
int(self.duration), int(self.duration),
) )

View File

@ -4,7 +4,7 @@ from collections import defaultdict
from django.utils import timezone from django.utils import timezone
from accounts.const import AutomationTypes from accounts.const import AutomationTypes
from accounts.models import GatheredAccount, Account, AccountRisk from accounts.models import GatheredAccount, Account, AccountRisk, RiskChoice
from common.const import ConfirmOrIgnore from common.const import ConfirmOrIgnore
from common.decorators import bulk_create_decorator, bulk_update_decorator from common.decorators import bulk_create_decorator, bulk_update_decorator
from common.utils import get_logger from common.utils import get_logger
@ -68,7 +68,7 @@ class AnalyseAccountRisk:
{"field": "date_last_login", "risk": "long_time_no_login", "delta": long_time}, {"field": "date_last_login", "risk": "long_time_no_login", "delta": long_time},
{ {
"field": "date_password_change", "field": "date_password_change",
"risk": "long_time_password", "risk": RiskChoice.long_time_password,
"delta": long_time, "delta": long_time,
}, },
{ {
@ -164,7 +164,7 @@ class AnalyseAccountRisk:
self._create_risk( self._create_risk(
dict( dict(
**basic, **basic,
risk="new_found", risk=RiskChoice.new_found,
details=[{"datetime": self.now.isoformat()}], details=[{"datetime": self.now.isoformat()}],
) )
) )

View File

@ -1,13 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.db.models import Q, Exists, OuterRef from django.db.models import Q
from django.utils import timezone from django.utils import timezone
from django_filters import rest_framework as drf_filters 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 common.utils.timezone import local_zero_hour, local_now
from .models import Account, GatheredAccount, ChangeSecretRecord, AccountRisk from .models import Account, GatheredAccount, ChangeSecretRecord
class AccountFilterSet(BaseFilterSet): class AccountFilterSet(BaseFilterSet):
@ -62,16 +62,7 @@ class AccountFilterSet(BaseFilterSet):
if not value: if not value:
return queryset return queryset
queryset = queryset.filter( return Account.get_risks(queryset, value)
Exists(
AccountRisk.objects.filter(
risk=value,
asset_id=OuterRef('asset_id'),
username=OuterRef('username')
)
)
)
return queryset
@staticmethod @staticmethod
def filter_latest(queryset, name, value): def filter_latest(queryset, name, value):

View File

@ -1,4 +1,5 @@
from django.db import models from django.db import models
from django.db.models import Exists, OuterRef
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
@ -165,6 +166,22 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
return escape(value) return escape(value)
@classmethod
def get_risks(cls, queryset=None, risk_type=None):
from accounts.models import AccountRisk
subquery = AccountRisk.objects.filter(
asset_id=OuterRef('asset_id'),
username=OuterRef('username')
)
if risk_type:
subquery = subquery.filter(risk=risk_type)
if queryset is None:
queryset = cls.objects.all()
return queryset.filter(Exists(subquery))
def replace_history_model_with_mixin(): def replace_history_model_with_mixin():
""" """

View File

@ -1,15 +1,13 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from accounts.const import AutomationTypes from accounts.const import AutomationTypes
from common.const import ConfirmOrIgnore
from accounts.models import ( from accounts.models import (
GatheredAccount, GatheredAccount,
AccountRisk, AccountRisk,
SecretType, SecretType,
AutomationExecution, AutomationExecution, RiskChoice,
) )
from django.utils import timezone
from common.const import ConfirmOrIgnore from common.const import ConfirmOrIgnore
TYPE_CHOICES = [ TYPE_CHOICES = [
@ -83,7 +81,7 @@ class RiskHandler:
GatheredAccount.objects.filter(asset=self.asset, username=self.username).update( GatheredAccount.objects.filter(asset=self.asset, username=self.username).update(
present=True, status=ConfirmOrIgnore.confirmed present=True, status=ConfirmOrIgnore.confirmed
) )
self.risk = "new_found" self.risk = RiskChoice.new_found
def handle_disable_remote(self): def handle_disable_remote(self):
pass pass

View File

@ -49,6 +49,7 @@ urlpatterns = [
path('push-account/<uuid:pk>/nodes/', api.PushAccountNodeAddRemoveApi.as_view(), path('push-account/<uuid:pk>/nodes/', api.PushAccountNodeAddRemoveApi.as_view(),
name='push-account-add-or-remove-node'), name='push-account-add-or-remove-node'),
path('push-account/<uuid:pk>/assets/', api.PushAccountAssetsListApi.as_view(), name='push-account-assets'), path('push-account/<uuid:pk>/assets/', api.PushAccountAssetsListApi.as_view(), name='push-account-assets'),
path('pam-dashboard/', api.PamDashboardApi.as_view(), name='pam-dashboard'),
path('change-secret-dashboard/', api.ChangeSecretDashboardApi.as_view(), name='change-secret-dashboard'), path('change-secret-dashboard/', api.ChangeSecretDashboardApi.as_view(), name='change-secret-dashboard'),
] ]