Browse Source

perf: 优化 accounts

pull/8023/head^2
ibuler 2 years ago
parent
commit
29c9c6d680
  1. 1
      apps/assets/migrations/0093_auto_20220711_1413.py
  2. 23
      apps/assets/migrations/0096_auto_20220714_1627.py
  3. 2
      apps/assets/models/account.py
  4. 10
      apps/assets/models/asset.py
  5. 168
      apps/assets/models/user.py
  6. 35
      apps/assets/serializers/account.py
  7. 19
      apps/assets/serializers/account_history.py
  8. 23
      apps/assets/serializers/asset.py
  9. 8
      apps/assets/serializers/system_user.py
  10. 34
      apps/assets/signal_handlers/asset.py
  11. 44
      apps/assets/signal_handlers/authbook.py
  12. 33
      apps/assets/signal_handlers/system_user.py
  13. 6
      apps/assets/task_handlers/backup/handlers.py
  14. 1
      apps/assets/tasks/common.py
  15. 2
      apps/assets/tasks/utils.py
  16. 2
      apps/audits/const.py
  17. 1
      apps/jumpserver/settings/custom.py
  18. 12
      apps/locale/zh/LC_MESSAGES/django.po
  19. 2
      apps/perms/serializers/system_user_permission.py
  20. 2
      utils/create_assets_user/bulk_create_user.py

1
apps/assets/migrations/0093_auto_20220711_1413.py

@ -39,6 +39,7 @@ def migrate_accounts(apps, schema_editor):
values.update({attr: getattr(system_user, attr) for attr in auth_attrs}) values.update({attr: getattr(system_user, attr) for attr in auth_attrs})
values['protocol'] = system_user.protocol values['protocol'] = system_user.protocol
values['created_by'] = str(system_user.id) values['created_by'] = str(system_user.id)
values['type'] = system_user.type
auth_book_auth = {attr: getattr(auth_book, attr) for attr in auth_attrs} auth_book_auth = {attr: getattr(auth_book, attr) for attr in auth_attrs}
auth_book_auth = {attr: value for attr, value in auth_book_auth.items() if value} auth_book_auth = {attr: value for attr, value in auth_book_auth.items() if value}

23
apps/assets/migrations/0096_auto_20220714_1627.py

@ -0,0 +1,23 @@
# Generated by Django 3.2.12 on 2022-07-14 08:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0095_auto_20220713_1746'),
]
operations = [
migrations.RenameField(
model_name='systemuser',
old_name='auto_push',
new_name='auto_push_account',
),
migrations.AddField(
model_name='systemuser',
name='auto_create_account',
field=models.BooleanField(default=False, verbose_name='Auto account if not exist'),
),
]

2
apps/assets/models/account.py

@ -32,4 +32,4 @@ class Account(BaseUser, AbsConnectivity, ProtocolMixin):
] ]
def __str__(self): def __str__(self):
return '{}//{}@{}'.format(self.protocol, self.username, self.asset.hostname) return '{}://{}@{}'.format(self.protocol, self.username, self.asset.hostname)

10
apps/assets/models/asset.py

