perf: Change secret

pull/14592/head
feng 2024-12-04 18:47:14 +08:00 committed by feng626
parent 0adb9e0d5e
commit e062b5820c
7 changed files with 59 additions and 49 deletions

View File

@ -11,8 +11,8 @@ from accounts.models import Account
from assets.models import Asset, Node from assets.models import Asset, Node
from authentication.permissions import UserConfirmation, ConfirmType from authentication.permissions import UserConfirmation, ConfirmType
from common.api.mixin import ExtraFilterFieldsMixin from common.api.mixin import ExtraFilterFieldsMixin
from common.permissions import IsValidUser
from common.drf.filters import AttrRulesFilterBackend from common.drf.filters import AttrRulesFilterBackend
from common.permissions import IsValidUser
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission from rbac.permissions import RBACPermission

View File

@ -75,24 +75,24 @@ class ChangeSecretManager(AccountBasePlaybookManager):
return accounts return accounts
def gen_new_secret(self, account, path_dir): def gen_new_secret(self, account, path_dir):
private_key_path = None
if self.secret_type is None: if self.secret_type is None:
new_secret = account.secret new_secret = account.secret
return new_secret, private_key_path
if self.secret_strategy == SecretStrategy.custom:
new_secret = self.execution.snapshot['secret']
else: else:
generator = SecretGenerator( if self.secret_strategy == SecretStrategy.custom:
self.secret_strategy, self.secret_type, new_secret = self.execution.snapshot['secret']
self.execution.snapshot.get('password_rules') else:
) generator = SecretGenerator(
new_secret = generator.get_secret() self.secret_strategy, self.secret_type,
self.execution.snapshot.get('password_rules')
)
new_secret = generator.get_secret()
recorder_secret = new_secret
private_key_path = None
if account.secret_type == SecretType.SSH_KEY: if account.secret_type == SecretType.SSH_KEY:
private_key_path = self.generate_private_key_path(new_secret, path_dir) private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret) new_secret = self.generate_public_key(new_secret)
return new_secret, private_key_path return new_secret, private_key_path, recorder_secret
def get_or_create_record(self, asset, account, new_secret, name): def get_or_create_record(self, asset, account, new_secret, name):
asset_account_id = f'{asset.id}-{account.id}' asset_account_id = f'{asset.id}-{account.id}'
@ -158,9 +158,9 @@ class ChangeSecretManager(AccountBasePlaybookManager):
return inventory_hosts return inventory_hosts
for account in accounts: for account in accounts:
new_secret, private_key_path = self.gen_new_secret(account, path_dir) new_secret, private_key_path, recorder_secret = self.gen_new_secret(account, path_dir)
h = self.gen_change_secret_inventory(host, account, new_secret, private_key_path, asset) h = self.gen_change_secret_inventory(host, account, new_secret, private_key_path, asset)
self.get_or_create_record(asset, account, new_secret, h['name']) self.get_or_create_record(asset, account, recorder_secret, h['name'])
inventory_hosts.append(h) inventory_hosts.append(h)
return inventory_hosts return inventory_hosts
@ -182,7 +182,8 @@ class ChangeSecretManager(AccountBasePlaybookManager):
with safe_db_connection(): with safe_db_connection():
recorder.save(update_fields=['status', 'date_finished']) recorder.save(update_fields=['status', 'date_finished'])
account.save(update_fields=['secret', 'version', 'date_updated']) account.save(update_fields=['secret', 'date_updated'])
self.summary['ok_accounts'] += 1 self.summary['ok_accounts'] += 1
self.result['ok_accounts'].append( self.result['ok_accounts'].append(
{ {

View File

@ -0,0 +1,25 @@
# Generated by Django 4.1.13 on 2024-12-05 08:39
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0020_alter_automationexecution_options'),
]
operations = [
migrations.RemoveField(
model_name='pushaccountautomation',
name='action',
),
migrations.RemoveField(
model_name='pushaccountautomation',
name='triggers',
),
migrations.RemoveField(
model_name='pushaccountautomation',
name='username',
),
]

View File

@ -14,6 +14,7 @@ __all__ = ['Account', 'AccountHistoricalRecords']
class AccountHistoricalRecords(HistoricalRecords): class AccountHistoricalRecords(HistoricalRecords):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.updated_version = None
self.included_fields = kwargs.pop('included_fields', None) self.included_fields = kwargs.pop('included_fields', None)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
@ -22,18 +23,29 @@ class AccountHistoricalRecords(HistoricalRecords):
return super().post_save(instance, created, using=using, **kwargs) return super().post_save(instance, created, using=using, **kwargs)
check_fields = set(self.included_fields) - {'version'} check_fields = set(self.included_fields) - {'version'}
history_attrs = instance.history.all().values(*check_fields).first()
if history_attrs is None: history_account = instance.history.first()
if history_account is None:
self.updated_version = 1
return super().post_save(instance, created, using=using, **kwargs) return super().post_save(instance, created, using=using, **kwargs)
history_attrs = {field: getattr(history_account, field) for field in check_fields}
attrs = {field: getattr(instance, field) for field in check_fields} attrs = {field: getattr(instance, field) for field in check_fields}
history_attrs = set(history_attrs.items()) history_attrs = set(history_attrs.items())
attrs = set(attrs.items()) attrs = set(attrs.items())
diff = attrs - history_attrs diff = attrs - history_attrs
if not diff: if not diff:
return return
self.updated_version = history_account.version + 1
return super().post_save(instance, created, using=using, **kwargs) return super().post_save(instance, created, using=using, **kwargs)
def create_historical_record(self, instance, history_type, using=None):
super().create_historical_record(instance, history_type, using=using)
if self.updated_version is not None:
instance.version = self.updated_version
instance.save(update_fields=['version'])
def create_history_model(self, model, inherited): def create_history_model(self, model, inherited):
if self.included_fields and not self.excluded_fields: if self.included_fields and not self.excluded_fields:
self.excluded_fields = [ self.excluded_fields = [
@ -60,7 +72,8 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount):
date_last_login = models.DateTimeField(null=True, blank=True, verbose_name=_('Date last access')) date_last_login = models.DateTimeField(null=True, blank=True, verbose_name=_('Date last access'))
login_by = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Access by')) login_by = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Access by'))
date_change_secret = models.DateTimeField(null=True, blank=True, verbose_name=_('Date change secret')) date_change_secret = models.DateTimeField(null=True, blank=True, verbose_name=_('Date change secret'))
change_secret_status = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Change secret status')) change_secret_status = models.CharField(max_length=16, null=True, blank=True,
verbose_name=_('Change secret status'))
class Meta: class Meta:
verbose_name = _('Account') verbose_name = _('Account')

View File

@ -1,5 +1,4 @@
from django.conf import settings from django.conf import settings
from django.db import models
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
@ -11,9 +10,6 @@ __all__ = ['PushAccountAutomation']
class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation): class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
triggers = models.JSONField(max_length=16, default=list, verbose_name=_('Triggers'))
username = models.CharField(max_length=128, verbose_name=_('Username'))
action = models.CharField(max_length=16, verbose_name=_('Action'))
def create_nonlocal_accounts(self, usernames, asset): def create_nonlocal_accounts(self, usernames, asset):
secret_type = self.secret_type secret_type = self.secret_type
@ -30,28 +26,11 @@ class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
] ]
Account.objects.bulk_create(create_account_objs) Account.objects.bulk_create(create_account_objs)
@property
def dynamic_username(self):
return self.username == '@USER'
@dynamic_username.setter
def dynamic_username(self, value):
if value:
self.username = '@USER'
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.type = AutomationTypes.push_account self.type = AutomationTypes.push_account
if not settings.XPACK_LICENSE_IS_VALID: if not settings.XPACK_LICENSE_IS_VALID:
self.is_periodic = False self.is_periodic = False
super().save(*args, **kwargs) super().save(*args, **kwargs)
def to_attr_json(self):
attr_json = super().to_attr_json()
attr_json.update({
'username': self.username,
'params': self.params,
})
return attr_json
class Meta: class Meta:
verbose_name = _("Push asset account") verbose_name = _("Push asset account")

View File

@ -1,7 +1,7 @@
from collections import defaultdict from collections import defaultdict
from django.db.models.signals import post_delete from django.db.models.signals import post_delete
from django.db.models.signals import pre_save, post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from django.utils.translation import gettext_noop from django.utils.translation import gettext_noop
@ -17,15 +17,6 @@ from .tasks.push_account import push_accounts_to_assets_task
logger = get_logger(__name__) logger = get_logger(__name__)
@receiver(pre_save, sender=Account)
def on_account_pre_save(sender, instance, **kwargs):
if instance.version == 0:
instance.version = 1
else:
history_account = instance.history.first()
instance.version = history_account.version + 1 if history_account else 0
@merge_delay_run(ttl=5) @merge_delay_run(ttl=5)
def push_accounts_if_need(accounts=()): def push_accounts_if_need(accounts=()):
from .models import AccountTemplate from .models import AccountTemplate

View File

@ -74,6 +74,7 @@ class BaseAutomation(PeriodTaskModelMixin, JMSOrgBaseModel):
"type": self.type, "type": self.type,
"comment": self.comment, "comment": self.comment,
"accounts": self.accounts, "accounts": self.accounts,
"params": self.params,
"org_id": str(self.org_id), "org_id": str(self.org_id),
"nodes": self.get_many_to_many_ids("nodes"), "nodes": self.get_many_to_many_ids("nodes"),
"assets": self.get_many_to_many_ids("assets"), "assets": self.get_many_to_many_ids("assets"),