mirror of https://github.com/jumpserver/jumpserver
parent
6b6900cfd4
commit
1933e82587
|
@ -1,4 +1,6 @@
|
|||
from django_filters import rest_framework as drf_filters
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from accounts import serializers
|
||||
from accounts.models import AccountTemplate
|
||||
|
@ -38,8 +40,20 @@ class AccountTemplateViewSet(OrgBulkModelViewSet):
|
|||
filterset_class = AccountTemplateFilterSet
|
||||
search_fields = ('username', 'name')
|
||||
serializer_classes = {
|
||||
'default': serializers.AccountTemplateSerializer
|
||||
'default': serializers.AccountTemplateSerializer,
|
||||
}
|
||||
rbac_perms = {
|
||||
'su_from_account_templates': 'accounts.view_accounttemplate',
|
||||
}
|
||||
|
||||
@action(methods=['get'], detail=False, url_path='su-from-account-templates')
|
||||
def su_from_account_templates(self, request, *args, **kwargs):
|
||||
pk = request.query_params.get('template_id')
|
||||
template = AccountTemplate.objects.filter(pk=pk).first()
|
||||
templates = AccountTemplate.get_su_from_account_templates(template)
|
||||
templates = self.filter_queryset(templates)
|
||||
serializer = self.get_serializer(templates, many=True)
|
||||
return Response(data=serializer.data)
|
||||
|
||||
|
||||
class AccountTemplateSecretsViewSet(RecordViewLogMixin, AccountTemplateViewSet):
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
# Generated by Django 3.2.17 on 2023-05-06 06:43
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0010_gatheraccountsautomation_is_sync_account'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='accounttemplate',
|
||||
name='su_from',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='su_to', to='accounts.accounttemplate', verbose_name='Su from'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='changesecretautomation',
|
||||
name='ssh_key_change_strategy',
|
||||
field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='pushaccountautomation',
|
||||
name='ssh_key_change_strategy',
|
||||
field=models.CharField(choices=[('add', 'Append SSH KEY'), ('set', 'Empty and append SSH KEY'), ('set_jms', 'Replace (Replace only keys pushed by JumpServer) ')], default='add', max_length=16, verbose_name='SSH key change strategy'),
|
||||
),
|
||||
]
|
|
@ -1,5 +1,5 @@
|
|||
from django.db import models
|
||||
from django.db.models import Count
|
||||
from django.db.models import Count, Q
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
@ -108,6 +108,11 @@ class Account(AbsConnectivity, BaseAccount):
|
|||
|
||||
|
||||
class AccountTemplate(BaseAccount):
|
||||
su_from = models.ForeignKey(
|
||||
'self', related_name='su_to', null=True,
|
||||
on_delete=models.SET_NULL, verbose_name=_("Su from")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Account template')
|
||||
unique_together = (
|
||||
|
@ -118,6 +123,21 @@ class AccountTemplate(BaseAccount):
|
|||
('change_accounttemplatesecret', _('Can change asset account template secret')),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_su_from_account_templates(cls, instance=None):
|
||||
if not instance:
|
||||
return cls.objects.all()
|
||||
return cls.objects.exclude(Q(id=instance.id) | Q(su_from=instance))
|
||||
|
||||
def get_su_from_account(self, asset):
|
||||
su_from = self.su_from
|
||||
if su_from and asset.platform.su_enabled:
|
||||
account = asset.accounts.filter(
|
||||
username=su_from.username,
|
||||
secret_type=su_from.secret_type
|
||||
).first()
|
||||
return account
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
|
|||
|
||||
self._template = template
|
||||
# Set initial data from template
|
||||
ignore_fields = ['id', 'date_created', 'date_updated', 'org_id']
|
||||
ignore_fields = ['id', 'date_created', 'date_updated', 'su_from', 'org_id']
|
||||
field_names = [
|
||||
field.name for field in template._meta.fields
|
||||
if field.name not in ignore_fields
|
||||
|
@ -151,6 +151,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
|
|||
template = self._template
|
||||
if template is None:
|
||||
return
|
||||
|
||||
validated_data['source'] = Source.TEMPLATE
|
||||
validated_data['source_id'] = str(template.id)
|
||||
|
||||
|
@ -238,6 +239,9 @@ class AssetAccountBulkSerializerResultSerializer(serializers.Serializer):
|
|||
class AssetAccountBulkSerializer(
|
||||
AccountCreateUpdateSerializerMixin, AuthValidateMixin, serializers.ModelSerializer
|
||||
):
|
||||
su_from_username = serializers.CharField(
|
||||
max_length=128, required=False, write_only=True, allow_null=True, label=_("Su from")
|
||||
)
|
||||
assets = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, many=True, label=_('Assets'))
|
||||
|
||||
class Meta:
|
||||
|
@ -245,7 +249,7 @@ class AssetAccountBulkSerializer(
|
|||
fields = [
|
||||
'name', 'username', 'secret', 'secret_type',
|
||||
'privileged', 'is_active', 'comment', 'template',
|
||||
'on_invalid', 'push_now', 'assets',
|
||||
'on_invalid', 'push_now', 'assets', 'su_from_username'
|
||||
]
|
||||
extra_kwargs = {
|
||||
'name': {'required': False},
|
||||
|
@ -293,8 +297,20 @@ class AssetAccountBulkSerializer(
|
|||
raise serializers.ValidationError(_('Account already exists'))
|
||||
return instance, True, 'created'
|
||||
|
||||
def generate_su_from_data(self, validated_data):
|
||||
template = self._template
|
||||
asset = validated_data['asset']
|
||||
su_from = validated_data.get('su_from')
|
||||
su_from_username = validated_data.pop('su_from_username', None)
|
||||
if template:
|
||||
su_from = template.get_su_from_account()
|
||||
elif su_from_username:
|
||||
su_from = asset.accounts.filter(username=su_from_username).first()
|
||||
validated_data['su_from'] = su_from
|
||||
|
||||
def perform_create(self, vd, handler):
|
||||
lookup = self.get_filter_lookup(vd)
|
||||
self.generate_su_from_data(vd)
|
||||
try:
|
||||
instance, changed, state = handler(vd, lookup)
|
||||
except IntegrityError:
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from accounts.models import AccountTemplate, Account
|
||||
from common.serializers import SecretReadableMixin
|
||||
from common.serializers.fields import ObjectRelatedField
|
||||
from .base import BaseAccountSerializer
|
||||
|
||||
|
||||
|
@ -9,9 +11,14 @@ class AccountTemplateSerializer(BaseAccountSerializer):
|
|||
is_sync_account = serializers.BooleanField(default=False, write_only=True)
|
||||
_is_sync_account = False
|
||||
|
||||
su_from = ObjectRelatedField(
|
||||
required=False, queryset=AccountTemplate.objects, allow_null=True,
|
||||
allow_empty=True, label=_('Su from'), attrs=('id', 'name', 'username')
|
||||
)
|
||||
|
||||
class Meta(BaseAccountSerializer.Meta):
|
||||
model = AccountTemplate
|
||||
fields = BaseAccountSerializer.Meta.fields + ['is_sync_account']
|
||||
fields = BaseAccountSerializer.Meta.fields + ['is_sync_account', 'su_from']
|
||||
|
||||
def sync_accounts_secret(self, instance, diff):
|
||||
if not self._is_sync_account or 'secret' not in diff:
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.17 on 2023-05-06 06:43
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assets', '0116_auto_20230418_1726'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='baseautomation',
|
||||
name='params',
|
||||
field=models.JSONField(default=dict, verbose_name='Parameters'),
|
||||
),
|
||||
]
|
|
@ -109,6 +109,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer):
|
|||
if condition in username_secret_type_dict:
|
||||
continue
|
||||
account_data = {key: getattr(template, key) for key in account_attribute}
|
||||
account_data['su_from'] = template.get_su_from_account(asset)
|
||||
account_data['name'] = f"{account_data['name']}-{_('Account template')}"
|
||||
need_create_accounts.append(Account(**{'asset_id': asset.id, **account_data}))
|
||||
return Account.objects.bulk_create(need_create_accounts)
|
||||
|
|
Loading…
Reference in New Issue