mirror of https://github.com/jumpserver/jumpserver
perf: update pam
commit
191212ca96
|
@ -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 *
|
|
||||||
|
|
|
@ -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()]
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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():
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue