mirror of https://github.com/jumpserver/jumpserver
Merge branch 'pam' of github.com:jumpserver/jumpserver into pam
commit
f223960b6a
|
@ -187,7 +187,7 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
|
|||
|
||||
@property
|
||||
def latest_change_secret_record(self) -> ChangeSecretRecord:
|
||||
return self.account.change_secret_records.filter(
|
||||
return self.account.changesecretrecords.filter(
|
||||
status=ChangeSecretRecordStatusChoice.pending
|
||||
).order_by('-date_created').first()
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from collections import defaultdict
|
||||
|
||||
from django.db.models import Count, F, Q
|
||||
from django.http.response import JsonResponse
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from accounts.models import (
|
||||
Account, RiskChoice, GatherAccountsAutomation,
|
||||
Account, GatherAccountsAutomation,
|
||||
PushAccountAutomation, BackupAccountAutomation,
|
||||
AccountRisk, IntegrationApplication, ChangeSecretAutomation
|
||||
)
|
||||
|
@ -23,126 +25,109 @@ class PamDashboardApi(APIView):
|
|||
|
||||
@staticmethod
|
||||
def get_type_to_accounts():
|
||||
result = Account.objects.annotate(type=F('asset__platform__type')). \
|
||||
values('type').order_by('type').annotate(total=Count(1))
|
||||
result = Account.objects.annotate(type=F('asset__platform__type')) \
|
||||
.values('type').order_by('type').annotate(total=Count(1))
|
||||
all_types_dict = dict(AllTypes.choices())
|
||||
|
||||
result = [
|
||||
{
|
||||
**i,
|
||||
'label': all_types_dict.get(i['type'], i['type'])
|
||||
}
|
||||
return [
|
||||
{**i, 'label': all_types_dict.get(i['type'], i['type'])}
|
||||
for i in result
|
||||
]
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def get_account_risk_data(_all, query_params):
|
||||
agg_map = {
|
||||
'total_privileged_accounts': ('long_time_no_login_count', Q(risk='long_time_no_login')),
|
||||
'total_new_found_accounts': ('new_found_count', Q(risk='new_found')),
|
||||
'total_group_changed_accounts': ('group_changed_count', Q(risk='group_changed')),
|
||||
'total_sudo_changed_accounts': ('sudo_changed_count', Q(risk='sudo_changed')),
|
||||
'total_authorized_keys_changed_accounts': (
|
||||
'authorized_keys_changed_count', Q(risk='authorized_keys_changed')),
|
||||
'total_account_deleted_accounts': ('account_deleted_count', Q(risk='account_deleted')),
|
||||
'total_password_expired_accounts': ('password_expired_count', Q(risk='password_expired')),
|
||||
'total_long_time_password_accounts': ('long_time_password_count', Q(risk='long_time_password')),
|
||||
'total_weak_password_accounts': ('weak_password_count', Q(risk='weak_password')),
|
||||
'total_leaked_password_accounts': ('leaked_password_count', Q(risk='leaked_password')),
|
||||
'total_repeated_password_accounts': ('repeated_password_count', Q(risk='repeated_password')),
|
||||
'total_password_error_accounts': ('password_error_count', Q(risk='password_error')),
|
||||
'total_no_admin_account_accounts': ('no_admin_account_count', Q(risk='no_admin_account')),
|
||||
}
|
||||
|
||||
aggregations = {
|
||||
agg_key: Count('account_id', distinct=True, filter=agg_filter)
|
||||
for param_key, (agg_key, agg_filter) in agg_map.items()
|
||||
if _all or query_params.get(param_key)
|
||||
}
|
||||
|
||||
data = {}
|
||||
if aggregations:
|
||||
account_stats = AccountRisk.objects.filter(account__isnull=False).aggregate(**aggregations)
|
||||
data = {param_key: account_stats.get(agg_key) for param_key, (agg_key, _) in agg_map.items() if
|
||||
agg_key in account_stats}
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def get_account_data(_all, query_params):
|
||||
agg_map = {
|
||||
'total_accounts': ('total_count', Count('id')),
|
||||
'total_privileged_accounts': ('privileged_count', Count('id', filter=Q(privileged=True))),
|
||||
'total_connectivity_ok_accounts': ('connectivity_ok_count', Count('id', filter=Q(connectivity='ok'))),
|
||||
'total_secret_reset_accounts': ('secret_reset_count', Count('id', filter=Q(secret_reset=True))),
|
||||
'total_unavailable_accounts': ('unavailable_count', Count('id', filter=Q(is_active=False))),
|
||||
'total_week_add_accounts': ('week_add_count', Count('id', filter=Q(date_created__gte=local_monday()))),
|
||||
}
|
||||
|
||||
aggregations = {
|
||||
agg_key: agg_expr
|
||||
for param_key, (agg_key, agg_expr) in agg_map.items()
|
||||
if _all or query_params.get(param_key)
|
||||
}
|
||||
|
||||
data = {}
|
||||
account_stats = Account.objects.aggregate(**aggregations)
|
||||
for param_key, (agg_key, __) in agg_map.items():
|
||||
if agg_key in account_stats:
|
||||
data[param_key] = account_stats[agg_key]
|
||||
|
||||
if _all or query_params.get('total_ordinary_accounts'):
|
||||
if 'total_count' in account_stats and 'privileged_count' in account_stats:
|
||||
data['total_ordinary_accounts'] = \
|
||||
account_stats['total_count'] - account_stats['privileged_count']
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def get_automation_counts(_all, query_params):
|
||||
automation_counts = defaultdict(int)
|
||||
automation_models = {
|
||||
'total_count_change_secret_automation': ChangeSecretAutomation,
|
||||
'total_count_gathered_account_automation': GatherAccountsAutomation,
|
||||
'total_count_push_account_automation': PushAccountAutomation,
|
||||
'total_count_backup_account_automation': BackupAccountAutomation,
|
||||
'total_count_risk_account': AccountRisk,
|
||||
'total_count_integration_application': IntegrationApplication,
|
||||
}
|
||||
|
||||
for param_key, model in automation_models.items():
|
||||
if _all or query_params.get(param_key):
|
||||
automation_counts[param_key] = model.objects.count()
|
||||
|
||||
return automation_counts
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
monday_time = local_monday()
|
||||
query_params = self.request.query_params
|
||||
|
||||
_all = query_params.get('all')
|
||||
|
||||
agg_map = {
|
||||
'total_accounts': (
|
||||
'total_count',
|
||||
Count('id')
|
||||
),
|
||||
'total_privileged_accounts': (
|
||||
'privileged_count',
|
||||
Count('id', filter=Q(privileged=True))
|
||||
),
|
||||
'total_connectivity_ok_accounts': (
|
||||
'connectivity_ok_count',
|
||||
Count('id', filter=Q(connectivity='ok'))
|
||||
),
|
||||
'total_secret_reset_accounts': (
|
||||
'secret_reset_count',
|
||||
Count('id', filter=Q(secret_reset=True))
|
||||
),
|
||||
'total_unavailable_accounts': (
|
||||
'unavailable_count',
|
||||
Count('id', filter=Q(is_active=False))
|
||||
),
|
||||
'total_week_add_accounts': (
|
||||
'week_add_count',
|
||||
Count('id', filter=Q(date_created__gte=monday_time))
|
||||
),
|
||||
}
|
||||
|
||||
aggregations = {}
|
||||
for param_key, (agg_key, agg_expr) in agg_map.items():
|
||||
if _all or query_params.get(param_key):
|
||||
aggregations[agg_key] = agg_expr
|
||||
|
||||
data = {}
|
||||
if aggregations:
|
||||
account_stats = Account.objects.aggregate(**aggregations)
|
||||
for param_key, (agg_key, __) in agg_map.items():
|
||||
if agg_key in account_stats:
|
||||
data[param_key] = account_stats[agg_key]
|
||||
|
||||
if (_all or query_params.get('total_ordinary_accounts')):
|
||||
if 'total_count' in account_stats and 'privileged_count' in account_stats:
|
||||
data['total_ordinary_accounts'] = \
|
||||
account_stats['total_count'] - account_stats['privileged_count']
|
||||
|
||||
if _all or query_params.get('total_unmanaged_accounts'):
|
||||
data['total_unmanaged_accounts'] = Account.get_risks(
|
||||
risk_type=RiskChoice.new_found).count()
|
||||
|
||||
if _all or 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 _all or query_params.get('total_weak_password_accounts'):
|
||||
data['total_weak_password_accounts'] = Account.get_risks(
|
||||
risk_type=RiskChoice.weak_password).count()
|
||||
|
||||
if _all or 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()
|
||||
|
||||
if _all or query_params.get('total_leaked_password_accounts'):
|
||||
data['total_leaked_password_accounts'] = Account.get_risks(
|
||||
risk_type=RiskChoice.leaked_password).count()
|
||||
|
||||
if _all or query_params.get('total_repeated_password_accounts'):
|
||||
data['total_repeated_password_accounts'] = Account.get_risks(
|
||||
risk_type=RiskChoice.repeated_password).count()
|
||||
data.update(self.get_account_data(_all, query_params))
|
||||
data.update(self.get_account_risk_data(_all, query_params))
|
||||
data.update(self.get_automation_counts(_all, query_params))
|
||||
|
||||
if _all or query_params.get('total_count_type_to_accounts'):
|
||||
data.update({
|
||||
'total_count_type_to_accounts': self.get_type_to_accounts(),
|
||||
})
|
||||
|
||||
if _all or query_params.get('total_count_change_secret_automation'):
|
||||
data.update({
|
||||
'total_count_change_secret_automation': ChangeSecretAutomation.objects.count()
|
||||
})
|
||||
|
||||
if _all or query_params.get('total_count_gathered_account_automation'):
|
||||
data.update({
|
||||
'total_count_gathered_account_automation': GatherAccountsAutomation.objects.count()
|
||||
})
|
||||
|
||||
if _all or query_params.get('total_count_push_account_automation'):
|
||||
data.update({
|
||||
'total_count_push_account_automation': PushAccountAutomation.objects.count()
|
||||
})
|
||||
|
||||
if _all or query_params.get('total_count_backup_account_automation'):
|
||||
data.update({
|
||||
'total_count_backup_account_automation': BackupAccountAutomation.objects.count()
|
||||
})
|
||||
|
||||
if _all or query_params.get('total_count_risk_account'):
|
||||
data.update({
|
||||
'total_count_risk_account': AccountRisk.objects.count()
|
||||
})
|
||||
|
||||
if _all or query_params.get('total_count_integration_application'):
|
||||
data.update({
|
||||
'total_count_integration_application': IntegrationApplication.objects.count()
|
||||
})
|
||||
|
||||
return JsonResponse(data, status=200)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework import mixins
|
||||
|
||||
from accounts import serializers
|
||||
from accounts.const import AutomationTypes
|
||||
from accounts.models import PushAccountAutomation
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
|
||||
from accounts.filters import PushAccountRecordFilterSet
|
||||
from accounts.models import PushAccountAutomation, PushSecretRecord
|
||||
from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet
|
||||
from .base import (
|
||||
AutomationAssetsListApi, AutomationRemoveAssetApi, AutomationAddAssetApi,
|
||||
AutomationNodeAddRemoveApi, AutomationExecutionViewSet
|
||||
|
@ -13,6 +15,7 @@ from .base import (
|
|||
__all__ = [
|
||||
'PushAccountAutomationViewSet', 'PushAccountAssetsListApi', 'PushAccountRemoveAssetApi',
|
||||
'PushAccountAddAssetApi', 'PushAccountNodeAddRemoveApi', 'PushAccountExecutionViewSet',
|
||||
'PushAccountRecordViewSet'
|
||||
]
|
||||
|
||||
|
||||
|
@ -39,6 +42,22 @@ class PushAccountExecutionViewSet(AutomationExecutionViewSet):
|
|||
return queryset
|
||||
|
||||
|
||||
class PushAccountRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
|
||||
filterset_class = PushAccountRecordFilterSet
|
||||
search_fields = ('asset__address', 'account_username')
|
||||
ordering_fields = ('date_finished',)
|
||||
tp = AutomationTypes.push_account
|
||||
serializer_classes = {
|
||||
'default': serializers.PushSecretRecordSerializer,
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
qs = PushSecretRecord.get_valid_records()
|
||||
return qs.filter(
|
||||
execution__automation__type=self.tp
|
||||
)
|
||||
|
||||
|
||||
class PushAccountAssetsListApi(AutomationAssetsListApi):
|
||||
model = PushAccountAutomation
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
from copy import deepcopy
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from accounts.automations.methods import platform_automation_methods
|
||||
from accounts.const import SSHKeyStrategy, SecretStrategy, SecretType
|
||||
from accounts.const import SSHKeyStrategy, SecretStrategy, SecretType, ChangeSecretRecordStatusChoice
|
||||
from accounts.models import BaseAccountQuerySet
|
||||
from assets.automations.base.manager import BasePlaybookManager
|
||||
from assets.const import HostTypes
|
||||
from common.db.utils import safe_db_connection
|
||||
from common.utils import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
@ -32,6 +34,8 @@ class BaseChangeSecretPushManager(AccountBasePlaybookManager):
|
|||
'ssh_key_change_strategy', SSHKeyStrategy.set_jms
|
||||
)
|
||||
self.account_ids = self.execution.snapshot['accounts']
|
||||
self.record_map = self.execution.snapshot.get('record_map', {}) # 这个是某个失败的记录重试
|
||||
self.name_recorder_mapper = {} # 做个映射,方便后面处理
|
||||
|
||||
def gen_account_inventory(self, account, asset, h, path_dir):
|
||||
raise NotImplementedError
|
||||
|
@ -119,3 +123,51 @@ class BaseChangeSecretPushManager(AccountBasePlaybookManager):
|
|||
inventory_hosts.append(h)
|
||||
|
||||
return inventory_hosts
|
||||
|
||||
def on_host_success(self, host, result):
|
||||
recorder = self.name_recorder_mapper.get(host)
|
||||
if not recorder:
|
||||
return
|
||||
recorder.status = ChangeSecretRecordStatusChoice.success.value
|
||||
recorder.date_finished = timezone.now()
|
||||
|
||||
account = recorder.account
|
||||
if not account:
|
||||
print("Account not found, deleted ?")
|
||||
return
|
||||
|
||||
account.secret = getattr(recorder, 'new_secret', account.secret)
|
||||
account.date_updated = timezone.now()
|
||||
|
||||
with safe_db_connection():
|
||||
recorder.save(update_fields=['status', 'date_finished'])
|
||||
account.save(update_fields=['secret', 'date_updated'])
|
||||
|
||||
self.summary['ok_accounts'] += 1
|
||||
self.result['ok_accounts'].append(
|
||||
{
|
||||
"asset": str(account.asset),
|
||||
"username": account.username,
|
||||
}
|
||||
)
|
||||
super().on_host_success(host, result)
|
||||
|
||||
def on_host_error(self, host, error, result):
|
||||
recorder = self.name_recorder_mapper.get(host)
|
||||
if not recorder:
|
||||
return
|
||||
recorder.status = ChangeSecretRecordStatusChoice.failed.value
|
||||
recorder.date_finished = timezone.now()
|
||||
recorder.error = error
|
||||
try:
|
||||
recorder.save()
|
||||
except Exception as e:
|
||||
print(f"\033[31m Save {host} recorder error: {e} \033[0m\n")
|
||||
self.summary['fail_accounts'] += 1
|
||||
self.result['fail_accounts'].append(
|
||||
{
|
||||
"asset": str(recorder.asset),
|
||||
"username": recorder.account.username,
|
||||
}
|
||||
)
|
||||
super().on_host_error(host, error, result)
|
||||
|
|
|
@ -2,7 +2,6 @@ import os
|
|||
import time
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from xlsxwriter import Workbook
|
||||
|
||||
|
@ -12,7 +11,6 @@ from accounts.const import (
|
|||
from accounts.models import ChangeSecretRecord
|
||||
from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretReportMsg
|
||||
from accounts.serializers import ChangeSecretRecordBackUpSerializer
|
||||
from common.db.utils import safe_db_connection
|
||||
from common.decorators import bulk_create_decorator
|
||||
from common.utils import get_logger
|
||||
from common.utils.file import encrypt_and_compress_zip_file
|
||||
|
@ -26,11 +24,6 @@ logger = get_logger(__name__)
|
|||
class ChangeSecretManager(BaseChangeSecretPushManager):
|
||||
ansible_account_prefer = ''
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.record_map = self.execution.snapshot.get('record_map', {}) # 这个是某个失败的记录重试
|
||||
self.name_recorder_mapper = {} # 做个映射,方便后面处理
|
||||
|
||||
@classmethod
|
||||
def method_type(cls):
|
||||
return AutomationTypes.change_secret
|
||||
|
@ -74,54 +67,6 @@ class ChangeSecretManager(BaseChangeSecretPushManager):
|
|||
)
|
||||
return recorder
|
||||
|
||||
def on_host_success(self, host, result):
|
||||
recorder = self.name_recorder_mapper.get(host)
|
||||
if not recorder:
|
||||
return
|
||||
recorder.status = ChangeSecretRecordStatusChoice.success.value
|
||||
recorder.date_finished = timezone.now()
|
||||
|
||||
account = recorder.account
|
||||
if not account:
|
||||
print("Account not found, deleted ?")
|
||||
return
|
||||
|
||||
account.secret = recorder.new_secret
|
||||
account.date_updated = timezone.now()
|
||||
|
||||
with safe_db_connection():
|
||||
recorder.save(update_fields=['status', 'date_finished'])
|
||||
account.save(update_fields=['secret', 'date_updated'])
|
||||
|
||||
self.summary['ok_accounts'] += 1
|
||||
self.result['ok_accounts'].append(
|
||||
{
|
||||
"asset": str(account.asset),
|
||||
"username": account.username,
|
||||
}
|
||||
)
|
||||
super().on_host_success(host, result)
|
||||
|
||||
def on_host_error(self, host, error, result):
|
||||
recorder = self.name_recorder_mapper.get(host)
|
||||
if not recorder:
|
||||
return
|
||||
recorder.status = ChangeSecretRecordStatusChoice.failed.value
|
||||
recorder.date_finished = timezone.now()
|
||||
recorder.error = error
|
||||
try:
|
||||
recorder.save()
|
||||
except Exception as e:
|
||||
print(f"\033[31m Save {host} recorder error: {e} \033[0m\n")
|
||||
self.summary['fail_accounts'] += 1
|
||||
self.result['fail_accounts'].append(
|
||||
{
|
||||
"asset": str(recorder.asset),
|
||||
"username": recorder.account.username,
|
||||
}
|
||||
)
|
||||
super().on_host_success(host, result)
|
||||
|
||||
def check_secret(self):
|
||||
if self.secret_strategy == SecretStrategy.custom \
|
||||
and not self.execution.snapshot['secret']:
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from accounts.const import AutomationTypes
|
||||
from common.decorators import bulk_create_decorator
|
||||
from common.utils import get_logger
|
||||
from common.utils.timezone import local_now_filename
|
||||
from ..base.manager import BaseChangeSecretPushManager
|
||||
from ...models import PushSecretRecord
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
@ -17,12 +19,33 @@ class PushAccountManager(BaseChangeSecretPushManager):
|
|||
return account.secret
|
||||
|
||||
def gen_account_inventory(self, account, asset, h, path_dir):
|
||||
self.get_or_create_record(asset, account, h['name'])
|
||||
secret = self.get_secret(account)
|
||||
secret_type = account.secret_type
|
||||
new_secret, private_key_path = self.handle_ssh_secret(secret_type, secret, path_dir)
|
||||
h = self.gen_inventory(h, account, new_secret, private_key_path, asset)
|
||||
return h
|
||||
|
||||
def get_or_create_record(self, asset, account, name):
|
||||
asset_account_id = f'{asset.id}-{account.id}'
|
||||
|
||||
if asset_account_id in self.record_map:
|
||||
record_id = self.record_map[asset_account_id]
|
||||
recorder = PushSecretRecord.objects.filter(id=record_id).first()
|
||||
else:
|
||||
recorder = self.create_record(asset, account)
|
||||
|
||||
self.name_recorder_mapper[name] = recorder
|
||||
return recorder
|
||||
|
||||
@bulk_create_decorator(PushSecretRecord)
|
||||
def create_record(self, asset, account):
|
||||
recorder = PushSecretRecord(
|
||||
asset=asset, account=account, execution=self.execution,
|
||||
comment=f'{account.username}@{asset.address}'
|
||||
)
|
||||
return recorder
|
||||
|
||||
def print_summary(self):
|
||||
print('\n\n' + '-' * 80)
|
||||
plan_execution_end = _('Plan execution end')
|
||||
|
|
|
@ -7,7 +7,7 @@ from django_filters import rest_framework as drf_filters
|
|||
from assets.models import Node
|
||||
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, PushSecretRecord
|
||||
|
||||
|
||||
class AccountFilterSet(BaseFilterSet):
|
||||
|
@ -134,7 +134,7 @@ class GatheredAccountFilterSet(BaseFilterSet):
|
|||
fields = ["id", "username"]
|
||||
|
||||
|
||||
class ChangeSecretRecordFilterSet(BaseFilterSet):
|
||||
class SecretRecordMixin:
|
||||
asset_name = drf_filters.CharFilter(
|
||||
field_name="asset__name", lookup_expr="icontains"
|
||||
)
|
||||
|
@ -155,6 +155,14 @@ class ChangeSecretRecordFilterSet(BaseFilterSet):
|
|||
dt = local_now() - timezone.timedelta(days=value)
|
||||
return queryset.filter(date_finished__gte=dt)
|
||||
|
||||
|
||||
class ChangeSecretRecordFilterSet(SecretRecordMixin, BaseFilterSet):
|
||||
class Meta:
|
||||
model = ChangeSecretRecord
|
||||
fields = ["id", "status", "asset_id", "execution"]
|
||||
|
||||
|
||||
class PushAccountRecordFilterSet(SecretRecordMixin, BaseFilterSet):
|
||||
class Meta:
|
||||
model = PushSecretRecord
|
||||
fields = ["id", "status", "asset_id", "execution"]
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# Generated by Django 4.1.13 on 2025-01-23 07:22
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0011_auto_20241204_1516'),
|
||||
('accounts', '0028_remove_checkaccountengine_is_active_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='changesecretrecord',
|
||||
name='account',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)ss', to='accounts.account'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='changesecretrecord',
|
||||
name='asset',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asset_%(class)ss', to='assets.asset'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='changesecretrecord',
|
||||
name='execution',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='execution_%(class)ss', to='accounts.automationexecution'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PushSecretRecord',
|
||||
fields=[
|
||||
('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')),
|
||||
('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
('date_finished', models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Date finished')),
|
||||
('status', models.CharField(default='pending', max_length=16, verbose_name='Status')),
|
||||
('error', models.TextField(blank=True, null=True, verbose_name='Error')),
|
||||
('account', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)ss', to='accounts.account')),
|
||||
('asset', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='asset_%(class)ss', to='assets.asset')),
|
||||
('execution', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='execution_%(class)ss', to='accounts.automationexecution')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Push secret record',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -166,17 +166,6 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
|
|||
|
||||
return escape(value)
|
||||
|
||||
@classmethod
|
||||
def get_risks(cls, queryset=None, risk_type=None):
|
||||
query = {
|
||||
'risks__risk': risk_type
|
||||
}
|
||||
|
||||
if queryset is None:
|
||||
queryset = cls.objects.all()
|
||||
|
||||
return queryset.filter(**query)
|
||||
|
||||
|
||||
def replace_history_model_with_mixin():
|
||||
"""
|
||||
|
|
|
@ -9,7 +9,7 @@ from common.db import fields
|
|||
from common.db.models import JMSBaseModel
|
||||
from .base import AccountBaseAutomation, ChangeSecretMixin
|
||||
|
||||
__all__ = ['ChangeSecretAutomation', 'ChangeSecretRecord', ]
|
||||
__all__ = ['ChangeSecretAutomation', 'ChangeSecretRecord', 'BaseSecretRecord']
|
||||
|
||||
|
||||
class ChangeSecretAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
||||
|
@ -30,36 +30,42 @@ class ChangeSecretAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
|||
return attr_json
|
||||
|
||||
|
||||
class ChangeSecretRecord(JMSBaseModel):
|
||||
class BaseSecretRecord(JMSBaseModel):
|
||||
account = models.ForeignKey(
|
||||
'accounts.Account', on_delete=models.SET_NULL,
|
||||
null=True, related_name='change_secret_records'
|
||||
null=True, related_name='%(class)ss'
|
||||
)
|
||||
asset = models.ForeignKey(
|
||||
'assets.Asset', on_delete=models.SET_NULL,
|
||||
null=True, related_name='asset_change_secret_records'
|
||||
null=True, related_name='asset_%(class)ss'
|
||||
)
|
||||
execution = models.ForeignKey(
|
||||
'accounts.AutomationExecution', on_delete=models.SET_NULL,
|
||||
null=True, related_name='execution_change_secret_records',
|
||||
null=True, related_name='execution_%(class)ss',
|
||||
)
|
||||
old_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Old secret'))
|
||||
new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('New secret'))
|
||||
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(
|
||||
max_length=16, verbose_name=_('Status'), default=ChangeSecretRecordStatusChoice.pending.value
|
||||
)
|
||||
error = models.TextField(blank=True, null=True, verbose_name=_('Error'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Change secret record")
|
||||
abstract = True
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.account.username}@{self.asset}'
|
||||
|
||||
@staticmethod
|
||||
def get_valid_records():
|
||||
return ChangeSecretRecord.objects.exclude(
|
||||
@classmethod
|
||||
def get_valid_records(cls):
|
||||
return cls.objects.exclude(
|
||||
Q(execution__isnull=True) | Q(asset__isnull=True) | Q(account__isnull=True)
|
||||
)
|
||||
|
||||
|
||||
class ChangeSecretRecord(BaseSecretRecord):
|
||||
old_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Old secret'))
|
||||
new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('New secret'))
|
||||
ignore_fail = models.BooleanField(default=False, verbose_name=_('Ignore fail'))
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Change secret record")
|
||||
|
|
|
@ -3,10 +3,10 @@ from django.utils.translation import gettext_lazy as _
|
|||
|
||||
from accounts.const import AutomationTypes
|
||||
from accounts.models import Account
|
||||
from .base import AccountBaseAutomation
|
||||
from .change_secret import ChangeSecretMixin
|
||||
from .base import AccountBaseAutomation, ChangeSecretMixin
|
||||
from .change_secret import BaseSecretRecord
|
||||
|
||||
__all__ = ['PushAccountAutomation']
|
||||
__all__ = ['PushAccountAutomation', 'PushSecretRecord']
|
||||
|
||||
|
||||
class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
||||
|
@ -36,3 +36,8 @@ class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
|||
|
||||
class Meta:
|
||||
verbose_name = _("Push asset account")
|
||||
|
||||
|
||||
class PushSecretRecord(BaseSecretRecord):
|
||||
class Meta:
|
||||
verbose_name = _("Push secret record")
|
||||
|
|
|
@ -2,7 +2,7 @@ from accounts.const import AutomationTypes
|
|||
from accounts.models import PushAccountAutomation
|
||||
from .change_secret import (
|
||||
ChangeSecretAutomationSerializer, ChangeSecretUpdateAssetSerializer,
|
||||
ChangeSecretUpdateNodeSerializer
|
||||
ChangeSecretUpdateNodeSerializer, ChangeSecretRecordSerializer
|
||||
)
|
||||
|
||||
|
||||
|
@ -19,6 +19,10 @@ class PushAccountAutomationSerializer(ChangeSecretAutomationSerializer):
|
|||
return AutomationTypes.push_account
|
||||
|
||||
|
||||
class PushSecretRecordSerializer(ChangeSecretRecordSerializer):
|
||||
pass
|
||||
|
||||
|
||||
class PushAccountUpdateAssetSerializer(ChangeSecretUpdateAssetSerializer):
|
||||
class Meta:
|
||||
model = PushAccountAutomation
|
||||
|
|
|
@ -23,6 +23,7 @@ router.register(r'gather-account-automations', api.GatherAccountsAutomationViewS
|
|||
router.register(r'gather-account-executions', api.GatherAccountsExecutionViewSet, 'gather-account-execution')
|
||||
router.register(r'push-account-automations', api.PushAccountAutomationViewSet, 'push-account-automation')
|
||||
router.register(r'push-account-executions', api.PushAccountExecutionViewSet, 'push-account-execution')
|
||||
router.register(r'push-account-records', api.PushAccountRecordViewSet, 'push-account-record')
|
||||
router.register(r'check-account-automations', api.CheckAccountAutomationViewSet, 'check-account-automation')
|
||||
router.register(r'check-account-executions', api.CheckAccountExecutionViewSet, 'check-account-execution')
|
||||
router.register(r'account-check-engines', api.CheckAccountEngineViewSet, 'account-check-engine')
|
||||
|
|
Loading…
Reference in New Issue