perf: change secret (#9014)

Co-authored-by: feng <1304903146@qq.com>
pull/9019/head
fit2bot 2022-11-03 22:39:48 +08:00 committed by GitHub
parent 7560a5cd1f
commit ebfc3b7b38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 150 additions and 6 deletions

View File

@ -1,17 +1,28 @@
import os
import time
import random import random
import string import string
from copy import deepcopy from copy import deepcopy
from openpyxl import Workbook
from collections import defaultdict from collections import defaultdict
from django.utils import timezone from django.utils import timezone
from django.conf import settings
from common.utils import lazyproperty, gen_key_pair from common.utils.timezone import local_now_display
from common.utils.file import encrypt_and_compress_zip_file
from common.utils import get_logger, lazyproperty, gen_key_pair
from users.models import User
from assets.models import ChangeSecretRecord from assets.models import ChangeSecretRecord
from assets.notifications import ChangeSecretExecutionTaskMsg
from assets.serializers import ChangeSecretRecordBackUpSerializer
from assets.const import ( from assets.const import (
AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy, DEFAULT_PASSWORD_RULES AutomationTypes, SecretType, SecretStrategy, SSHKeyStrategy, DEFAULT_PASSWORD_RULES
) )
from ..base.manager import BasePlaybookManager from ..base.manager import BasePlaybookManager
logger = get_logger(__name__)
class ChangeSecretManager(BasePlaybookManager): class ChangeSecretManager(BasePlaybookManager):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -125,7 +136,7 @@ class ChangeSecretManager(BasePlaybookManager):
new_secret = self.get_secret() new_secret = self.get_secret()
recorder = ChangeSecretRecord( recorder = ChangeSecretRecord(
account=account, execution=self.execution, asset=asset, account=account, execution=self.execution,
old_secret=account.secret, new_secret=new_secret, old_secret=account.secret, new_secret=new_secret,
) )
records.append(recorder) records.append(recorder)
@ -172,4 +183,51 @@ class ChangeSecretManager(BasePlaybookManager):
recorder.save() recorder.save()
def on_runner_failed(self, runner, e): def on_runner_failed(self, runner, e):
pass logger.error("Change secret error: ", e)
def run(self, *args, **kwargs):
super().run(*args, **kwargs)
recorders = self.name_recorder_mapper.values()
recorders = list(recorders)
self.send_recorder_mail(recorders)
def send_recorder_mail(self, recorders):
if not recorders:
return
recipients = self.execution.recipients
if not recipients:
return
recipients = User.objects.filter(id__in=list(recipients))
name = self.execution.snapshot['name']
path = os.path.join(os.path.dirname(settings.BASE_DIR), 'tmp')
filename = os.path.join(path, f'{name}-{local_now_display()}-{time.time()}.xlsx')
if not self.create_file(recorders, filename):
return
for user in recipients:
attachments = []
if user.secret_key:
password = user.secret_key.encode('utf8')
attachment = os.path.join(path, f'{name}-{local_now_display()}-{time.time()}.zip')
encrypt_and_compress_zip_file(attachment, password, [filename])
attachments = [attachment]
ChangeSecretExecutionTaskMsg(name, user).publish(attachments)
os.remove(filename)
@staticmethod
def create_file(recorders, filename):
serializer_cls = ChangeSecretRecordBackUpSerializer
serializer = serializer_cls(recorders, many=True)
header = [v.label for v in serializer.child.fields.values()]
rows = [list(row.values()) for row in serializer.data]
if not rows:
return False
rows.insert(0, header)
wb = Workbook(filename)
ws = wb.create_sheet('Sheet1')
for row in rows:
ws.append(row)
wb.save(filename)
return True

View File

@ -0,0 +1,19 @@
# Generated by Django 3.2.14 on 2022-11-03 13:57
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0109_rename_categories_to_types'),
]
operations = [
migrations.AddField(
model_name='changesecretrecord',
name='asset',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assets.asset'),
),
]

View File

