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 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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 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):
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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__)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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())]
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue