perf: merge with remote

pull/14774/head^2
ibuler 2025-01-03 11:23:42 +08:00
commit 738465c02d
21 changed files with 6783 additions and 6607 deletions

View File

@ -5,14 +5,16 @@ from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK from rest_framework.status import HTTP_200_OK
from accounts import serializers from accounts import serializers
from accounts.const import ChangeSecretRecordStatusChoice
from accounts.filters import AccountFilterSet from accounts.filters import AccountFilterSet
from accounts.mixins import AccountRecordViewLogMixin from accounts.mixins import AccountRecordViewLogMixin
from accounts.models import Account from accounts.models import Account, ChangeSecretRecord
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.drf.filters import AttrRulesFilterBackend from common.drf.filters import AttrRulesFilterBackend
from common.permissions import IsValidUser from common.permissions import IsValidUser
from common.utils import lazyproperty
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission from rbac.permissions import RBACPermission
@ -35,6 +37,8 @@ class AccountViewSet(OrgBulkModelViewSet):
'partial_update': ['accounts.change_account'], 'partial_update': ['accounts.change_account'],
'su_from_accounts': 'accounts.view_account', 'su_from_accounts': 'accounts.view_account',
'clear_secret': 'accounts.change_account', 'clear_secret': 'accounts.change_account',
'move_to_assets': 'accounts.create_account',
'copy_to_assets': 'accounts.create_account',
} }
export_as_zip = True export_as_zip = True
@ -88,6 +92,43 @@ class AccountViewSet(OrgBulkModelViewSet):
self.model.objects.filter(id__in=account_ids).update(secret=None) self.model.objects.filter(id__in=account_ids).update(secret=None)
return Response(status=HTTP_200_OK) return Response(status=HTTP_200_OK)
def _copy_or_move_to_assets(self, request, move=False):
account = self.get_object()
asset_ids = request.data.get('assets', [])
assets = Asset.objects.filter(id__in=asset_ids)
field_names = [
'name', 'username', 'secret_type', 'secret',
'privileged', 'is_active', 'source', 'source_id', 'comment'
]
account_data = {field: getattr(account, field) for field in field_names}
creation_results = {}
success_count = 0
for asset in assets:
account_data['asset'] = asset
creation_results[asset] = {'state': 'created'}
try:
self.model.objects.create(**account_data)
success_count += 1
except Exception as e:
creation_results[asset] = {'error': str(e), 'state': 'error'}
results = [{'asset': str(asset), **res} for asset, res in creation_results.items()]
if move and success_count > 0:
account.delete()
return Response(results, status=HTTP_200_OK)
@action(methods=['post'], detail=True, url_path='move-to-assets')
def move_to_assets(self, request, *args, **kwargs):
return self._copy_or_move_to_assets(request, move=True)
@action(methods=['post'], detail=True, url_path='copy-to-assets')
def copy_to_assets(self, request, *args, **kwargs):
return self._copy_or_move_to_assets(request, move=False)
class AccountSecretsViewSet(AccountRecordViewLogMixin, AccountViewSet): class AccountSecretsViewSet(AccountRecordViewLogMixin, AccountViewSet):
""" """
@ -127,17 +168,31 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
'GET': 'accounts.view_accountsecret', 'GET': 'accounts.view_accountsecret',
} }
def get_object(self): @lazyproperty
def account(self) -> Account:
return get_object_or_404(Account, pk=self.kwargs.get('pk')) return get_object_or_404(Account, pk=self.kwargs.get('pk'))
def get_object(self):
return self.account
@lazyproperty
def latest_history(self):
return self.account.history.first()
@property
def latest_change_secret_record(self) -> ChangeSecretRecord:
return self.account.change_secret_records.filter(
status=ChangeSecretRecordStatusChoice.pending
).order_by('-date_created').first()
@staticmethod @staticmethod
def filter_spm_queryset(resource_ids, queryset): def filter_spm_queryset(resource_ids, queryset):
return queryset.filter(history_id__in=resource_ids) return queryset.filter(history_id__in=resource_ids)
def get_queryset(self): def get_queryset(self):
account = self.get_object() account = self.account
histories = account.history.all() histories = account.history.all()
latest_history = account.history.first() latest_history = self.latest_history
if not latest_history: if not latest_history:
return histories return histories
if account.secret != latest_history.secret: if account.secret != latest_history.secret:
@ -146,3 +201,25 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
return histories return histories
histories = histories.exclude(history_id=latest_history.history_id) histories = histories.exclude(history_id=latest_history.history_id)
return histories return histories
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = list(queryset)
latest_history = self.latest_history
if not latest_history:
return queryset
latest_change_secret_record = self.latest_change_secret_record
if not latest_change_secret_record:
return queryset
if latest_change_secret_record.date_created > latest_history.history_date:
temp_history = self.model(
secret=latest_change_secret_record.new_secret,
secret_type=self.account.secret_type,
version=latest_history.version,
history_date=latest_change_secret_record.date_created,
)
queryset = [temp_history] + queryset
return queryset

View File

@ -9,9 +9,10 @@ from rest_framework.response import Response
from accounts import serializers from accounts import serializers
from accounts.const import AutomationTypes from accounts.const import AutomationTypes
from accounts.filters import GatheredAccountFilterSet from accounts.filters import GatheredAccountFilterSet
from accounts.models import GatherAccountsAutomation, AutomationExecution from accounts.models import GatherAccountsAutomation, AutomationExecution, Account
from accounts.models import GatheredAccount from accounts.models import GatheredAccount
from assets.models import Asset from assets.models import Asset
from common.utils.http import is_true
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from .base import AutomationExecutionViewSet from .base import AutomationExecutionViewSet
@ -80,30 +81,39 @@ class GatheredAccountViewSet(OrgBulkModelViewSet):
"details": serializers.GatheredAccountDetailsSerializer "details": serializers.GatheredAccountDetailsSerializer
} }
rbac_perms = { rbac_perms = {
"sync_accounts": "assets.add_gatheredaccount",
"status": "assets.change_gatheredaccount", "status": "assets.change_gatheredaccount",
"details": "assets.view_gatheredaccount" "details": "assets.view_gatheredaccount"
} }
@action(methods=["put"], detail=True, url_path="status") @action(methods=["put"], detail=False, url_path="status")
def status(self, request, *args, **kwargs): def status(self, request, *args, **kwargs):
instance = self.get_object() ids = request.data.get('ids', [])
instance.status = request.data.get("status") new_status = request.data.get("status")
instance.save(update_fields=["status"]) updated_instances = GatheredAccount.objects.filter(id__in=ids)
updated_instances.update(status=new_status)
if instance.status == "confirmed": if new_status == "confirmed":
GatheredAccount.sync_accounts([instance]) GatheredAccount.sync_accounts(updated_instances)
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
@action(methods=["post"], detail=False, url_path="delete-remote") def perform_destroy(self, instance):
def delete_remote(self, request, *args, **kwargs): request = self.request
asset_id = request.data.get("asset_id") params = request.query_params
username = request.data.get("username") is_delete_remote = params.get("is_delete_remote")
is_delete_account = params.get("is_delete_account")
asset_id = params.get("asset")
username = params.get("username")
if is_true(is_delete_remote):
self._delete_remote(asset_id, username)
if is_true(is_delete_account):
account = get_object_or_404(Account, username=username, asset_id=asset_id)
account.delete()
super().perform_destroy(instance)
def _delete_remote(self, asset_id, username):
asset = get_object_or_404(Asset, pk=asset_id) asset = get_object_or_404(Asset, pk=asset_id)
handler = RiskHandler(asset, username, request=self.request) handler = RiskHandler(asset, username, request=self.request)
handler.handle_delete_remote() handler.handle_delete_remote()
return Response(status=status.HTTP_200_OK)
@action(methods=["get"], detail=True, url_path="details") @action(methods=["get"], detail=True, url_path="details")
def details(self, request, *args, **kwargs): def details(self, request, *args, **kwargs):

View File