@ -111,6 +111,13 @@ class AutomationExecution(OrgModelMixin):
def manager_type(self): def manager_type(self):
return self.snapshot['type'] return self.snapshot['type']
@property
def recipients(self):
recipients = self.snapshot.get('recipients')
if not recipients:
return []
return recipients.values()
def start(self): def start(self):
from assets.automations.endpoint import ExecutionManager from assets.automations.endpoint import ExecutionManager
manager = ExecutionManager(execution=self) manager = ExecutionManager(execution=self)

View File

@ -51,6 +51,7 @@ class ChangeSecretAutomation(BaseAutomation):
class ChangeSecretRecord(JMSBaseModel): class ChangeSecretRecord(JMSBaseModel):
execution = models.ForeignKey('assets.AutomationExecution', on_delete=models.CASCADE) execution = models.ForeignKey('assets.AutomationExecution', on_delete=models.CASCADE)
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, null=True)
account = models.ForeignKey('assets.Account', on_delete=models.CASCADE, null=True) account = models.ForeignKey('assets.Account', on_delete=models.CASCADE, null=True)
old_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Old secret')) old_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Old secret'))
new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Secret')) new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('Secret'))

View File

@ -158,7 +158,6 @@ class BaseAccount(OrgModelMixin):
return { return {
'name': self.name, 'name': self.name,
'username': self.username, 'username': self.username,
'password': self.password,
'public_key': self.public_key, 'public_key': self.public_key,
} }

View File

@ -15,11 +15,35 @@ class AccountBackupExecutionTaskMsg(object):
def message(self): def message(self):
name = self.name name = self.name
if self.user.secret_key: if self.user.secret_key:
return _('{} - The account backup passage task has been completed. See the attachment for details').format(name) return _('{} - The account backup passage task has been completed. See the attachment for details').format(
name)
return _("{} - The account backup passage task has been completed: the encryption password has not been set - " return _("{} - The account backup passage task has been completed: the encryption password has not been set - "
"please go to personal information -> file encryption password to set the encryption password").format(name) "please go to personal information -> file encryption password to set the encryption password").format(
name)
def publish(self, attachment_list=None): def publish(self, attachment_list=None):
send_mail_attachment_async.delay( send_mail_attachment_async.delay(
self.subject, self.message, [self.user.email], attachment_list self.subject, self.message, [self.user.email], attachment_list
) )
class ChangeSecretExecutionTaskMsg(object):
subject = _('Notification of implementation result of encryption change plan')
def __init__(self, name: str, user: User):
self.name = name
self.user = user
@property
def message(self):
name = self.name
if self.user.secret_key:
return _('{} - The encryption change task has been completed. See the attachment for details').format(name)
return _("{} - The encryption change task has been completed: the encryption password has not been set - "
"please go to personal information -> file encryption password to set the encryption password").format(
name)
def publish(self, attachments=None):
send_mail_attachment_async.delay(
self.subject, self.message, [self.user.email], attachments
)

View File

@ -11,3 +11,4 @@ from .account import *
from assets.serializers.account.backup import * from assets.serializers.account.backup import *
from .platform import * from .platform import *
from .cagegory import * from .cagegory import *
from .automation import *

View File

@ -0,0 +1,35 @@
from django.utils.translation import ugettext as _
from rest_framework import serializers
from common.utils import get_logger
from assets.models import ChangeSecretRecord
logger = get_logger(__file__)
class ChangeSecretRecordBackUpSerializer(serializers.ModelSerializer):
asset = serializers.SerializerMethodField(label=_('Asset'))
account = serializers.SerializerMethodField(label=_('Account'))
is_success = serializers.SerializerMethodField(label=_('Is success'))
class Meta:
model = ChangeSecretRecord
fields = [
'id', 'asset', 'account', 'old_secret', 'new_secret',
'status', 'error', 'is_success'
]
@staticmethod
def get_asset(instance):
return str(instance.asset)
@staticmethod
def get_account(instance):
return str(instance.account)
@staticmethod
def get_is_success(obj):
if obj.status == 'success':
return _("Success")
return _("Failed")