@ -238,16 +238,6 @@ class Asset(AbsConnectivity, AbsHardwareInfo, ProtocolsMixin, NodesRelationMixin
def get_target_ip(self): def get_target_ip(self):
return self.ip return self.ip
def set_admin_user_relation(self):
from .authbook import AuthBook
if not self.admin_user:
return
if self.admin_user.type != 'admin':
raise ValidationError('System user should be type admin')
defaults = {'asset': self, 'systemuser': self.admin_user, 'org_id': self.org_id}
AuthBook.objects.get_or_create(defaults=defaults, asset=self, systemuser=self.admin_user)
@property @property
def admin_user_display(self): def admin_user_display(self):
if not self.admin_user: if not self.admin_user:

168
apps/assets/models/user.py

@ -7,12 +7,10 @@ import logging
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.cache import cache
from common.utils import signer, get_object_or_none from common.utils import signer
from .base import BaseUser from .base import BaseUser
from .asset import Asset from .asset import Asset
from .authbook import AuthBook
__all__ = ['AdminUser', 'SystemUser', 'ProtocolMixin'] __all__ = ['AdminUser', 'SystemUser', 'ProtocolMixin']
@ -82,155 +80,11 @@ class ProtocolMixin:
return self.protocol in self.ASSET_CATEGORY_PROTOCOLS return self.protocol in self.ASSET_CATEGORY_PROTOCOLS
class AuthMixin: class SystemUser(ProtocolMixin, BaseUser):
username_same_with_user: bool
protocol: str
ASSET_CATEGORY_PROTOCOLS: list
login_mode: str
LOGIN_MANUAL: str
id: str
username: str
password: str
private_key: str
public_key: str
def set_temp_auth(self, asset_or_app_id, user_id, auth, ttl=300):
if not auth:
raise ValueError('Auth not set')
key = 'TEMP_PASSWORD_{}_{}_{}'.format(self.id, asset_or_app_id, user_id)
logger.debug(f'Set system user temp auth: {key}')
cache.set(key, auth, ttl)
def get_temp_auth(self, asset_or_app_id, user_id):
key = 'TEMP_PASSWORD_{}_{}_{}'.format(self.id, asset_or_app_id, user_id)
logger.debug(f'Get system user temp auth: {key}')
password = cache.get(key)
return password
def _clean_auth_info_if_manual_login_mode(self):
if self.login_mode == self.LOGIN_MANUAL:
self.password = ''
self.private_key = ''
self.public_key = ''
def _load_tmp_auth_if_has(self, asset_or_app_id, user_id):
if self.login_mode != self.LOGIN_MANUAL:
return
if not asset_or_app_id or not user_id:
return
auth = self.get_temp_auth(asset_or_app_id, user_id)
if not auth:
return
username = auth.get('username')
password = auth.get('password')
if username:
self.username = username
if password:
self.password = password
def load_app_more_auth(self, app_id=None, username=None, user_id=None):
# 清除认证信息
self._clean_auth_info_if_manual_login_mode()
# 先加载临时认证信息
if self.login_mode == self.LOGIN_MANUAL:
self._load_tmp_auth_if_has(app_id, user_id)
return
# Remote app
from applications.models import Application
app = get_object_or_none(Application, pk=app_id)
if app and app.category_remote_app:
# Remote app
self._load_remoteapp_more_auth(app, username, user_id)
return
# Other app
# 更新用户名
from users.models import User
user = get_object_or_none(User, pk=user_id) if user_id else None
if self.username_same_with_user:
if user and not username:
_username = user.username
else:
_username = username
self.username = _username
def _load_remoteapp_more_auth(self, app, username, user_id):
asset = app.get_remote_app_asset(raise_exception=False)
if asset:
self.load_asset_more_auth(asset_id=asset.id, username=username, user_id=user_id)
def load_asset_special_auth(self, asset, username=''):
"""
AuthBook 的数据状态
| asset | systemuser | username |
1 | * | * | x |
2 | * | x | * |
当前 AuthBook 只有以上两种状态systemuser username 不会并存
正常的资产与系统用户关联产生的是第1种状态改密则产生第2种状态改密之后
只有 username 而没有 systemuser
Freq: 关联同一资产的多个系统用户指定同一用户名时修改用户密码会影响所有系统用户
这里有一个不对称的行为同名系统用户密码覆盖
当有相同 username 的多个系统用户时有改密动作之后所有的同名系统用户都使用最后
一次改动但如果没有发生过改密同名系统用户使用的密码还是各自的
"""
if username == '':
username = self.username
authbook = AuthBook.objects.filter(
asset=asset, username=username, systemuser__isnull=True
).order_by('-date_created').first()
if not authbook:
authbook = AuthBook.objects.filter(
asset=asset, systemuser=self
).order_by('-date_created').first()
if not authbook:
return None
authbook.load_auth()
self.password = authbook.password
self.private_key = authbook.private_key
self.public_key = authbook.public_key
def load_asset_more_auth(self, asset_id=None, username=None, user_id=None):
from users.models import User
self._clean_auth_info_if_manual_login_mode()
# 加载临时认证信息
if self.login_mode == self.LOGIN_MANUAL:
self._load_tmp_auth_if_has(asset_id, user_id)
return
# 更新用户名
user = get_object_or_none(User, pk=user_id) if user_id else None
if self.username_same_with_user:
if user and not username:
_username = user.username
else:
_username = username
self.username = _username
# 加载某个资产的特殊配置认证信息
asset = get_object_or_none(Asset, pk=asset_id) if asset_id else None
if not asset:
logger.debug('Asset not found, pass')
return
self.load_asset_special_auth(asset, self.username)
class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
LOGIN_AUTO = 'auto' LOGIN_AUTO = 'auto'
LOGIN_MANUAL = 'manual' LOGIN_MANUAL = 'manual'
LOGIN_MODE_CHOICES = ( LOGIN_MODE_CHOICES = (
(LOGIN_AUTO, _('Automatic managed')), (LOGIN_AUTO, _('使用账号')),
(LOGIN_MANUAL, _('Manually input')) (LOGIN_MANUAL, _('Manually input'))
) )
@ -246,13 +100,19 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
) )
users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users")) users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users"))
groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups")) groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups"))
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_('Type')) priority = models.IntegerField(
priority = models.IntegerField(default=81, verbose_name=_("Priority"), help_text=_("1-100, the lower the value will be match first"), validators=[MinValueValidator(1), MaxValueValidator(100)]) default=81, verbose_name=_("Priority"),
help_text=_("1-100, the lower the value will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)]
)
protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol')) protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
auto_create_account = models.BooleanField(default=False, verbose_name=_("自动创建账号"))
auto_push_account = models.BooleanField(default=True, verbose_name=_('推送账号到资产'))
type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_('Type'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo')) sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell'))
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root")) sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
token = models.TextField(default='', verbose_name=_('Token')) token = models.TextField(default='', verbose_name=_('Token'))
home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True) home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True)
@ -277,7 +137,7 @@ class SystemUser(ProtocolMixin, AuthMixin, BaseUser):
return self.get_login_mode_display() return self.get_login_mode_display()
def is_need_push(self): def is_need_push(self):
if self.auto_push and self.is_protocol_support_push: if self.auto_push_account and self.is_protocol_support_push:
return True return True
else: else:
return False return False

