perf: Change secret

pull/14608/head
feng 2024-12-06 11:30:26 +08:00 committed by feng626
parent d9af381570
commit eefda353d2
7 changed files with 53 additions and 56 deletions

View File

@ -46,7 +46,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
def method_type(cls): def method_type(cls):
return AutomationTypes.change_secret return AutomationTypes.change_secret
def get_ssh_params(self, account, secret, secret_type): def get_ssh_params(self, secret, secret_type):
kwargs = {} kwargs = {}
if secret_type != SecretType.SSH_KEY: if secret_type != SecretType.SSH_KEY:
return kwargs return kwargs
@ -65,6 +65,7 @@ class ChangeSecretManager(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)
if self.secret_type: if self.secret_type:
accounts = accounts.filter(secret_type=self.secret_type) accounts = accounts.filter(secret_type=self.secret_type)
@ -74,37 +75,36 @@ class ChangeSecretManager(AccountBasePlaybookManager):
) )
return accounts return accounts
def gen_new_secret(self, account, path_dir): def get_secret(self, account):
if self.secret_type is None: if self.secret_strategy == SecretStrategy.custom:
new_secret = account.secret new_secret = self.execution.snapshot['secret']
else: else:
if self.secret_strategy == SecretStrategy.custom: generator = SecretGenerator(
new_secret = self.execution.snapshot['secret'] self.secret_strategy, self.secret_type,
else: self.execution.snapshot.get('password_rules')
generator = SecretGenerator( )
self.secret_strategy, self.secret_type, new_secret = generator.get_secret()
self.execution.snapshot.get('password_rules') return new_secret
)
new_secret = generator.get_secret()
recorder_secret = new_secret def gen_new_secret(self, account, new_secret, path_dir):
private_key_path = None 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, recorder_secret return new_secret, private_key_path
def get_or_create_record(self, asset, account, new_secret, name): def get_or_create_record(self, asset, account, name):
asset_account_id = f'{asset.id}-{account.id}' asset_account_id = f'{asset.id}-{account.id}'
if asset_account_id in self.record_map: if asset_account_id in self.record_map:
record_id = self.record_map[asset_account_id] record_id = self.record_map[asset_account_id]
recorder = ChangeSecretRecord.objects.filter(id=record_id).first() recorder = ChangeSecretRecord.objects.filter(id=record_id).first()
else: else:
new_secret = self.get_secret(account)
recorder = self.create_record(asset, account, new_secret) recorder = self.create_record(asset, account, new_secret)
if recorder: self.name_recorder_mapper[name] = recorder
self.name_recorder_mapper[name] = recorder return recorder
@bulk_create_decorator(ChangeSecretRecord) @bulk_create_decorator(ChangeSecretRecord)
def create_record(self, asset, account, new_secret): def create_record(self, asset, account, new_secret):
@ -115,11 +115,9 @@ class ChangeSecretManager(AccountBasePlaybookManager):
) )
return recorder return recorder
def gen_change_secret_inventory(self, host, account, new_secret, private_key_path, asset): def gen_change_secret_inventory(self, h, account, new_secret, private_key_path, asset):
h = deepcopy(host)
secret_type = account.secret_type secret_type = account.secret_type
h['name'] += '(' + account.username + ')' h['ssh_params'].update(self.get_ssh_params(new_secret, secret_type))
h['ssh_params'].update(self.get_ssh_params(account, new_secret, secret_type))
h['account'] = { h['account'] = {
'name': account.name, 'name': account.name,
'username': account.username, 'username': account.username,
@ -144,7 +142,7 @@ class ChangeSecretManager(AccountBasePlaybookManager):
host['ssh_params'] = {} host['ssh_params'] = {}
accounts = self.get_accounts(account) accounts = self.get_accounts(account)
error_msg = _("! No pending accounts found") error_msg = _("No pending accounts found")
if not accounts: if not accounts:
print(f'{asset}: {error_msg}') print(f'{asset}: {error_msg}')
return [] return []
@ -154,13 +152,15 @@ class ChangeSecretManager(AccountBasePlaybookManager):
inventory_hosts = [] inventory_hosts = []
if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY: if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY:
print(f'! Windows {asset} does not support ssh key push') print(f'Windows {asset} does not support ssh key push')
return inventory_hosts return inventory_hosts
for account in accounts: for account in accounts:
new_secret, private_key_path, recorder_secret = self.gen_new_secret(account, path_dir) h = deepcopy(host)
h = self.gen_change_secret_inventory(host, account, new_secret, private_key_path, asset) h['name'] += '(' + account.username + ')' # To distinguish different accounts
self.get_or_create_record(asset, account, recorder_secret, h['name']) record = self.get_or_create_record(asset, account, h['name'])
new_secret, private_key_path = self.gen_new_secret(account, record.new_secret, path_dir)
h = self.gen_change_secret_inventory(h, account, new_secret, private_key_path, asset)
inventory_hosts.append(h) inventory_hosts.append(h)
return inventory_hosts return inventory_hosts

View File

@ -10,3 +10,6 @@ class PushAccountManager(ChangeSecretManager, AccountBasePlaybookManager):
@classmethod @classmethod
def method_type(cls): def method_type(cls):
return AutomationTypes.push_account return AutomationTypes.push_account
def get_secret(self, account):
return account.secret

View File

@ -64,19 +64,25 @@ class ChangeSecretMixin(SecretWithRandomMixin):
verbose_name=_('Check connection after change') verbose_name=_('Check connection after change')
) )
get_all_assets: callable # get all assets get_all_assets: callable # get all assets
accounts: list # account usernames
class Meta: class Meta:
abstract = True abstract = True
def create_nonlocal_accounts(self, usernames, asset): def gen_nonlocal_accounts(self, usernames, asset):
pass return []
def get_account_ids(self): def get_account_ids(self):
account_objs = []
usernames = self.accounts usernames = self.accounts
accounts = Account.objects.none() assets = self.get_all_assets()
for asset in self.get_all_assets(): for asset in assets:
self.create_nonlocal_accounts(usernames, asset) objs = self.gen_nonlocal_accounts(usernames, asset)
accounts = accounts | asset.accounts.all() account_objs.extend(objs)
Account.objects.bulk_create(account_objs)
accounts = Account.objects.filter(asset__in=assets)
account_ids = accounts.filter( account_ids = accounts.filter(
username__in=usernames, secret_type=self.secret_type username__in=usernames, secret_type=self.secret_type
).values_list('id', flat=True) ).values_list('id', flat=True)

View File

@ -11,20 +11,22 @@ __all__ = ['PushAccountAutomation']
class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation): class PushAccountAutomation(ChangeSecretMixin, AccountBaseAutomation):
def create_nonlocal_accounts(self, usernames, asset): def gen_nonlocal_accounts(self, usernames, asset):
secret_type = self.secret_type secret_type = self.secret_type
account_usernames = asset.accounts \ account_usernames = asset.accounts \
.filter(secret_type=self.secret_type) \ .filter(secret_type=self.secret_type) \
.values_list('username', flat=True) .values_list('username', flat=True)
create_usernames = set(usernames) - set(account_usernames) create_usernames = set(usernames) - set(account_usernames)
create_account_objs = [
create_accounts = [
Account( Account(
name=f'{username}-{secret_type}', username=username, name=f'{username}-{secret_type}',
username=username, secret=self.get_secret(),
secret_type=secret_type, asset=asset, secret_type=secret_type, asset=asset,
) )
for username in create_usernames for username in create_usernames
] ]
Account.objects.bulk_create(create_account_objs) return create_accounts
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.type = AutomationTypes.push_account self.type = AutomationTypes.push_account

View File

@ -54,10 +54,9 @@ class SecretWithRandomMixin(models.Model):
) )
def get_secret(self): def get_secret(self):
if self.secret_strategy == 'random': if self.secret_strategy == SecretStrategy.custom:
return self.secret_generator.get_secret()
else:
return self.secret return self.secret
return self.secret_generator.get_secret()
class BaseAccount(VaultModelMixin, JMSOrgBaseModel): class BaseAccount(VaultModelMixin, JMSOrgBaseModel):

View File

@ -86,3 +86,7 @@ class AccountTemplate(LabeledMixin, BaseAccount, SecretWithRandomMixin):
""" 批量同步账号密码 """ """ 批量同步账号密码 """
self.bulk_update_accounts(accounts) self.bulk_update_accounts(accounts)
self.bulk_create_history_accounts(accounts, user_id) self.bulk_create_history_accounts(accounts, user_id)
def save(self, *args, **kwargs):
self.secret = self.get_secret()
super().save(*args, **kwargs)

View File

@ -1,9 +1,7 @@
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from accounts.const import SecretStrategy, SecretType
from accounts.models import AccountTemplate from accounts.models import AccountTemplate
from accounts.utils import SecretGenerator
from common.serializers import SecretReadableMixin from common.serializers import SecretReadableMixin
from common.serializers.fields import ObjectRelatedField from common.serializers.fields import ObjectRelatedField
from .base import BaseAccountSerializer from .base import BaseAccountSerializer
@ -47,21 +45,6 @@ class AccountTemplateSerializer(BaseAccountSerializer):
}, },
} }
@staticmethod
def generate_secret(attrs):
secret_type = attrs.get('secret_type', SecretType.PASSWORD)
secret_strategy = attrs.get('secret_strategy', SecretStrategy.custom)
password_rules = attrs.get('password_rules')
if secret_strategy != SecretStrategy.random:
return
generator = SecretGenerator(secret_strategy, secret_type, password_rules)
attrs['secret'] = generator.get_secret()
def validate(self, attrs):
attrs = super().validate(attrs)
self.generate_secret(attrs)
return attrs
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer): class AccountTemplateSecretSerializer(SecretReadableMixin, AccountTemplateSerializer):
class Meta(AccountTemplateSerializer.Meta): class Meta(AccountTemplateSerializer.Meta):