feat: 修改模版账号密码 同步更新关联的账号 (#10328)

* feat: 修改模版账号密码 同步更新关联的账号

* feat: 同步多个账号

---------

Co-authored-by: feng <1304903146@qq.com>
pull/10333/head
fit2bot 2023-04-25 10:28:19 +08:00 committed by GitHub
parent 9d2ae7d1ed
commit 917620736b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 69 deletions

View File

@ -5,7 +5,6 @@ from django_filters import rest_framework as drf_filters
from assets.models import Node from assets.models import Node
from common.drf.filters import BaseFilterSet from common.drf.filters import BaseFilterSet
from .models import Account, GatheredAccount from .models import Account, GatheredAccount
@ -46,7 +45,7 @@ class AccountFilterSet(BaseFilterSet):
class Meta: class Meta:
model = Account model = Account
fields = ['id', 'asset_id'] fields = ['id', 'asset_id', 'source_id']
class GatheredAccountFilterSet(BaseFilterSet): class GatheredAccountFilterSet(BaseFilterSet):

View File

@ -1,4 +1,6 @@
from django.db import models from django.db import models
from django.db.models import Count
from django.utils import timezone
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
@ -118,3 +120,45 @@ class AccountTemplate(BaseAccount):
def __str__(self): def __str__(self):
return self.username return self.username
@staticmethod
def bulk_update_accounts(accounts, data):
history_model = Account.history.model
account_ids = accounts.values_list('id', flat=True)
history_accounts = history_model.objects.filter(id__in=account_ids)
account_id_count_map = {
str(i['id']): i['count']
for i in history_accounts.values('id').order_by('id')
.annotate(count=Count(1)).values('id', 'count')
}
for account in accounts:
account_id = str(account.id)
account.version = account_id_count_map.get(account_id) + 1
for k, v in data.items():
setattr(account, k, v)
Account.objects.bulk_update(accounts, ['version', 'secret'])
@staticmethod
def bulk_create_history_accounts(accounts, user_id):
history_model = Account.history.model
history_account_objs = []
for account in accounts:
history_account_objs.append(
history_model(
id=account.id,
version=account.version,
secret=account.secret,
secret_type=account.secret_type,
history_user_id=user_id,
history_date=timezone.now()
)
)
history_model.objects.bulk_create(history_account_objs)
def bulk_sync_account_secret(self, accounts, user_id):
""" 批量同步账号密码 """
if not accounts:
return
self.bulk_update_accounts(accounts, {'secret': self.secret})
self.bulk_create_history_accounts(accounts, user_id)

View File

@ -1,86 +1,37 @@
from django.db.transaction import atomic from rest_framework import serializers
from django.db.utils import IntegrityError
from accounts.models import AccountTemplate, Account from accounts.models import AccountTemplate, Account
from assets.models import Asset
from common.serializers import SecretReadableMixin from common.serializers import SecretReadableMixin
from .base import BaseAccountSerializer from .base import BaseAccountSerializer
class AccountTemplateSerializer(BaseAccountSerializer): class AccountTemplateSerializer(BaseAccountSerializer):
is_sync_account = serializers.BooleanField(default=False, write_only=True)
_is_sync_account = False
class Meta(BaseAccountSerializer.Meta): class Meta(BaseAccountSerializer.Meta):
model = AccountTemplate model = AccountTemplate
fields = BaseAccountSerializer.Meta.fields + ['is_sync_account']
@staticmethod def sync_accounts_secret(self, instance, diff):
def account_save(data, account): if not self._is_sync_account or 'secret' not in diff:
for field, value in data.items():
setattr(account, field, value)
try:
account.save(update_fields=list(data.keys()))
except IntegrityError:
pass
# TODO 数据库访问的太多了 后期优化
@atomic()
def bulk_update_accounts(self, instance, diff):
accounts = Account.objects.filter(source_id=instance.id)
if not accounts:
return return
diff.pop('secret', None) accounts = Account.objects.filter(source_id=instance.id)
name = diff.pop('name', None) instance.bulk_sync_account_secret(accounts, self.context['request'].user.id)
username = diff.pop('username', None)
secret_type = diff.pop('secret_type', None)
update_accounts = []
for account in accounts:
for field, value in diff.items():
setattr(account, field, value)
update_accounts.append(account)
if update_accounts: def validate(self, attrs):
Account.objects.bulk_update(update_accounts, diff.keys()) self._is_sync_account = attrs.pop('is_sync_account', None)
attrs = super().validate(attrs)
if name: return attrs
for account in accounts:
data = {'name': name}
self.account_save(data, account)
if secret_type and username:
asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type)
for account in accounts:
asset_id = account.asset_id
if asset_id not in asset_ids_supports:
data = {'username': username}
self.account_save(data, account)
continue
data = {'username': username, 'secret_type': secret_type, 'secret': instance.secret}
self.account_save(data, account)
elif secret_type:
asset_ids_supports = self.get_asset_ids_supports(accounts, secret_type)
for account in accounts:
asset_id = account.asset_id
if asset_id not in asset_ids_supports:
continue
data = {'secret_type': secret_type, 'secret': instance.secret}
self.account_save(data, account)
elif username:
for account in accounts:
data = {'username': username}
self.account_save(data, account)
@staticmethod
def get_asset_ids_supports(accounts, secret_type):
asset_ids = accounts.values_list('asset_id', flat=True)
secret_type_supports = Asset.get_secret_type_assets(asset_ids, secret_type)
return [asset.id for asset in secret_type_supports]
def update(self, instance, validated_data): def update(self, instance, validated_data):
# diff = { diff = {
# k: v for k, v in validated_data.items() k: v for k, v in validated_data.items()
# if getattr(instance, k) != v if getattr(instance, k) != v
# } }
instance = super().update(instance, validated_data) instance = super().update(instance, validated_data)
# self.bulk_update_accounts(instance, diff) self.sync_accounts_secret(instance, diff)
return instance return instance