35
apps/assets/serializers/account.py

@ -1,7 +1,8 @@
from rest_framework import serializers from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db.models import F
from assets.models import AuthBook from assets.models import Account
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .base import AuthSerializerMixin from .base import AuthSerializerMixin
@ -13,7 +14,6 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
ip = serializers.ReadOnlyField(label=_("IP")) ip = serializers.ReadOnlyField(label=_("IP"))
hostname = serializers.ReadOnlyField(label=_("Hostname")) hostname = serializers.ReadOnlyField(label=_("Hostname"))
platform = serializers.ReadOnlyField(label=_("Platform")) platform = serializers.ReadOnlyField(label=_("Platform"))
protocols = serializers.SerializerMethodField(label=_("Protocols"))
date_created = serializers.DateTimeField( date_created = serializers.DateTimeField(
label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True
) )
@ -22,18 +22,20 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
) )
class Meta: class Meta:
model = AuthBook model = Account
fields_mini = ['id', 'username', 'ip', 'hostname', 'platform', 'protocols', 'version'] fields_mini = [
fields_write_only = ['password', 'private_key', "public_key", 'passphrase'] 'id', 'type', 'username', 'ip', 'hostname',
'platform', 'protocol', 'version'
]
fields_write_only = ['password', 'private_key', 'public_key', 'passphrase']
fields_other = ['date_created', 'date_updated', 'connectivity', 'date_verified', 'comment'] fields_other = ['date_created', 'date_updated', 'connectivity', 'date_verified', 'comment']
fields_small = fields_mini + fields_write_only + fields_other fields_small = fields_mini + fields_write_only + fields_other
fields_fk = ['asset', 'systemuser', 'systemuser_display'] fields_fk = ['asset']
fields = fields_small + fields_fk fields = fields_small + fields_fk
extra_kwargs = { extra_kwargs = {
'username': {'required': True}, 'username': {'required': True},
'private_key': {'write_only': True}, 'private_key': {'write_only': True},
'public_key': {'write_only': True}, 'public_key': {'write_only': True},
'systemuser_display': {'label': _('System user display')}
} }
ref_name = 'AssetAccountSerializer' ref_name = 'AssetAccountSerializer'
@ -52,27 +54,14 @@ class AccountSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
attrs = self._validate_gen_key(attrs) attrs = self._validate_gen_key(attrs)
return attrs return attrs
def get_protocols(self, v):
""" protocols 是 queryset 中返回的,Post 创建成功后返回序列化时没有这个字段 """
if hasattr(v, 'protocols'):
protocols = v.protocols
elif hasattr(v, 'asset') and v.asset:
protocols = v.asset.protocols
else:
protocols = ''
protocols = protocols.replace(' ', ', ')
return protocols
@classmethod @classmethod
def setup_eager_loading(cls, queryset): def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """ """ Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('systemuser', 'asset') queryset = queryset.prefetch_related('asset')\
.annotate(ip=F('asset__ip')) \
.annotate(hostname=F('asset__hostname'))
return queryset return queryset
def to_representation(self, instance):
instance.load_auth()
return super().to_representation(instance)
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer): class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
class Meta(AccountSerializer.Meta): class Meta(AccountSerializer.Meta):

