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 .application import *
from .pam_dashboard import *
from .task import *
from .template 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.utils.translation import gettext_lazy as _
from rest_framework.decorators import action
from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.response import Response
@ -14,10 +16,12 @@ from authentication.permissions import UserConfirmation, ConfirmType
from common.api.mixin import ExtraFilterFieldsMixin
from common.drf.filters import AttrRulesFilterBackend
from common.permissions import IsValidUser
from common.utils import lazyproperty
from common.utils import lazyproperty, get_logger
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
logger = get_logger(__file__)
__all__ = [
'AccountViewSet', 'AccountSecretsViewSet',
'AccountHistoriesSecretAPI', 'AssetAccountBulkCreateApi',
@ -109,10 +113,12 @@ class AccountViewSet(OrgBulkModelViewSet):
account_data['asset'] = asset
creation_results[asset] = {'state': 'created'}
try:
self.model.objects.create(**account_data)
success_count += 1
with transaction.atomic():
self.model.objects.create(**account_data)
success_count += 1
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()]

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
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:
accounts = accounts.filter(secret_type=self.secret_type)

View File

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

View File

@ -4,7 +4,7 @@ from collections import defaultdict
from django.utils import timezone
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.decorators import bulk_create_decorator, bulk_update_decorator
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_password_change",
"risk": "long_time_password",
"risk": RiskChoice.long_time_password,
"delta": long_time,
},
{
@ -164,7 +164,7 @@ class AnalyseAccountRisk:
self._create_risk(
dict(
**basic,
risk="new_found",
risk=RiskChoice.new_found,
details=[{"datetime": self.now.isoformat()}],
)
)
@ -358,7 +358,6 @@ class GatherAccountsManager(AccountBasePlaybookManager):
for asset, accounts_data in self.asset_account_info.items():
ori_users = self.ori_asset_usernames[str(asset.id)]
with tmp_to_org(asset.org_id):
gathered_accounts = []
for d in accounts_data:
username = d["username"]
ori_account = self.ori_gathered_accounts_mapper.get(
@ -374,6 +373,9 @@ class GatherAccountsManager(AccountBasePlaybookManager):
self.create_gathered_account.finish()
self.update_gathered_account.finish()
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)
# 因为有 bulk create, bulk update, 所以这里需要 sleep 一下,等待数据同步
time.sleep(0.5)

View File

@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
#
from django.db.models import Q, F, Value, CharField
from django.db.models.functions import Concat
from django.db.models import Q
from django.utils import timezone
from django_filters import rest_framework as drf_filters

View File

@ -1,4 +1,5 @@
from django.db import models
from django.db.models import Exists, OuterRef
from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords
@ -165,6 +166,23 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
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():
"""

View File

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

View File

@ -49,6 +49,7 @@ urlpatterns = [
path('push-account/<uuid:pk>/nodes/', api.PushAccountNodeAddRemoveApi.as_view(),
name='push-account-add-or-remove-node'),
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'),
]