@ -33,18 +33,19 @@ class GatherAccountsFilter:
@staticmethod @staticmethod
def mysql_filter(info): def mysql_filter(info):
result = {} result = {}
for username, user_info in info.items(): for host, user_dict in info.items():
password_last_changed = parse_date(user_info.get('password_last_changed')) for username, user_info in user_dict.items():
password_lifetime = user_info.get('password_lifetime') password_last_changed = parse_date(user_info.get('password_last_changed'))
user = { password_lifetime = user_info.get('password_lifetime')
'username': username, user = {
'date_password_change': password_last_changed, 'username': username,
'date_password_expired': password_last_changed + timezone.timedelta( 'date_password_change': password_last_changed,
days=password_lifetime) if password_last_changed and password_lifetime else None, 'date_password_expired': password_last_changed + timezone.timedelta(
'date_last_login': None, days=password_lifetime) if password_last_changed and password_lifetime else None,
'groups': '', 'date_last_login': None,
} 'groups': '',
result[username] = user }
result[username] = user
return result return result
@staticmethod @staticmethod
@ -59,7 +60,7 @@ class GatherAccountsFilter:
'groups': '', 'groups': '',
} }
detail = { detail = {
'canlogin': user_info.get('canlogin'), 'can_login': user_info.get('canlogin'),
'superuser': user_info.get('superuser'), 'superuser': user_info.get('superuser'),
} }
user['detail'] = detail user['detail'] = detail
@ -87,7 +88,6 @@ class GatherAccountsFilter:
'is_disabled': user_info.get('is_disabled', ''), 'is_disabled': user_info.get('is_disabled', ''),
'default_database_name': user_info.get('default_database_name', ''), 'default_database_name': user_info.get('default_database_name', ''),
} }
print(user)
user['detail'] = detail user['detail'] = detail
result[user['username']] = user result[user['username']] = user
return result return result

View File

@ -16,7 +16,7 @@ DEFAULT_PASSWORD_RULES = {
__all__ = [ __all__ = [
'AutomationTypes', 'SecretStrategy', 'SSHKeyStrategy', 'Connectivity', 'AutomationTypes', 'SecretStrategy', 'SSHKeyStrategy', 'Connectivity',
'DEFAULT_PASSWORD_LENGTH', 'DEFAULT_PASSWORD_RULES', 'TriggerChoice', 'DEFAULT_PASSWORD_LENGTH', 'DEFAULT_PASSWORD_RULES', 'TriggerChoice',
'PushAccountActionChoice', 'AccountBackupType', 'ChangeSecretRecordStatusChoice', 'PushAccountActionChoice', 'AccountBackupType', 'ChangeSecretRecordStatusChoice', 'GatherAccountDetailField'
] ]
@ -114,3 +114,20 @@ class ChangeSecretRecordStatusChoice(models.TextChoices):
failed = 'failed', _('Failed') failed = 'failed', _('Failed')
success = 'success', _('Success') success = 'success', _('Success')
pending = 'pending', _('Pending') pending = 'pending', _('Pending')
class GatherAccountDetailField(models.TextChoices):
can_login = 'can_login', _('Can login')
superuser = 'superuser', _('Superuser')
create_date = 'create_date', _('Create date')
is_disabled = 'is_disabled', _('Is disabled')
default_database_name = 'default_database_name', _('Default database name')
uid = 'uid', _('UID')
account_status = 'account_status', _('Account status')
default_tablespace = 'default_tablespace', _('Default tablespace')
roles = 'roles', _('Roles')
privileges = 'privileges', _('Privileges')
groups = 'groups', _('Groups')
sudoers = 'sudoers', 'sudoers'
authorized_keys = 'authorized_keys', _('Authorized keys')
db = 'db', _('DB')

View File

@ -1,13 +1,14 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.db.models import Q, F from django.db.models import Q, F, Value, CharField
from django.db.models.functions import Concat
from django.utils import timezone from django.utils import timezone
from django_filters import rest_framework as drf_filters 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 common.utils.timezone import local_zero_hour, local_now from common.utils.timezone import local_zero_hour, local_now
from .models import Account, GatheredAccount, ChangeSecretRecord from .models import Account, GatheredAccount, ChangeSecretRecord, AccountRisk
class AccountFilterSet(BaseFilterSet): class AccountFilterSet(BaseFilterSet):
@ -70,11 +71,20 @@ class AccountFilterSet(BaseFilterSet):
if not value: if not value:
return queryset return queryset
queryset = ( asset_usernames = AccountRisk.objects.filter(risk=value). \
queryset.prefetch_related("risks") values_list(
.annotate(risk=F("risks__risk"), confirmed=F("risks__confirmed")) Concat(
.filter(risk=value, confirmed=False) F('asset_id'), Value('-'), F('username'),
output_field=CharField()
), flat=True
) )
queryset = queryset.annotate(
asset_username=Concat(
F('asset_id'), Value('-'), F('username'),
output_field=CharField()
)
).filter(asset_username__in=asset_usernames)
return queryset return queryset
@staticmethod @staticmethod

View File

@ -0,0 +1,42 @@
# Generated by Django 4.1.13 on 2024-12-24 05:27
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0011_auto_20241204_1516'),
('accounts', '0023_alter_changesecretrecord_options'),
]
operations = [
migrations.RemoveField(
model_name='changesecretrecord',
name='date_started',
),
migrations.AlterField(
model_name='changesecretrecord',
name='account',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL,
related_name='change_secret_records', to='accounts.account'
),
),
migrations.AlterField(
model_name='changesecretrecord',
name='asset',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL,
related_name='asset_change_secret_records', to='assets.asset'
),
),
migrations.AlterField(
model_name='changesecretrecord',
name='execution',
field=models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL,
related_name='execution_change_secret_records', to='accounts.automationexecution'
),
),
]

View File

@ -3,12 +3,14 @@ from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from assets.models.base import AbsConnectivity from assets.models.base import AbsConnectivity
from common.utils import lazyproperty from common.utils import lazyproperty, get_logger
from labels.mixins import LabeledMixin from labels.mixins import LabeledMixin
from .base import BaseAccount from .base import BaseAccount
from .mixins import VaultModelMixin from .mixins import VaultModelMixin
from ..const import Source from ..const import Source
logger = get_logger(__file__)
__all__ = ['Account', 'AccountHistoricalRecords'] __all__ = ['Account', 'AccountHistoricalRecords']
@ -26,7 +28,7 @@ class AccountHistoricalRecords(HistoricalRecords):
history_account = instance.history.first() history_account = instance.history.first()
if history_account is None: if history_account is None:
self.updated_version = 1 self.updated_version = 0
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} history_attrs = {field: getattr(history_account, field) for field in check_fields}
@ -38,12 +40,13 @@ class AccountHistoricalRecords(HistoricalRecords):
if not diff: if not diff:
return return
self.updated_version = history_account.version + 1 self.updated_version = history_account.version + 1
instance.version = self.updated_version
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): def create_historical_record(self, instance, history_type, using=None):
super().create_historical_record(instance, history_type, using=using) super().create_historical_record(instance, history_type, using=using)
if self.updated_version is not None: # Ignore deletion history_type: -
instance.version = self.updated_version if self.updated_version is not None and history_type != '-':
instance.save(update_fields=['version']) instance.save(update_fields=['version'])
def create_history_model(self, model, inherited): def create_history_model(self, model, inherited):

View File

@ -31,12 +31,20 @@ class ChangeSecretAutomation(ChangeSecretMixin, AccountBaseAutomation):
class ChangeSecretRecord(JMSBaseModel): class ChangeSecretRecord(JMSBaseModel):
execution = models.ForeignKey('accounts.AutomationExecution', on_delete=models.SET_NULL, null=True) account = models.ForeignKey(
asset = models.ForeignKey('assets.Asset', on_delete=models.SET_NULL, null=True) 'accounts.Account', on_delete=models.SET_NULL,
account = models.ForeignKey('accounts.Account', on_delete=models.SET_NULL, null=True) null=True, related_name='change_secret_records'
)
asset = models.ForeignKey(
'assets.Asset', on_delete=models.SET_NULL,
null=True, related_name='asset_change_secret_records'
)
execution = models.ForeignKey(
'accounts.AutomationExecution', on_delete=models.SET_NULL,
null=True, related_name='execution_change_secret_records',
)
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=_('New secret')) new_secret = fields.EncryptTextField(blank=True, null=True, verbose_name=_('New secret'))
date_started = models.DateTimeField(blank=True, null=True, verbose_name=_('Date started'))
date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('Date finished'), db_index=True) date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('Date finished'), db_index=True)
ignore_fail = models.BooleanField(default=False, verbose_name=_('Ignore fail')) ignore_fail = models.BooleanField(default=False, verbose_name=_('Ignore fail'))
status = models.CharField( status = models.CharField(

View File

@ -2,7 +2,7 @@ from django.shortcuts import get_object_or_404
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 AutomationTypes from accounts.const import AutomationTypes, GatherAccountDetailField
from accounts.models import GatherAccountsAutomation from accounts.models import GatherAccountsAutomation
from accounts.models import GatheredAccount from accounts.models import GatheredAccount
from accounts.serializers.account.account import AccountAssetSerializer as _AccountAssetSerializer from accounts.serializers.account.account import AccountAssetSerializer as _AccountAssetSerializer
@ -82,7 +82,9 @@ class GatheredAccountDetailsSerializer(serializers.Serializer):
obj = get_object_or_404(GatheredAccount, pk=pk) obj = get_object_or_404(GatheredAccount, pk=pk)
details = obj.detail details = obj.detail
for key, value in details.items(): for key, value in details.items():
field_dict = GatherAccountDetailField._member_map_
label = field_dict[key].label if key in field_dict else key
if isinstance(value, bool): if isinstance(value, bool):
self.fields[key] = serializers.BooleanField(label=key, read_only=True) self.fields[key] = serializers.BooleanField(label=label, read_only=True)
else: else:
self.fields[key] = serializers.CharField(label=key, read_only=True) self.fields[key] = serializers.CharField(label=label, read_only=True)

View File

@ -6,9 +6,7 @@ from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from assets.const import Connectivity from assets.const import Connectivity
from common.utils import ( from common.utils import get_logger
get_logger
)
logger = get_logger(__file__) logger = get_logger(__file__)

View File

@ -230,7 +230,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
.prefetch_related('platform', 'platform__automation') \ .prefetch_related('platform', 'platform__automation') \
.annotate(category=F("platform__category")) \ .annotate(category=F("platform__category")) \
.annotate(type=F("platform__type")) \ .annotate(type=F("platform__type")) \
.annotate(assets_amount=Count('accounts')) .annotate(accounts_amount=Count('accounts'))
if queryset.model is Asset: if queryset.model is Asset:
queryset = queryset.prefetch_related('labels__label', 'labels') queryset = queryset.prefetch_related('labels__label', 'labels')
else: else:

View File

@ -6,7 +6,8 @@ from rest_framework.validators import UniqueValidator
from assets.models import Asset from assets.models import Asset
from common.serializers import ( from common.serializers import (
WritableNestedModelSerializer, type_field_map, MethodSerializer, WritableNestedModelSerializer, type_field_map, MethodSerializer,
DictSerializer, create_serializer_class, ResourceLabelsMixin DictSerializer, create_serializer_class, ResourceLabelsMixin,
CommonSerializerMixin
) )
from common.serializers.fields import LabeledChoiceField, ObjectRelatedField from common.serializers.fields import LabeledChoiceField, ObjectRelatedField
from common.utils import lazyproperty from common.utils import lazyproperty
@ -158,7 +159,7 @@ class PlatformCustomField(serializers.Serializer):
choices = serializers.ListField(default=list, label=_("Choices"), required=False) choices = serializers.ListField(default=list, label=_("Choices"), required=False)
class PlatformSerializer(ResourceLabelsMixin, WritableNestedModelSerializer): class PlatformSerializer(ResourceLabelsMixin, CommonSerializerMixin, WritableNestedModelSerializer):
id = serializers.IntegerField( id = serializers.IntegerField(
label='ID', required=False, label='ID', required=False,
validators=[UniqueValidator(queryset=Platform.objects.all())] validators=[UniqueValidator(queryset=Platform.objects.all())]

View File

@ -5254,7 +5254,7 @@ msgstr ""
#: ops/models/job.py:148 #: ops/models/job.py:148
msgid "Timeout (Seconds)" msgid "Timeout (Seconds)"
msgstr "" msgstr "Timeout (Sec)"
#: ops/models/job.py:153 #: ops/models/job.py:153
msgid "Use Parameter Define" msgid "Use Parameter Define"
@ -5334,8 +5334,8 @@ msgid "Next execution time"
msgstr "" msgstr ""
#: ops/serializers/job.py:15 #: ops/serializers/job.py:15
msgid "Execute after saving" msgid "Run on save"
msgstr "Execute after saving" msgstr "Run on save"
#: ops/serializers/job.py:72 #: ops/serializers/job.py:72
msgid "Job type" msgid "Job type"

View File

@ -5527,7 +5527,7 @@ msgid "Next execution time"
msgstr "最後の実行" msgstr "最後の実行"
#: ops/serializers/job.py:15 #: ops/serializers/job.py:15
msgid "Execute after saving" msgid "Run on save"
msgstr "保存後に実行" msgstr "保存後に実行"
#: ops/serializers/job.py:72 #: ops/serializers/job.py:72

View File

@ -5478,7 +5478,7 @@ msgid "Next execution time"
msgstr "下次执行时间" msgstr "下次执行时间"
#: ops/serializers/job.py:15 #: ops/serializers/job.py:15
msgid "Execute after saving" msgid "Run on save"
msgstr "保存后执行" msgstr "保存后执行"
#: ops/serializers/job.py:72 #: ops/serializers/job.py:72

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer
class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
run_after_save = serializers.BooleanField(label=_("Execute after saving"), default=False, required=False) run_after_save = serializers.BooleanField(label=_("Run on save"), default=False, required=False)
nodes = serializers.ListField(required=False, child=serializers.CharField()) nodes = serializers.ListField(required=False, child=serializers.CharField())
date_last_run = serializers.DateTimeField(label=_('Date last run'), read_only=True) date_last_run = serializers.DateTimeField(label=_('Date last run'), read_only=True)
name = serializers.CharField(label=_('Name'), max_length=128, allow_blank=True, required=False) name = serializers.CharField(label=_('Name'), max_length=128, allow_blank=True, required=False)

View File

@ -1,12 +1,14 @@
from collections import defaultdict from collections import defaultdict
from django.utils import timezone
from accounts.const import AliasAccount from accounts.const import AliasAccount
from accounts.models import VirtualAccount from accounts.models import VirtualAccount
from assets.models import Asset, MyAsset from assets.models import Asset, MyAsset
from common.utils import lazyproperty from common.utils import lazyproperty
from orgs.utils import tmp_to_org, tmp_to_root_org from orgs.utils import tmp_to_org, tmp_to_root_org
from .permission import AssetPermissionUtil
from perms.const import ActionChoices from perms.const import ActionChoices
from .permission import AssetPermissionUtil
__all__ = ['PermAssetDetailUtil'] __all__ = ['PermAssetDetailUtil']
@ -40,6 +42,12 @@ class PermAssetDetailUtil:
def validate_permission(self, account_name, protocol): def validate_permission(self, account_name, protocol):
with tmp_to_org(self.asset.org): with tmp_to_org(self.asset.org):
if self.user.is_superuser:
account = self.asset.accounts.all().active().get(name=account_name)
account.actions = ActionChoices.all()
account.date_expired = timezone.now() + timezone.timedelta(days=365)
return account
protocols = self.get_permed_protocols_for_user(only_name=True) protocols = self.get_permed_protocols_for_user(only_name=True)
if 'all' not in protocols and protocol not in protocols: if 'all' not in protocols and protocol not in protocols:
return None return None