mirror of https://github.com/jumpserver/jumpserver
perf: merge with remote
commit
738465c02d
|
@ -5,14 +5,16 @@ from rest_framework.response import Response
|
|||
from rest_framework.status import HTTP_200_OK
|
||||
|
||||
from accounts import serializers
|
||||
from accounts.const import ChangeSecretRecordStatusChoice
|
||||
from accounts.filters import AccountFilterSet
|
||||
from accounts.mixins import AccountRecordViewLogMixin
|
||||
from accounts.models import Account
|
||||
from accounts.models import Account, ChangeSecretRecord
|
||||
from assets.models import Asset, Node
|
||||
from authentication.permissions import UserConfirmation, ConfirmType
|
||||
from common.api.mixin import ExtraFilterFieldsMixin
|
||||
from common.drf.filters import AttrRulesFilterBackend
|
||||
from common.permissions import IsValidUser
|
||||
from common.utils import lazyproperty
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from rbac.permissions import RBACPermission
|
||||
|
||||
|
@ -35,6 +37,8 @@ class AccountViewSet(OrgBulkModelViewSet):
|
|||
'partial_update': ['accounts.change_account'],
|
||||
'su_from_accounts': 'accounts.view_account',
|
||||
'clear_secret': 'accounts.change_account',
|
||||
'move_to_assets': 'accounts.create_account',
|
||||
'copy_to_assets': 'accounts.create_account',
|
||||
}
|
||||
export_as_zip = True
|
||||
|
||||
|
@ -88,6 +92,43 @@ class AccountViewSet(OrgBulkModelViewSet):
|
|||
self.model.objects.filter(id__in=account_ids).update(secret=None)
|
||||
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):
|
||||
"""
|
||||
|
@ -127,17 +168,31 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
|
|||
'GET': 'accounts.view_accountsecret',
|
||||
}
|
||||
|
||||
def get_object(self):
|
||||
@lazyproperty
|
||||
def account(self) -> Account:
|
||||
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
|
||||
def filter_spm_queryset(resource_ids, queryset):
|
||||
return queryset.filter(history_id__in=resource_ids)
|
||||
|
||||
def get_queryset(self):
|
||||
account = self.get_object()
|
||||
account = self.account
|
||||
histories = account.history.all()
|
||||
latest_history = account.history.first()
|
||||
latest_history = self.latest_history
|
||||
if not latest_history:
|
||||
return histories
|
||||
if account.secret != latest_history.secret:
|
||||
|
@ -146,3 +201,25 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
|
|||
return histories
|
||||
histories = histories.exclude(history_id=latest_history.history_id)
|
||||
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
|
||||
|
|
|
@ -9,9 +9,10 @@ from rest_framework.response import Response
|
|||
from accounts import serializers
|
||||
from accounts.const import AutomationTypes
|
||||
from accounts.filters import GatheredAccountFilterSet
|
||||
from accounts.models import GatherAccountsAutomation, AutomationExecution
|
||||
from accounts.models import GatherAccountsAutomation, AutomationExecution, Account
|
||||
from accounts.models import GatheredAccount
|
||||
from assets.models import Asset
|
||||
from common.utils.http import is_true
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from .base import AutomationExecutionViewSet
|
||||
|
||||
|
@ -80,30 +81,39 @@ class GatheredAccountViewSet(OrgBulkModelViewSet):
|
|||
"details": serializers.GatheredAccountDetailsSerializer
|
||||
}
|
||||
rbac_perms = {
|
||||
"sync_accounts": "assets.add_gatheredaccount",
|
||||
"status": "assets.change_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):
|
||||
instance = self.get_object()
|
||||
instance.status = request.data.get("status")
|
||||
instance.save(update_fields=["status"])
|
||||
|
||||
if instance.status == "confirmed":
|
||||
GatheredAccount.sync_accounts([instance])
|
||||
ids = request.data.get('ids', [])
|
||||
new_status = request.data.get("status")
|
||||
updated_instances = GatheredAccount.objects.filter(id__in=ids)
|
||||
updated_instances.update(status=new_status)
|
||||
if new_status == "confirmed":
|
||||
GatheredAccount.sync_accounts(updated_instances)
|
||||
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
@action(methods=["post"], detail=False, url_path="delete-remote")
|
||||
def delete_remote(self, request, *args, **kwargs):
|
||||
asset_id = request.data.get("asset_id")
|
||||
username = request.data.get("username")
|
||||
def perform_destroy(self, instance):
|
||||
request = self.request
|
||||
params = request.query_params
|
||||
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)
|
||||
handler = RiskHandler(asset, username, request=self.request)
|
||||
handler.handle_delete_remote()
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
@action(methods=["get"], detail=True, url_path="details")
|
||||
def details(self, request, *args, **kwargs):
|
||||
|
|
|
@ -33,18 +33,19 @@ class GatherAccountsFilter:
|
|||
@staticmethod
|
||||
def mysql_filter(info):
|
||||
result = {}
|
||||
for username, user_info in info.items():
|
||||
password_last_changed = parse_date(user_info.get('password_last_changed'))
|
||||
password_lifetime = user_info.get('password_lifetime')
|
||||
user = {
|
||||
'username': username,
|
||||
'date_password_change': password_last_changed,
|
||||
'date_password_expired': password_last_changed + timezone.timedelta(
|
||||
days=password_lifetime) if password_last_changed and password_lifetime else None,
|
||||
'date_last_login': None,
|
||||
'groups': '',
|
||||
}
|
||||
result[username] = user
|
||||
for host, user_dict in info.items():
|
||||
for username, user_info in user_dict.items():
|
||||
password_last_changed = parse_date(user_info.get('password_last_changed'))
|
||||
password_lifetime = user_info.get('password_lifetime')
|
||||
user = {
|
||||
'username': username,
|
||||
'date_password_change': password_last_changed,
|
||||
'date_password_expired': password_last_changed + timezone.timedelta(
|
||||
days=password_lifetime) if password_last_changed and password_lifetime else None,
|
||||
'date_last_login': None,
|
||||
'groups': '',
|
||||
}
|
||||
result[username] = user
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
|
@ -59,7 +60,7 @@ class GatherAccountsFilter:
|
|||
'groups': '',
|
||||
}
|
||||
detail = {
|
||||
'canlogin': user_info.get('canlogin'),
|
||||
'can_login': user_info.get('canlogin'),
|
||||
'superuser': user_info.get('superuser'),
|
||||
}
|
||||
user['detail'] = detail
|
||||
|
@ -87,7 +88,6 @@ class GatherAccountsFilter:
|
|||
'is_disabled': user_info.get('is_disabled', ''),
|
||||
'default_database_name': user_info.get('default_database_name', ''),
|
||||
}
|
||||
print(user)
|
||||
user['detail'] = detail
|
||||
result[user['username']] = user
|
||||
return result
|
||||
|
|
|
@ -16,7 +16,7 @@ DEFAULT_PASSWORD_RULES = {
|
|||
__all__ = [
|
||||
'AutomationTypes', 'SecretStrategy', 'SSHKeyStrategy', 'Connectivity',
|
||||
'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')
|
||||
success = 'success', _('Success')
|
||||
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')
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
# -*- 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_filters import rest_framework as drf_filters
|
||||
|
||||
from assets.models import Node
|
||||
from common.drf.filters import BaseFilterSet
|
||||
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):
|
||||
|
@ -70,11 +71,20 @@ class AccountFilterSet(BaseFilterSet):
|
|||
if not value:
|
||||
return queryset
|
||||
|
||||
queryset = (
|
||||
queryset.prefetch_related("risks")
|
||||
.annotate(risk=F("risks__risk"), confirmed=F("risks__confirmed"))
|
||||
.filter(risk=value, confirmed=False)
|
||||
asset_usernames = AccountRisk.objects.filter(risk=value). \
|
||||
values_list(
|
||||
Concat(
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -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'
|
||||
),
|
||||
),
|
||||
]
|
|
@ -3,12 +3,14 @@ from django.utils.translation import gettext_lazy as _
|
|||
from simple_history.models import HistoricalRecords
|
||||
|
||||
from assets.models.base import AbsConnectivity
|
||||
from common.utils import lazyproperty
|
||||
from common.utils import lazyproperty, get_logger
|
||||
from labels.mixins import LabeledMixin
|
||||
from .base import BaseAccount
|
||||
from .mixins import VaultModelMixin
|
||||
from ..const import Source
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
__all__ = ['Account', 'AccountHistoricalRecords']
|
||||
|
||||
|
||||
|
@ -26,7 +28,7 @@ class AccountHistoricalRecords(HistoricalRecords):
|
|||
|
||||
history_account = instance.history.first()
|
||||
if history_account is None:
|
||||
self.updated_version = 1
|
||||
self.updated_version = 0
|
||||
return super().post_save(instance, created, using=using, **kwargs)
|
||||
|
||||
history_attrs = {field: getattr(history_account, field) for field in check_fields}
|
||||
|
@ -38,12 +40,13 @@ class AccountHistoricalRecords(HistoricalRecords):
|
|||
if not diff:
|
||||
return
|
||||
self.updated_version = history_account.version + 1
|
||||
instance.version = self.updated_version
|
||||
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
|
||||
# Ignore deletion history_type: -
|
||||
if self.updated_version is not None and history_type != '-':
|
||||
instance.save(update_fields=['version'])
|
||||
|
||||
def create_history_model(self, model, inherited):
|
||||
|
|
|
@ -31,12 +31,20 @@ class ChangeSecretAutomation(ChangeSecretMixin, AccountBaseAutomation):
|
|||
|
||||
|
||||
class ChangeSecretRecord(JMSBaseModel):
|
||||
execution = models.ForeignKey('accounts.AutomationExecution', on_delete=models.SET_NULL, null=True)
|
||||
asset = models.ForeignKey('assets.Asset', on_delete=models.SET_NULL, null=True)
|
||||
account = models.ForeignKey('accounts.Account', on_delete=models.SET_NULL, null=True)
|
||||
account = models.ForeignKey(
|
||||
'accounts.Account', on_delete=models.SET_NULL,
|
||||
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'))
|
||||
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)
|
||||
ignore_fail = models.BooleanField(default=False, verbose_name=_('Ignore fail'))
|
||||
status = models.CharField(
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.shortcuts import get_object_or_404
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
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 GatheredAccount
|
||||
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)
|
||||
details = obj.detail
|
||||
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):
|
||||
self.fields[key] = serializers.BooleanField(label=key, read_only=True)
|
||||
self.fields[key] = serializers.BooleanField(label=label, read_only=True)
|
||||
else:
|
||||
self.fields[key] = serializers.CharField(label=key, read_only=True)
|
||||
self.fields[key] = serializers.CharField(label=label, read_only=True)
|
||||
|
|
|
@ -6,9 +6,7 @@ from django.utils import timezone
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from assets.const import Connectivity
|
||||
from common.utils import (
|
||||
get_logger
|
||||
)
|
||||
from common.utils import get_logger
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
|
|
@ -230,7 +230,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, Writa
|
|||
.prefetch_related('platform', 'platform__automation') \
|
||||
.annotate(category=F("platform__category")) \
|
||||
.annotate(type=F("platform__type")) \
|
||||
.annotate(assets_amount=Count('accounts'))
|
||||
.annotate(accounts_amount=Count('accounts'))
|
||||
if queryset.model is Asset:
|
||||
queryset = queryset.prefetch_related('labels__label', 'labels')
|
||||
else:
|
||||
|
|
|
@ -6,7 +6,8 @@ from rest_framework.validators import UniqueValidator
|
|||
from assets.models import Asset
|
||||
from common.serializers import (
|
||||
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.utils import lazyproperty
|
||||
|
@ -158,7 +159,7 @@ class PlatformCustomField(serializers.Serializer):
|
|||
choices = serializers.ListField(default=list, label=_("Choices"), required=False)
|
||||
|
||||
|
||||
class PlatformSerializer(ResourceLabelsMixin, WritableNestedModelSerializer):
|
||||
class PlatformSerializer(ResourceLabelsMixin, CommonSerializerMixin, WritableNestedModelSerializer):
|
||||
id = serializers.IntegerField(
|
||||
label='ID', required=False,
|
||||
validators=[UniqueValidator(queryset=Platform.objects.all())]
|
||||
|
|
|
@ -5254,7 +5254,7 @@ msgstr ""
|
|||
|
||||
#: ops/models/job.py:148
|
||||
msgid "Timeout (Seconds)"
|
||||
msgstr ""
|
||||
msgstr "Timeout (Sec)"
|
||||
|
||||
#: ops/models/job.py:153
|
||||
msgid "Use Parameter Define"
|
||||
|
@ -5334,8 +5334,8 @@ msgid "Next execution time"
|
|||
msgstr ""
|
||||
|
||||
#: ops/serializers/job.py:15
|
||||
msgid "Execute after saving"
|
||||
msgstr "Execute after saving"
|
||||
msgid "Run on save"
|
||||
msgstr "Run on save"
|
||||
|
||||
#: ops/serializers/job.py:72
|
||||
msgid "Job type"
|
||||
|
|
|
@ -5527,7 +5527,7 @@ msgid "Next execution time"
|
|||
msgstr "最後の実行"
|
||||
|
||||
#: ops/serializers/job.py:15
|
||||
msgid "Execute after saving"
|
||||
msgid "Run on save"
|
||||
msgstr "保存後に実行"
|
||||
|
||||
#: ops/serializers/job.py:72
|
||||
|
|
|
@ -5478,7 +5478,7 @@ msgid "Next execution time"
|
|||
msgstr "下次执行时间"
|
||||
|
||||
#: ops/serializers/job.py:15
|
||||
msgid "Execute after saving"
|
||||
msgid "Run on save"
|
||||
msgstr "保存后执行"
|
||||
|
||||
#: 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
|
@ -12,7 +12,7 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
|||
|
||||
class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
|
||||
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())
|
||||
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)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
from collections import defaultdict
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from accounts.const import AliasAccount
|
||||
from accounts.models import VirtualAccount
|
||||
from assets.models import Asset, MyAsset
|
||||
from common.utils import lazyproperty
|
||||
from orgs.utils import tmp_to_org, tmp_to_root_org
|
||||
from .permission import AssetPermissionUtil
|
||||
from perms.const import ActionChoices
|
||||
from .permission import AssetPermissionUtil
|
||||
|
||||
__all__ = ['PermAssetDetailUtil']
|
||||
|
||||
|
@ -40,6 +42,12 @@ class PermAssetDetailUtil:
|
|||
|
||||
def validate_permission(self, account_name, protocol):
|
||||
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)
|
||||
if 'all' not in protocols and protocol not in protocols:
|
||||
return None
|
||||
|
|
Loading…
Reference in New Issue