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 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 import serializers
|
||||||
from accounts.models import AccountTemplate
|
from accounts.models import AccountTemplate
|
||||||
|
@ -38,8 +40,20 @@ class AccountTemplateViewSet(OrgBulkModelViewSet):
|
||||||
filterset_class = AccountTemplateFilterSet
|
filterset_class = AccountTemplateFilterSet
|
||||||
search_fields = ('username', 'name')
|
search_fields = ('username', 'name')
|
||||||
serializer_classes = {
|
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):
|
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 import models
|
||||||
from django.db.models import Count
|
from django.db.models import Count, Q
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from simple_history.models import HistoricalRecords
|
from simple_history.models import HistoricalRecords
|
||||||
|
@ -108,6 +108,11 @@ class Account(AbsConnectivity, BaseAccount):
|
||||||
|
|
||||||
|
|
||||||
class AccountTemplate(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:
|
class Meta:
|
||||||
verbose_name = _('Account template')
|
verbose_name = _('Account template')
|
||||||
unique_together = (
|
unique_together = (
|
||||||
|
@ -118,6 +123,21 @@ class AccountTemplate(BaseAccount):
|
||||||
('change_accounttemplatesecret', _('Can change asset account template secret')),
|
('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):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
|
||||||
|
|
||||||
self._template = template
|
self._template = template
|
||||||
# Set initial data from 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_names = [
|
||||||
field.name for field in template._meta.fields
|
field.name for field in template._meta.fields
|
||||||
if field.name not in ignore_fields
|
if field.name not in ignore_fields
|
||||||
|
@ -151,6 +151,7 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer):
|
||||||
template = self._template
|
template = self._template
|
||||||
if template is None:
|
if template is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
validated_data['source'] = Source.TEMPLATE
|
validated_data['source'] = Source.TEMPLATE
|
||||||
validated_data['source_id'] = str(template.id)
|
validated_data['source_id'] = str(template.id)
|
||||||
|
|
||||||
|
@ -238,6 +239,9 @@ class AssetAccountBulkSerializerResultSerializer(serializers.Serializer):
|
||||||
class AssetAccountBulkSerializer(
|
class AssetAccountBulkSerializer(
|
||||||
AccountCreateUpdateSerializerMixin, AuthValidateMixin, serializers.ModelSerializer
|
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'))
|
assets = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, many=True, label=_('Assets'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -245,7 +249,7 @@ class AssetAccountBulkSerializer(
|
||||||
fields = [
|
fields = [
|
||||||
'name', 'username', 'secret', 'secret_type',
|
'name', 'username', 'secret', 'secret_type',
|
||||||
'privileged', 'is_active', 'comment', 'template',
|
'privileged', 'is_active', 'comment', 'template',
|
||||||
'on_invalid', 'push_now', 'assets',
|
'on_invalid', 'push_now', 'assets', 'su_from_username'
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'name': {'required': False},
|
'name': {'required': False},
|
||||||
|
@ -293,8 +297,20 @@ class AssetAccountBulkSerializer(
|
||||||
raise serializers.ValidationError(_('Account already exists'))
|
raise serializers.ValidationError(_('Account already exists'))
|
||||||
return instance, True, 'created'
|
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):
|
def perform_create(self, vd, handler):
|
||||||
lookup = self.get_filter_lookup(vd)
|
lookup = self.get_filter_lookup(vd)
|
||||||
|
self.generate_su_from_data(vd)
|
||||||
try:
|
try:
|
||||||
instance, changed, state = handler(vd, lookup)
|
instance, changed, state = handler(vd, lookup)
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from accounts.models import AccountTemplate, Account
|
from accounts.models import AccountTemplate, Account
|
||||||
from common.serializers import SecretReadableMixin
|
from common.serializers import SecretReadableMixin
|
||||||
|
from common.serializers.fields import ObjectRelatedField
|
||||||
from .base import BaseAccountSerializer
|
from .base import BaseAccountSerializer
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,9 +11,14 @@ class AccountTemplateSerializer(BaseAccountSerializer):
|
||||||
is_sync_account = serializers.BooleanField(default=False, write_only=True)
|
is_sync_account = serializers.BooleanField(default=False, write_only=True)
|
||||||
_is_sync_account = False
|
_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):
|
class Meta(BaseAccountSerializer.Meta):
|
||||||
model = AccountTemplate
|
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):
|
def sync_accounts_secret(self, instance, diff):
|
||||||
if not self._is_sync_account or 'secret' not in 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:
|
if condition in username_secret_type_dict:
|
||||||
continue
|
continue
|
||||||
account_data = {key: getattr(template, key) for key in account_attribute}
|
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')}"
|
account_data['name'] = f"{account_data['name']}-{_('Account template')}"
|
||||||
need_create_accounts.append(Account(**{'asset_id': asset.id, **account_data}))
|
need_create_accounts.append(Account(**{'asset_id': asset.id, **account_data}))
|
||||||
return Account.objects.bulk_create(need_create_accounts)
|
return Account.objects.bulk_create(need_create_accounts)
|
||||||
|
|
Loading…
Reference in New Issue