mirror of https://github.com/jumpserver/jumpserver
perf: Change secret
parent
0adb9e0d5e
commit
e062b5820c
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
{
|
{
|
||||||
|
|
|
@ -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',
|
||||||
|
),
|
||||||
|
]
|
|
@ -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')
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
Loading…
Reference in New Issue