perf: update pam

pull/14774/head^2
ibuler 2025-01-06 19:14:11 +08:00
commit 191212ca96
10 changed files with 100 additions and 22 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

@ -1,4 +1,6 @@
from django.db import transaction
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.generics import ListAPIView, CreateAPIView from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.response import Response from rest_framework.response import Response
@ -14,10 +16,12 @@ from authentication.permissions import UserConfirmation, ConfirmType
from common.api.mixin import ExtraFilterFieldsMixin from common.api.mixin import ExtraFilterFieldsMixin
from common.drf.filters import AttrRulesFilterBackend from common.drf.filters import AttrRulesFilterBackend
from common.permissions import IsValidUser from common.permissions import IsValidUser
from common.utils import lazyproperty from common.utils import lazyproperty, get_logger
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission from rbac.permissions import RBACPermission
logger = get_logger(__file__)
__all__ = [ __all__ = [
'AccountViewSet', 'AccountSecretsViewSet', 'AccountViewSet', 'AccountSecretsViewSet',
'AccountHistoriesSecretAPI', 'AssetAccountBulkCreateApi', 'AccountHistoriesSecretAPI', 'AssetAccountBulkCreateApi',
@ -109,10 +113,12 @@ class AccountViewSet(OrgBulkModelViewSet):
account_data['asset'] = asset account_data['asset'] = asset
creation_results[asset] = {'state': 'created'} creation_results[asset] = {'state': 'created'}
try: try:
with transaction.atomic():
self.model.objects.create(**account_data) self.model.objects.create(**account_data)
success_count += 1 success_count += 1
except Exception as e: except Exception as e:
creation_results[asset] = {'error': str(e), 'state': 'error'} logger.debug(f'{ "Move" if move else "Copy" } to assets error: {e}')
creation_results[asset] = {'error': _('Account already exists'), 'state': 'error'}
results = [{'asset': str(asset), **res} for asset, res in creation_results.items()] results = [{'asset': str(asset), **res} for asset, res in creation_results.items()]

View File

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
#
from django.http.response import JsonResponse
from rest_framework.views import APIView
from accounts.models import Account, RiskChoice
from common.utils.timezone import local_monday
__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_accounts'):
data['total_accounts'] = account_count
if query_params.get('total_week_add_accounts'):
monday_time = local_monday()
data['total_week_add_accounts'] = Account.objects.filter(date_created__gte=monday_time).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(risk_type=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_long_time_no_login_accounts'):
data['total_long_time_no_login_accounts'] = Account.get_risks(risk_type=RiskChoice.long_time_no_login).count()
if query_params.get('total_weak_password_accounts'):
data['total_weak_password_accounts'] = Account.get_risks(risk_type=RiskChoice.weak_password).count()
if query_params.get('total_long_time_change_password_accounts'):
data['total_long_time_change_password_accounts'] = Account.get_risks(risk_type=RiskChoice.long_time_password).count()
return JsonResponse(data, status=200)

View File

@ -54,7 +54,7 @@ class BaseChangeSecretPushManager(AccountBasePlaybookManager):
asset = privilege_account.asset asset = privilege_account.asset
accounts = asset.accounts.all() accounts = asset.accounts.all()
accounts = accounts.filter(id__in=self.account_ids) accounts = accounts.filter(id__in=self.account_ids, secret_reset=True)
if self.secret_type: if self.secret_type:
accounts = accounts.filter(secret_type=self.secret_type) accounts = accounts.filter(secret_type=self.secret_type)

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()}],
) )
) )
@ -358,7 +358,6 @@ class GatherAccountsManager(AccountBasePlaybookManager):
for asset, accounts_data in self.asset_account_info.items(): for asset, accounts_data in self.asset_account_info.items():
ori_users = self.ori_asset_usernames[str(asset.id)] ori_users = self.ori_asset_usernames[str(asset.id)]
with tmp_to_org(asset.org_id): with tmp_to_org(asset.org_id):
gathered_accounts = []
for d in accounts_data: for d in accounts_data:
username = d["username"] username = d["username"]
ori_account = self.ori_gathered_accounts_mapper.get( ori_account = self.ori_gathered_accounts_mapper.get(
@ -374,6 +373,9 @@ class GatherAccountsManager(AccountBasePlaybookManager):
self.create_gathered_account.finish() self.create_gathered_account.finish()
self.update_gathered_account.finish() self.update_gathered_account.finish()
self.update_gather_accounts_status(asset) self.update_gather_accounts_status(asset)
if not self.is_sync_account:
continue
gathered_accounts = GatheredAccount.objects.filter(asset=asset)
GatheredAccount.sync_accounts(gathered_accounts, self.is_sync_account) GatheredAccount.sync_accounts(gathered_accounts, self.is_sync_account)
# 因为有 bulk create, bulk update, 所以这里需要 sleep 一下,等待数据同步 # 因为有 bulk create, bulk update, 所以这里需要 sleep 一下,等待数据同步
time.sleep(0.5) time.sleep(0.5)

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.db.models import Q, F, Value, CharField from django.db.models import Q
from django.db.models.functions import Concat
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

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,23 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
return escape(value) return escape(value)
@classmethod
def get_risks(cls, queryset=None, risk_type=None):
# TODO 数据量大时子查询性能不佳考虑用原生sql或者在模型层面做出改动
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'),
] ]