19
apps/assets/serializers/account_history.py

@ -1,29 +1,20 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from assets.models import AuthBook from assets.models import Account
from common.drf.serializers import SecretReadableMixin from common.drf.serializers import SecretReadableMixin
from .account import AccountSerializer, AccountSecretSerializer from .account import AccountSerializer, AccountSecretSerializer
class AccountHistorySerializer(AccountSerializer): class AccountHistorySerializer(AccountSerializer):
systemuser_display = serializers.SerializerMethodField(label=_('System user display'))
class Meta: class Meta:
model = AuthBook.history.model model = Account.history.model
fields = AccountSerializer.Meta.fields_mini + \ fields = AccountSerializer.Meta.fields_mini + \
AccountSerializer.Meta.fields_write_only + \ AccountSerializer.Meta.fields_write_only + \
AccountSerializer.Meta.fields_fk + \ AccountSerializer.Meta.fields_fk + \
['history_id', 'date_created', 'date_updated'] ['history_id', 'date_created', 'date_updated']
read_only_fields = fields read_only_fields = fields
ref_name = 'AccountHistorySerializer' ref_name = 'AccountHistorySerializer'
@staticmethod
def get_systemuser_display(instance):
if not instance.systemuser:
return ''
return str(instance.systemuser)
def get_field_names(self, declared_fields, info): def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info) fields = super().get_field_names(declared_fields, info)
fields = list(set(fields) - {'org_name'}) fields = list(set(fields) - {'org_name'})

23
apps/assets/serializers/asset.py

@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import Asset, Node, Platform, SystemUser from ..models import Asset, Node, Platform, SystemUser
from .account import AccountSerializer
__all__ = [ __all__ = [
'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer',
@ -70,6 +71,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
labels_display = serializers.ListField( labels_display = serializers.ListField(
child=serializers.CharField(), label=_('Labels name'), required=False, read_only=True child=serializers.CharField(), label=_('Labels name'), required=False, read_only=True
) )
accounts = AccountSerializer(many=True, write_only=True, required=False)
""" """
资产的数据结构 资产的数据结构
@ -92,7 +94,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
'domain', 'domain_display', 'platform', 'admin_user', 'admin_user_display' 'domain', 'domain_display', 'platform', 'admin_user', 'admin_user_display'
] ]
fields_m2m = [ fields_m2m = [
'nodes', 'nodes_display', 'labels', 'labels_display', 'nodes', 'nodes_display', 'labels', 'labels_display', 'accounts'
] ]
read_only_fields = [ read_only_fields = [
'connectivity', 'date_verified', 'cpu_info', 'hardware_info', 'connectivity', 'date_verified', 'cpu_info', 'hardware_info',
@ -107,6 +109,11 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
'cpu_info': {'label': _('CPU info')}, 'cpu_info': {'label': _('CPU info')},
} }
def __init__(self, *args, **kwargs):
data = kwargs.get('data', {})
self.accounts_data = data.pop('accounts', [])
super().__init__(*args, **kwargs)
def get_fields(self): def get_fields(self):
fields = super().get_fields() fields = super().get_fields()
@ -151,10 +158,24 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
nodes_to_set.append(node) nodes_to_set.append(node)
instance.nodes.set(nodes_to_set) instance.nodes.set(nodes_to_set)
@staticmethod
def add_accounts(instance, accounts_data):
for data in accounts_data:
data['asset'] = instance.id
print("Data: ", accounts_data)
serializer = AccountSerializer(data=accounts_data, many=True)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
raise serializers.ValidationError({'accounts': e})
serializer.save()
def create(self, validated_data): def create(self, validated_data):
self.compatible_with_old_protocol(validated_data) self.compatible_with_old_protocol(validated_data)
nodes_display = validated_data.pop('nodes_display', '') nodes_display = validated_data.pop('nodes_display', '')
instance = super().create(validated_data) instance = super().create(validated_data)
if self.accounts_data:
self.add_accounts(instance, self.accounts_data)
self.perform_nodes_display_create(instance, nodes_display) self.perform_nodes_display_create(instance, nodes_display)
return instance return instance

8
apps/assets/serializers/system_user.py

@ -47,9 +47,9 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
fields_small = fields_mini + fields_write_only + [ fields_small = fields_mini + fields_write_only + [
'token', 'ssh_key_fingerprint', 'token', 'ssh_key_fingerprint',
'type', 'type_display', 'protocol', 'is_asset_protocol', 'type', 'type_display', 'protocol', 'is_asset_protocol',
'login_mode', 'login_mode_display', 'priority', 'auto_create_account', 'login_mode', 'login_mode_display', 'priority',
'sudo', 'shell', 'sftp_root', 'home', 'system_groups', 'ad_domain', 'sudo', 'shell', 'sftp_root', 'home', 'system_groups', 'ad_domain',
'username_same_with_user', 'auto_push', 'auto_generate_key', 'username_same_with_user', 'auto_push_account', 'auto_generate_key',
'su_enabled', 'su_from', 'su_enabled', 'su_from',
'date_created', 'date_updated', 'comment', 'created_by', 'date_created', 'date_updated', 'comment', 'created_by',
] ]
@ -191,7 +191,7 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
attrs['protocol'] = SystemUser.Protocol.ssh attrs['protocol'] = SystemUser.Protocol.ssh
attrs['login_mode'] = SystemUser.LOGIN_AUTO attrs['login_mode'] = SystemUser.LOGIN_AUTO
attrs['username_same_with_user'] = False attrs['username_same_with_user'] = False
attrs['auto_push'] = False attrs['auto_push_account'] = False
return attrs return attrs
def _validate_gen_key(self, attrs): def _validate_gen_key(self, attrs):
@ -259,7 +259,7 @@ class SystemUserWithAuthInfoSerializer(SecretReadableMixin, SystemUserSerializer
fields_small = fields_mini + fields_write_only + [ fields_small = fields_mini + fields_write_only + [
'protocol', 'login_mode', 'login_mode_display', 'priority', 'protocol', 'login_mode', 'login_mode_display', 'priority',
'sudo', 'shell', 'ad_domain', 'sftp_root', 'token', 'sudo', 'shell', 'ad_domain', 'sftp_root', 'token',
"username_same_with_user", 'auto_push', 'auto_generate_key', "username_same_with_user", 'auto_push_account', 'auto_generate_key',
'comment', 'comment',
] ]
fields = fields_small fields = fields_small

34
apps/assets/signal_handlers/asset.py

@ -52,8 +52,6 @@ def on_asset_created_or_update(sender, instance=None, created=False, **kwargs):
if not has_node: if not has_node:
instance.nodes.add(Node.org_root()) instance.nodes.add(Node.org_root())
instance.set_admin_user_relation()
@receiver(m2m_changed, sender=Asset.nodes.through) @receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs): def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs):
@ -89,22 +87,22 @@ def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs):
systemuser_id__in=system_user_ids, asset_id__in=asset_ids systemuser_id__in=system_user_ids, asset_id__in=asset_ids
).values_list('systemuser_id', 'asset_id')) ).values_list('systemuser_id', 'asset_id'))
# TODO 优化 # TODO 优化
to_create = [] # to_create = []
for system_user_id in system_user_ids: # for system_user_id in system_user_ids:
asset_ids_to_push = [] # asset_ids_to_push = []
for asset_id in asset_ids: # for asset_id in asset_ids:
if (system_user_id, asset_id) in exist: # if (system_user_id, asset_id) in exist:
continue # continue
asset_ids_to_push.append(asset_id) # asset_ids_to_push.append(asset_id)
to_create.append(m2m_model( # to_create.append(m2m_model(
systemuser_id=system_user_id, # systemuser_id=system_user_id,
asset_id=asset_id, # asset_id=asset_id,
org_id=instance.org_id # org_id=instance.org_id
)) # ))
if asset_ids_to_push: # if asset_ids_to_push:
push_system_user_to_assets.delay(system_user_id, asset_ids_to_push) # push_system_user_to_assets.delay(system_user_id, asset_ids_to_push)
m2m_model.objects.bulk_create(to_create) # m2m_model.objects.bulk_create(to_create)
#
RELATED_NODE_IDS = '_related_node_ids' RELATED_NODE_IDS = '_related_node_ids'

44
apps/assets/signal_handlers/authbook.py

@ -1,50 +1,14 @@
from django.dispatch import receiver from django.dispatch import receiver
from django.apps import apps from django.db.models.signals import pre_save
from simple_history.signals import pre_create_historical_record
from django.db.models.signals import post_save, pre_save, pre_delete
from common.utils import get_logger from common.utils import get_logger
from ..models import AuthBook, SystemUser from ..models import Account
AuthBookHistory = apps.get_model('assets', 'HistoricalAuthBook')
logger = get_logger(__name__) logger = get_logger(__name__)
@receiver(pre_create_historical_record, sender=AuthBookHistory) @receiver(pre_save, sender=Account)
def pre_create_historical_record_callback(sender, history_instance=None, **kwargs): def on_account_pre_create(sender, instance, **kwargs):
attrs_to_copy = ['username', 'password', 'private_key']
for attr in attrs_to_copy:
if getattr(history_instance, attr):
continue
try:
system_user = history_instance.systemuser
except SystemUser.DoesNotExist:
continue
if not system_user:
continue
system_user_attr_value = getattr(history_instance.systemuser, attr)
if system_user_attr_value:
setattr(history_instance, attr, system_user_attr_value)
@receiver(pre_delete, sender=AuthBook)
def on_authbook_post_delete(sender, instance, **kwargs):
instance.remove_asset_admin_user_if_need()
@receiver(post_save, sender=AuthBook)
def on_authbook_post_create(sender, instance, created, **kwargs):
instance.sync_to_system_user_account()
if created:
pass
# # 不再自动更新资产管理用户,只允许用户手动指定。
# 只在创建时进行更新资产的管理用户
# instance.update_asset_admin_user_if_need()
@receiver(pre_save, sender=AuthBook)
def on_authbook_pre_create(sender, instance, **kwargs):
# 升级版本号 # 升级版本号
instance.version += 1 instance.version += 1
# 即使在 root 组织也不怕 # 即使在 root 组织也不怕

33
apps/assets/signal_handlers/system_user.py

@ -9,9 +9,8 @@ from common.exceptions import M2MReverseNotAllowed
from common.const.signals import POST_ADD from common.const.signals import POST_ADD
from common.utils import get_logger from common.utils import get_logger
from common.decorator import on_transaction_commit from common.decorator import on_transaction_commit
from assets.models import Asset, SystemUser, Node, AuthBook from assets.models import Asset, SystemUser, Node
from users.models import User from users.models import User
from orgs.utils import tmp_to_root_org
from assets.tasks import ( from assets.tasks import (
push_system_user_to_assets_manual, push_system_user_to_assets_manual,
push_system_user_to_assets, push_system_user_to_assets,
@ -39,35 +38,7 @@ def on_system_user_assets_change(instance, action, model, pk_set, **kwargs):
else: else:
system_user_ids = pk_set system_user_ids = pk_set
asset_ids = [instance.id] asset_ids = [instance.id]
# todo: Auto create account if need
org_id = instance.org_id
# 关联创建的 authbook 没有系统用户id
with tmp_to_root_org():
authbooks = AuthBook.objects.filter(
asset_id__in=asset_ids,
systemuser_id__in=system_user_ids
)
if action == POST_ADD:
authbooks.update(org_id=org_id)
save_action_mapper = {
'pre_add': pre_save,
'post_add': post_save,
'pre_remove': pre_delete,
'post_remove': post_delete
}
for ab in authbooks:
ab.org_id = org_id
save_action = save_action_mapper[action]
logger.debug('Send AuthBook post save signal: {} -> {}'.format(action, ab.id))
save_action.send(sender=AuthBook, instance=ab, created=True)
if action == POST_ADD:
for system_user_id in system_user_ids:
push_system_user_to_assets.delay(system_user_id, asset_ids)
@receiver(m2m_changed, sender=SystemUser.users.through) @receiver(m2m_changed, sender=SystemUser.users.through)

6
apps/assets/task_handlers/backup/handlers.py

@ -7,7 +7,7 @@ from django.conf import settings
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from assets.models import AuthBook from assets.models import Account
from assets.serializers import AccountSecretSerializer from assets.serializers import AccountSecretSerializer
from assets.notifications import AccountBackupExecutionTaskMsg from assets.notifications import AccountBackupExecutionTaskMsg
from applications.models import Account from applications.models import Account
@ -74,9 +74,9 @@ class AssetAccountHandler(BaseAccountHandler):
@classmethod @classmethod
def create_data_map(cls): def create_data_map(cls):
data_map = defaultdict(list) data_map = defaultdict(list)
sheet_name = AuthBook._meta.verbose_name sheet_name = Account._meta.verbose_name
accounts = AuthBook.get_queryset().select_related('systemuser') accounts = Account.get_queryset()
if not accounts.first(): if not accounts.first():
return data_map return data_map

1
apps/assets/tasks/common.py

@ -9,6 +9,7 @@ from assets.models import AuthBook
__all__ = ['add_nodes_assets_to_system_users'] __all__ = ['add_nodes_assets_to_system_users']
# Todo: 等待优化
@shared_task @shared_task
@tmp_to_root_org() @tmp_to_root_org()
def add_nodes_assets_to_system_users(nodes_keys, system_users): def add_nodes_assets_to_system_users(nodes_keys, system_users):

2
apps/assets/tasks/utils.py

@ -25,7 +25,7 @@ def check_asset_can_run_ansible(asset):
def check_system_user_can_run_ansible(system_user): def check_system_user_can_run_ansible(system_user):
if not system_user.auto_push: if not system_user.auto_push_account:
logger.warn(f'Push system user task skip, auto push not enable: system_user={system_user.name}') logger.warn(f'Push system user task skip, auto push not enable: system_user={system_user.name}')
return False return False
if not system_user.is_protocol_support_push: if not system_user.is_protocol_support_push:

2
apps/audits/const.py

@ -11,7 +11,7 @@ MODELS_NEED_RECORD = (
'LoginACL', 'LoginAssetACL', 'LoginConfirmSetting', 'LoginACL', 'LoginAssetACL', 'LoginConfirmSetting',
# assets # assets
'Asset', 'Node', 'AdminUser', 'SystemUser', 'Domain', 'Gateway', 'CommandFilterRule', 'Asset', 'Node', 'AdminUser', 'SystemUser', 'Domain', 'Gateway', 'CommandFilterRule',
'CommandFilter', 'Platform', 'AuthBook', 'CommandFilter', 'Platform', 'Account',
# applications # applications
'Application', 'Application',
# orgs # orgs

1
apps/jumpserver/settings/custom.py

@ -81,7 +81,6 @@ TERMINAL_HOST_KEY = CONFIG.TERMINAL_HOST_KEY
TERMINAL_HEADER_TITLE = CONFIG.TERMINAL_HEADER_TITLE TERMINAL_HEADER_TITLE = CONFIG.TERMINAL_HEADER_TITLE
TERMINAL_TELNET_REGEX = CONFIG.TERMINAL_TELNET_REGEX TERMINAL_TELNET_REGEX = CONFIG.TERMINAL_TELNET_REGEX
# Asset user auth external backend, default AuthBook backend
BACKEND_ASSET_USER_AUTH_VAULT = False BACKEND_ASSET_USER_AUTH_VAULT = False
PERM_SINGLE_ASSET_TO_UNGROUP_NODE = CONFIG.PERM_SINGLE_ASSET_TO_UNGROUP_NODE PERM_SINGLE_ASSET_TO_UNGROUP_NODE = CONFIG.PERM_SINGLE_ASSET_TO_UNGROUP_NODE

12
apps/locale/zh/LC_MESSAGES/django.po

@ -785,13 +785,13 @@ msgstr "密码"
#: xpack/plugins/change_auth_plan/models/asset.py:130 #: xpack/plugins/change_auth_plan/models/asset.py:130
#: xpack/plugins/change_auth_plan/models/asset.py:206 #: xpack/plugins/change_auth_plan/models/asset.py:206
msgid "SSH private key" msgid "SSH private key"
msgstr "SSH密钥" msgstr "SSH 密钥"
#: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:56 #: assets/models/base.py:179 xpack/plugins/change_auth_plan/models/asset.py:56
#: xpack/plugins/change_auth_plan/models/asset.py:126 #: xpack/plugins/change_auth_plan/models/asset.py:126
#: xpack/plugins/change_auth_plan/models/asset.py:202 #: xpack/plugins/change_auth_plan/models/asset.py:202
msgid "SSH public key" msgid "SSH public key"
msgstr "SSH公钥" msgstr "SSH 公钥"
#: assets/models/cluster.py:20 #: assets/models/cluster.py:20
msgid "Bandwidth" msgid "Bandwidth"
@ -2067,7 +2067,7 @@ msgstr "Access key"
#: authentication/models.py:41 #: authentication/models.py:41
msgid "Private Token" msgid "Private Token"
msgstr "SSH密钥" msgstr "SSH 密钥"
#: authentication/models.py:50 #: authentication/models.py:50
msgid "Expired" msgid "Expired"
@ -5468,7 +5468,7 @@ msgstr "原来密码错误"
#: users/forms/profile.py:129 #: users/forms/profile.py:129
msgid "Automatically configure and download the SSH key" msgid "Automatically configure and download the SSH key"
msgstr "自动配置并下载SSH密钥" msgstr "自动配置并下载 SSH 密钥"
#: users/forms/profile.py:131 #: users/forms/profile.py:131
msgid "ssh public key" msgid "ssh public key"
@ -5489,11 +5489,11 @@ msgstr "不能和原来的密钥相同"
#: users/forms/profile.py:150 users/serializers/profile.py:100 #: users/forms/profile.py:150 users/serializers/profile.py:100
#: users/serializers/profile.py:183 users/serializers/profile.py:210 #: users/serializers/profile.py:183 users/serializers/profile.py:210
msgid "Not a valid ssh public key" msgid "Not a valid ssh public key"
msgstr "SSH密钥不合法" msgstr "SSH 密钥不合法"
#: users/forms/profile.py:161 users/models/user.py:692 #: users/forms/profile.py:161 users/models/user.py:692
msgid "Public key" msgid "Public key"
msgstr "SSH公钥" msgstr "SSH 公钥"
#: users/models/user.py:558 #: users/models/user.py:558
msgid "Force enable" msgid "Force enable"

2
apps/perms/serializers/system_user_permission.py

@ -13,7 +13,7 @@ class SystemUserSerializer(serializers.ModelSerializer):
'id', 'name', 'username', 'protocol', 'id', 'name', 'username', 'protocol',
'login_mode', 'login_mode_display', 'login_mode', 'login_mode_display',
'priority', 'username_same_with_user', 'priority', 'username_same_with_user',
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'auto_push_account', 'cmd_filters', 'sudo', 'shell', 'comment',
'sftp_root', 'date_created', 'created_by' 'sftp_root', 'date_created', 'created_by'
] ]
ref_name = 'PermedSystemUserSerializer' ref_name = 'PermedSystemUserSerializer'

2
utils/create_assets_user/bulk_create_user.py

@ -113,7 +113,7 @@ class UserCreation:
"username": username, "username": username,
"password": password, "password": password,
"protocol": protocol, "protocol": protocol,
"auto_push": bool(int(auto_push)), "auto_push_account": bool(int(auto_push)),
"login_mode": "auto" "login_mode": "auto"
} }
users.append(info) users.append(info)

Loading…
Cancel
Save