diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index 3e95f59ab..e8b06b537 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -1,9 +1,7 @@ from .mixin import * -from .admin_user import * from .asset import * from .label import * from .system_user import * -from .system_user_relation import * from .accounts import * from .node import * from .domain import * diff --git a/apps/assets/api/admin_user.py b/apps/assets/api/admin_user.py deleted file mode 100644 index 1192599b9..000000000 --- a/apps/assets/api/admin_user.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.db.models import Count - -from orgs.mixins.api import OrgBulkModelViewSet -from common.utils import get_logger -from ..models import SystemUser -from .. import serializers -from rbac.permissions import RBACPermission - - -logger = get_logger(__file__) -__all__ = ['AdminUserViewSet'] - - -# 兼容一下老的 api -class AdminUserViewSet(OrgBulkModelViewSet): - """ - Admin user api set, for add,delete,update,list,retrieve resource - """ - model = SystemUser - filterset_fields = ("name", "username") - search_fields = filterset_fields - serializer_class = serializers.AdminUserSerializer - permission_classes = (RBACPermission,) - ordering_fields = ('name',) - ordering = ('name', ) - - def get_queryset(self): - queryset = super().get_queryset().filter(type=SystemUser.Type.admin) - queryset = queryset.annotate(assets_amount=Count('assets')) - return queryset diff --git a/apps/assets/api/asset.py b/apps/assets/api/asset.py index 3f4b7a209..a930bfd41 100644 --- a/apps/assets/api/asset.py +++ b/apps/assets/api/asset.py @@ -42,7 +42,6 @@ class AssetViewSet(SuggestionMixin, FilterAssetByNodeMixin, OrgBulkModelViewSet) filterset_fields = { 'hostname': ['exact'], 'ip': ['exact'], - 'system_users__id': ['exact'], 'platform__base': ['exact'], 'is_active': ['exact'], 'protocols': ['exact', 'icontains'] diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index 14c02aa8d..7ae682839 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -5,23 +5,17 @@ from rest_framework.decorators import action from rest_framework.viewsets import GenericViewSet from common.utils import get_logger, get_object_or_none -from common.permissions import IsValidUser from common.mixins.api import SuggestionMixin from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins import generics -from orgs.utils import tmp_to_root_org from ..models import SystemUser, CommandFilterRule, Account from .. import serializers -from ..tasks import ( - push_system_user_to_assets_manual, test_system_user_connectivity_manual, - push_system_user_to_assets -) logger = get_logger(__file__) __all__ = [ - 'SystemUserViewSet', 'SystemUserAuthInfoApi', 'SystemUserAssetAuthInfoApi', - 'SystemUserCommandFilterRuleListApi', 'SystemUserTaskApi', 'SystemUserAssetsListView', - 'SystemUserTempAuthInfoApi', 'SystemUserAppAuthInfoApi', 'SystemUserAssetAccountApi', + 'SystemUserViewSet', 'SystemUserAuthInfoApi', + 'SystemUserCommandFilterRuleListApi', + 'SystemUserAssetAccountApi', 'SystemUserAssetAccountSecretApi', ] @@ -35,7 +29,6 @@ class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet): 'name': ['exact'], 'username': ['exact'], 'protocol': ['exact', 'in'], - 'type': ['exact', 'in'], } search_fields = filterset_fields serializer_class = serializers.SystemUserSerializer @@ -154,116 +147,6 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView): return Response(status=204) -class SystemUserTempAuthInfoApi(generics.CreateAPIView): - model = SystemUser - permission_classes = (IsValidUser,) - serializer_class = serializers.SystemUserTempAuthSerializer - - def create(self, request, *args, **kwargs): - serializer = super().get_serializer(data=request.data) - serializer.is_valid(raise_exception=True) - - pk = kwargs.get('pk') - data = serializer.validated_data - asset_or_app_id = data.get('instance_id') - - with tmp_to_root_org(): - instance = get_object_or_404(SystemUser, pk=pk) - instance.set_temp_auth(asset_or_app_id, self.request.user.id, data) - return Response(serializer.data, status=201) - - -class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView): - """ - Get system user with asset auth info - """ - model = SystemUser - serializer_class = serializers.SystemUserWithAuthInfoSerializer - - def get_object(self): - instance = super().get_object() - asset_id = self.kwargs.get('asset_id') - user_id = self.request.query_params.get("user_id") - username = self.request.query_params.get("username") - instance.load_asset_more_auth(asset_id, username, user_id) - return instance - - -class SystemUserAppAuthInfoApi(generics.RetrieveAPIView): - """ - Get system user with asset auth info - """ - model = SystemUser - serializer_class = serializers.SystemUserWithAuthInfoSerializer - rbac_perms = { - 'retrieve': 'assets.view_systemusersecret', - } - - def get_object(self): - instance = super().get_object() - app_id = self.kwargs.get('app_id') - user_id = self.request.query_params.get("user_id") - username = self.request.query_params.get("username") - instance.load_app_more_auth(app_id, username, user_id) - return instance - - -class SystemUserTaskApi(generics.CreateAPIView): - serializer_class = serializers.SystemUserTaskSerializer - - def do_push(self, system_user, asset_ids=None): - if asset_ids is None: - task = push_system_user_to_assets_manual.delay(system_user) - else: - username = self.request.query_params.get('username') - task = push_system_user_to_assets.delay( - system_user.id, asset_ids, username=username - ) - return task - - @staticmethod - def do_test(system_user, asset_ids): - task = test_system_user_connectivity_manual.delay(system_user, asset_ids) - return task - - def get_object(self): - pk = self.kwargs.get('pk') - return get_object_or_404(SystemUser, pk=pk) - - def check_permissions(self, request): - action = request.data.get('action') - action_perm_require = { - 'push': 'assets.push_assetsystemuser', - 'test': 'assets.test_assetconnectivity' - } - perm_required = action_perm_require.get(action) - has = self.request.user.has_perm(perm_required) - - if not has: - self.permission_denied(request) - - def perform_create(self, serializer): - action = serializer.validated_data["action"] - asset = serializer.validated_data.get('asset') - - if asset: - assets = [asset] - else: - assets = serializer.validated_data.get('assets') or [] - - asset_ids = [asset.id for asset in assets] - asset_ids = asset_ids if asset_ids else None - - system_user = self.get_object() - if action == 'push': - task = self.do_push(system_user, asset_ids) - else: - task = self.do_test(system_user, asset_ids) - data = getattr(serializer, '_data', {}) - data["task"] = task.id - setattr(serializer, '_data', data) - - class SystemUserCommandFilterRuleListApi(generics.ListAPIView): rbac_perms = { 'list': 'assets.view_commandfilterule' @@ -291,19 +174,3 @@ class SystemUserCommandFilterRuleListApi(generics.ListAPIView): ) return rules - -class SystemUserAssetsListView(generics.ListAPIView): - serializer_class = serializers.AssetSimpleSerializer - filterset_fields = ("hostname", "ip") - search_fields = filterset_fields - rbac_perms = { - 'list': 'assets.view_asset' - } - - def get_object(self): - pk = self.kwargs.get('pk') - return get_object_or_404(SystemUser, pk=pk) - - def get_queryset(self): - system_user = self.get_object() - return system_user.get_all_assets() diff --git a/apps/assets/api/system_user_relation.py b/apps/assets/api/system_user_relation.py deleted file mode 100644 index 36c16a09b..000000000 --- a/apps/assets/api/system_user_relation.py +++ /dev/null @@ -1,138 +0,0 @@ -# -*- coding: utf-8 -*- -# -from collections import defaultdict -from django.db.models import F, Value, Model -from django.db.models.signals import m2m_changed -from django.db.models.functions import Concat - -from common.utils import get_logger -from orgs.mixins.api import OrgBulkModelViewSet -from orgs.utils import current_org -from .. import models, serializers - -__all__ = [ - 'SystemUserAssetRelationViewSet', 'SystemUserNodeRelationViewSet', - 'SystemUserUserRelationViewSet', 'BaseRelationViewSet', -] - -logger = get_logger(__name__) - - -class RelationMixin: - model: Model - - def get_queryset(self): - queryset = self.model.objects.all() - if not current_org.is_root(): - org_id = current_org.org_id() - queryset = queryset.filter(systemuser__org_id=org_id) - - queryset = queryset.annotate(systemuser_display=Concat( - F('systemuser__name'), Value('('), - F('systemuser__username'), Value(')') - )) - return queryset - - def send_post_add_signal(self, instance): - if not isinstance(instance, list): - instance = [instance] - - system_users_objects_map = defaultdict(list) - model, object_field = self.get_objects_attr() - - for i in instance: - _id = getattr(i, object_field).id - system_users_objects_map[i.systemuser].append(_id) - - sender = self.get_sender() - for system_user, object_ids in system_users_objects_map.items(): - logger.debug('System user relation changed, send m2m_changed signals') - m2m_changed.send( - sender=sender, instance=system_user, action='post_add', - reverse=False, model=model, pk_set=set(object_ids) - ) - - def get_sender(self): - return self.model - - def get_objects_attr(self): - return models.Asset, 'asset' - - def perform_create(self, serializer): - instance = serializer.save() - self.send_post_add_signal(instance) - - -class BaseRelationViewSet(RelationMixin, OrgBulkModelViewSet): - perm_model = models.SystemUser - - -class SystemUserAssetRelationViewSet(BaseRelationViewSet): - serializer_class = serializers.SystemUserAssetRelationSerializer - model = models.SystemUser.assets.through - filterset_fields = [ - 'id', 'asset', 'systemuser', - ] - search_fields = [ - "id", "asset__hostname", "asset__ip", - "systemuser__name", "systemuser__username", - ] - - def get_objects_attr(self): - return models.Asset, 'asset' - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate( - asset_display=Concat( - F('asset__hostname'), Value('('), - F('asset__ip'), Value(')') - ) - ) - return queryset - - -class SystemUserNodeRelationViewSet(BaseRelationViewSet): - serializer_class = serializers.SystemUserNodeRelationSerializer - model = models.SystemUser.nodes.through - filterset_fields = [ - 'id', 'node', 'systemuser', - ] - search_fields = [ - "node__value", "systemuser__name", "systemuser__username" - ] - - def get_objects_attr(self): - return models.Node, 'node' - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset \ - .annotate(node_key=F('node__key')) - return queryset - - -class SystemUserUserRelationViewSet(BaseRelationViewSet): - serializer_class = serializers.SystemUserUserRelationSerializer - model = models.SystemUser.users.through - filterset_fields = [ - 'id', 'user', 'systemuser', - ] - search_fields = [ - "user__username", "user__name", - "systemuser__name", "systemuser__username", - ] - - def get_objects_attr(self): - from users.models import User - return User, 'user' - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate( - user_display=Concat( - F('user__name'), Value('('), - F('user__username'), Value(')') - ) - ) - return queryset diff --git a/apps/assets/migrations/0094_alter_systemuser_assets.py b/apps/assets/migrations/0094_alter_systemuser_assets.py.bak similarity index 100% rename from apps/assets/migrations/0094_alter_systemuser_assets.py rename to apps/assets/migrations/0094_alter_systemuser_assets.py.bak diff --git a/apps/assets/migrations/0094_auto_20220728_1125.py b/apps/assets/migrations/0094_auto_20220728_1125.py new file mode 100644 index 000000000..adc477c1c --- /dev/null +++ b/apps/assets/migrations/0094_auto_20220728_1125.py @@ -0,0 +1,89 @@ +# Generated by Django 3.2.14 on 2022-07-28 03:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0093_auto_20220711_1413'), + ] + + operations = [ + migrations.RemoveField( + model_name='cluster', + name='admin_user', + ), + migrations.RemoveField( + model_name='systemuser', + name='ad_domain', + ), + migrations.RemoveField( + model_name='systemuser', + name='assets', + ), + migrations.RemoveField( + model_name='systemuser', + name='auto_push', + ), + migrations.RemoveField( + model_name='systemuser', + name='groups', + ), + migrations.RemoveField( + model_name='systemuser', + name='home', + ), + migrations.RemoveField( + model_name='systemuser', + name='nodes', + ), + migrations.RemoveField( + model_name='systemuser', + name='priority', + ), + migrations.RemoveField( + model_name='systemuser', + name='sftp_root', + ), + migrations.RemoveField( + model_name='systemuser', + name='shell', + ), + migrations.RemoveField( + model_name='systemuser', + name='sudo', + ), + migrations.RemoveField( + model_name='systemuser', + name='system_groups', + ), + migrations.RemoveField( + model_name='systemuser', + name='token', + ), + migrations.RemoveField( + model_name='systemuser', + name='type', + ), + migrations.RemoveField( + model_name='systemuser', + name='users', + ), + migrations.AlterField( + model_name='historicalaccount', + name='version', + field=models.IntegerField(default=0, verbose_name='Version'), + ), + migrations.AlterField( + model_name='systemuser', + name='login_mode', + field=models.CharField(choices=[('auto', '使用账号'), ('manual', 'Manually input')], default='auto', max_length=10, verbose_name='Login mode'), + ), + migrations.DeleteModel( + name='AdminUser', + ), + migrations.DeleteModel( + name='Cluster', + ), + ] diff --git a/apps/assets/migrations/0095_auto_20220713_1746.py b/apps/assets/migrations/0095_auto_20220713_1746.py.bak similarity index 100% rename from apps/assets/migrations/0095_auto_20220713_1746.py rename to apps/assets/migrations/0095_auto_20220713_1746.py.bak diff --git a/apps/assets/migrations/0096_auto_20220714_1627.py b/apps/assets/migrations/0096_auto_20220714_1627.py.bak similarity index 100% rename from apps/assets/migrations/0096_auto_20220714_1627.py rename to apps/assets/migrations/0096_auto_20220714_1627.py.bak diff --git a/apps/assets/models/__init__.py b/apps/assets/models/__init__.py index 9d1df04a1..da35178d1 100644 --- a/apps/assets/models/__init__.py +++ b/apps/assets/models/__init__.py @@ -2,7 +2,6 @@ from .base import * from .asset import * from .label import Label from .user import * -from .cluster import * from .group import * from .domain import * from .node import * @@ -12,5 +11,5 @@ from .utils import * from .authbook import * from .gathered_user import * from .favorite_asset import * -from .backup import * from .account import * +from .backup import * diff --git a/apps/assets/models/cluster.py b/apps/assets/models/cluster.py deleted file mode 100644 index 6c0692ab9..000000000 --- a/apps/assets/models/cluster.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# - -import logging -import uuid - -from django.db import models -from django.utils.translation import ugettext_lazy as _ - - -__all__ = ['Cluster'] -logger = logging.getLogger(__name__) - - -class Cluster(models.Model): - id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=32, verbose_name=_('Name')) - admin_user = models.ForeignKey('assets.AdminUser', null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_("Admin user")) - bandwidth = models.CharField(max_length=32, blank=True, verbose_name=_('Bandwidth')) - contact = models.CharField(max_length=128, blank=True, verbose_name=_('Contact')) - phone = models.CharField(max_length=32, blank=True, verbose_name=_('Phone')) - address = models.CharField(max_length=128, blank=True, verbose_name=_("Address")) - intranet = models.TextField(blank=True, verbose_name=_('Intranet')) - extranet = models.TextField(blank=True, verbose_name=_('Extranet')) - date_created = models.DateTimeField(auto_now_add=True, null=True, verbose_name=_('Date created')) - operator = models.CharField(max_length=32, blank=True, verbose_name=_('Operator')) - created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by')) - comment = models.TextField(blank=True, verbose_name=_('Comment')) - - def __str__(self): - return self.name - - @classmethod - def initial(cls): - return cls.objects.get_or_create(name=_('Default'), created_by=_('System'), comment=_('Default Cluster'))[0] - - class Meta: - ordering = ['name'] - verbose_name = _("Cluster") diff --git a/apps/assets/models/protocol.py b/apps/assets/models/protocol.py new file mode 100644 index 000000000..cd27e7b19 --- /dev/null +++ b/apps/assets/models/protocol.py @@ -0,0 +1,65 @@ +from django.db import models + + +class ProtocolMixin: + protocol: str + + class Protocol(models.TextChoices): + ssh = 'ssh', 'SSH' + rdp = 'rdp', 'RDP' + telnet = 'telnet', 'Telnet' + vnc = 'vnc', 'VNC' + mysql = 'mysql', 'MySQL' + oracle = 'oracle', 'Oracle' + mariadb = 'mariadb', 'MariaDB' + postgresql = 'postgresql', 'PostgreSQL' + sqlserver = 'sqlserver', 'SQLServer' + redis = 'redis', 'Redis' + mongodb = 'mongodb', 'MongoDB' + k8s = 'k8s', 'K8S' + + SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp] + + ASSET_CATEGORY_PROTOCOLS = [ + Protocol.ssh, Protocol.rdp, Protocol.telnet, Protocol.vnc + ] + APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS = [ + Protocol.rdp + ] + APPLICATION_CATEGORY_DB_PROTOCOLS = [ + Protocol.mysql, Protocol.mariadb, Protocol.oracle, + Protocol.postgresql, Protocol.sqlserver, + Protocol.redis, Protocol.mongodb + ] + APPLICATION_CATEGORY_CLOUD_PROTOCOLS = [ + Protocol.k8s + ] + APPLICATION_CATEGORY_PROTOCOLS = [ + *APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS, + *APPLICATION_CATEGORY_DB_PROTOCOLS, + *APPLICATION_CATEGORY_CLOUD_PROTOCOLS + ] + + @property + def is_protocol_support_push(self): + return self.protocol in self.SUPPORT_PUSH_PROTOCOLS + + @classmethod + def get_protocol_by_application_type(cls, app_type): + from applications.const import AppType + if app_type in cls.APPLICATION_CATEGORY_PROTOCOLS: + protocol = app_type + elif app_type in AppType.remote_app_types(): + protocol = cls.Protocol.rdp + else: + protocol = None + return protocol + + @property + def can_perm_to_asset(self): + return self.protocol in self.ASSET_CATEGORY_PROTOCOLS + + @property + def is_asset_protocol(self): + return self.protocol in self.ASSET_CATEGORY_PROTOCOLS + diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 9770b2266..a5be417a2 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -6,83 +6,17 @@ import logging from django.db import models from django.utils.translation import ugettext_lazy as _ -from django.core.validators import MinValueValidator, MaxValueValidator from django.shortcuts import get_object_or_404 from django.core.cache import cache -from common.utils import signer -from users.models import User from .base import BaseUser -from .asset import Asset +from .protocol import ProtocolMixin -__all__ = ['AdminUser', 'SystemUser', 'ProtocolMixin'] +__all__ = ['SystemUser'] logger = logging.getLogger(__name__) -class ProtocolMixin: - protocol: str - - class Protocol(models.TextChoices): - ssh = 'ssh', 'SSH' - rdp = 'rdp', 'RDP' - telnet = 'telnet', 'Telnet' - vnc = 'vnc', 'VNC' - mysql = 'mysql', 'MySQL' - oracle = 'oracle', 'Oracle' - mariadb = 'mariadb', 'MariaDB' - postgresql = 'postgresql', 'PostgreSQL' - sqlserver = 'sqlserver', 'SQLServer' - redis = 'redis', 'Redis' - mongodb = 'mongodb', 'MongoDB' - k8s = 'k8s', 'K8S' - - SUPPORT_PUSH_PROTOCOLS = [Protocol.ssh, Protocol.rdp] - - ASSET_CATEGORY_PROTOCOLS = [ - Protocol.ssh, Protocol.rdp, Protocol.telnet, Protocol.vnc - ] - APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS = [ - Protocol.rdp - ] - APPLICATION_CATEGORY_DB_PROTOCOLS = [ - Protocol.mysql, Protocol.mariadb, Protocol.oracle, - Protocol.postgresql, Protocol.sqlserver, - Protocol.redis, Protocol.mongodb - ] - APPLICATION_CATEGORY_CLOUD_PROTOCOLS = [ - Protocol.k8s - ] - APPLICATION_CATEGORY_PROTOCOLS = [ - *APPLICATION_CATEGORY_REMOTE_APP_PROTOCOLS, - *APPLICATION_CATEGORY_DB_PROTOCOLS, - *APPLICATION_CATEGORY_CLOUD_PROTOCOLS - ] - - @property - def is_protocol_support_push(self): - return self.protocol in self.SUPPORT_PUSH_PROTOCOLS - - @classmethod - def get_protocol_by_application_type(cls, app_type): - from applications.const import AppType - if app_type in cls.APPLICATION_CATEGORY_PROTOCOLS: - protocol = app_type - elif app_type in AppType.remote_app_types(): - protocol = cls.Protocol.rdp - else: - protocol = None - return protocol - - @property - def can_perm_to_asset(self): - return self.protocol in self.ASSET_CATEGORY_PROTOCOLS - - @property - def is_asset_protocol(self): - return self.protocol in self.ASSET_CATEGORY_PROTOCOLS - - class SystemUser(ProtocolMixin, BaseUser): LOGIN_AUTO = 'auto' LOGIN_MANUAL = 'manual' @@ -91,39 +25,10 @@ class SystemUser(ProtocolMixin, BaseUser): (LOGIN_MANUAL, _('Manually input')) ) - class Type(models.TextChoices): - common = 'common', _('Common user') - admin = 'admin', _('Admin user') - username_same_with_user = models.BooleanField(default=False, verbose_name=_("Username same with user")) - nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes")) - assets = models.ManyToManyField( - 'assets.Asset', blank=True, verbose_name=_("Assets"), - related_name='system_users' - ) - users = models.ManyToManyField('users.User', blank=True, verbose_name=_("Users")) - groups = models.ManyToManyField('users.UserGroup', blank=True, verbose_name=_("User groups")) - 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)] - ) protocol = models.CharField(max_length=16, choices=ProtocolMixin.Protocol.choices, default='ssh', verbose_name=_('Protocol')) login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode')) - # Todo: 重构平台后或许这里也得变化 - # 账号模版 - account_template_enabled = 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')) - shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) - sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root")) - token = models.TextField(default='', verbose_name=_('Token')) - home = models.CharField(max_length=4096, default='', verbose_name=_('Home'), blank=True) - system_groups = models.CharField(default='', max_length=4096, verbose_name=_('System groups'), blank=True) - ad_domain = models.CharField(default='', max_length=256) - # linux su 命令 (switch user) # Todo: 修改为 username, 不必系统用户了 su_enabled = models.BooleanField(default=False, verbose_name=_('User switch')) @@ -135,32 +40,6 @@ class SystemUser(ProtocolMixin, BaseUser): username = '*' return '{0.name}({1})'.format(self, username) - @property - def nodes_amount(self): - return self.nodes.all().count() - - @property - def login_mode_display(self): - return self.get_login_mode_display() - - def is_need_push(self): - if self.auto_push_account and self.is_protocol_support_push: - return True - else: - return False - - @property - def is_admin_user(self): - return self.type == self.Type.admin - - @property - def is_need_cmd_filter(self): - return self.protocol not in [self.Protocol.rdp, self.Protocol.vnc] - - @property - def is_need_test_asset_connective(self): - return self.protocol in self.ASSET_CATEGORY_PROTOCOLS - @property def cmd_filter_rules(self): from .cmd_filter import CommandFilterRule @@ -178,30 +57,6 @@ class SystemUser(ProtocolMixin, BaseUser): return False, matched_cmd return True, None - def get_all_assets(self): - from assets.models import Node - nodes_keys = self.nodes.all().values_list('key', flat=True) - asset_ids = set(self.assets.all().values_list('id', flat=True)) - nodes_asset_ids = Node.get_nodes_all_asset_ids_by_keys(nodes_keys) - asset_ids.update(nodes_asset_ids) - assets = Asset.objects.filter(id__in=asset_ids) - return assets - - def add_related_assets(self, assets_or_ids): - self.assets.add(*tuple(assets_or_ids)) - self.add_related_assets_to_su_from_if_need(assets_or_ids) - - def add_related_assets_to_su_from_if_need(self, assets_or_ids): - if self.protocol not in [self.Protocol.ssh.value]: - return - if not self.su_enabled: - return - if not self.su_from: - return - if self.su_from.protocol != self.protocol: - return - self.su_from.assets.add(*tuple(assets_or_ids)) - @classmethod def create_accounts_with_assets(cls, asset_ids, system_user_ids): pass @@ -216,6 +71,7 @@ class SystemUser(ProtocolMixin, BaseUser): def get_auto_account(self, user_id, asset_id): from .account import Account + from users.models import User username = self.username if self.username_same_with_user: user = get_object_or_404(User, id=user_id) @@ -235,52 +91,3 @@ class SystemUser(ProtocolMixin, BaseUser): permissions = [ ('match_systemuser', _('Can match system user')), ] - - -# Deprecated: 准备废弃 -class AdminUser(BaseUser): - """ - A privileged user that ansible can use it to push system user and so on - """ - BECOME_METHOD_CHOICES = ( - ('sudo', 'sudo'), - ('su', 'su'), - ) - become = models.BooleanField(default=True) - become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4) - become_user = models.CharField(default='root', max_length=64) - _become_pass = models.CharField(default='', blank=True, max_length=128) - CONNECTIVITY_CACHE_KEY = '_ADMIN_USER_CONNECTIVE_{}' - _prefer = "admin_user" - - def __str__(self): - return self.name - - @property - def become_pass(self): - password = signer.unsign(self._become_pass) - if password: - return password - else: - return "" - - @become_pass.setter - def become_pass(self, password): - self._become_pass = signer.sign(password) - - @property - def become_info(self): - if self.become: - info = { - "method": self.become_method, - "user": self.become_user, - "pass": self.become_pass, - } - else: - info = None - return info - - class Meta: - ordering = ['name'] - unique_together = [('name', 'org_id')] - verbose_name = _("Admin user") diff --git a/apps/assets/serializers/__init__.py b/apps/assets/serializers/__init__.py index 3f1222fc5..5c684652c 100644 --- a/apps/assets/serializers/__init__.py +++ b/apps/assets/serializers/__init__.py @@ -2,7 +2,6 @@ # from .asset import * -from .admin_user import * from .label import * from .system_user import * from .node import * diff --git a/apps/assets/serializers/admin_user.py b/apps/assets/serializers/admin_user.py deleted file mode 100644 index b6ab18af3..000000000 --- a/apps/assets/serializers/admin_user.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# -from ..models import SystemUser -from .system_user import SystemUserSerializer as SuS - - -class AdminUserSerializer(SuS): - """ - 管理用户 - """ - - class Meta(SuS.Meta): - fields = SuS.Meta.fields_mini + \ - SuS.Meta.fields_write_only + \ - SuS.Meta.fields_m2m + \ - [ - 'type', 'protocol', "priority", 'sftp_root', 'ssh_key_fingerprint', - 'su_enabled', 'su_from', - 'date_created', 'date_updated', 'comment', 'created_by', - ] - - def validate_type(self, val): - return SystemUser.Type.admin - - def validate_protocol(self, val): - return 'ssh' diff --git a/apps/assets/serializers/asset.py b/apps/assets/serializers/asset.py index da1a0a0fc..40e845e15 100644 --- a/apps/assets/serializers/asset.py +++ b/apps/assets/serializers/asset.py @@ -91,7 +91,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer): 'cpu_info', 'hardware_info', ] fields_fk = [ - 'domain', 'domain_display', 'platform', 'admin_user', 'admin_user_display' + 'domain', 'domain_display', 'platform', ] fields_m2m = [ 'nodes', 'nodes_display', 'labels', 'labels_display', 'accounts' @@ -114,19 +114,10 @@ class AssetSerializer(BulkOrgResourceModelSerializer): self.accounts_data = data.pop('accounts', []) super().__init__(*args, **kwargs) - def get_fields(self): - fields = super().get_fields() - - admin_user_field = fields.get('admin_user') - # 因为 mixin 中对 fields 有处理,可能不需要返回 admin_user - if admin_user_field: - admin_user_field.queryset = SystemUser.objects.filter(type=SystemUser.Type.admin) - return fields - @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.prefetch_related('domain', 'platform', 'admin_user') + queryset = queryset.prefetch_related('domain', 'platform') queryset = queryset.prefetch_related('nodes', 'labels') return queryset @@ -162,7 +153,6 @@ class AssetSerializer(BulkOrgResourceModelSerializer): 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) diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 7d15041ec..dc1133e73 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -1,97 +1,38 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ -from django.db.models import Count -from common.mixins.serializers import BulkSerializerMixin -from common.utils import ssh_pubkey_gen -from common.drf.fields import EncryptedField -from common.drf.serializers import SecretReadableMixin from common.validators import alphanumeric_re, alphanumeric_cn_re, alphanumeric_win_re from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from ..models import SystemUser, Asset -from .utils import validate_password_for_ansible -from .base import AuthSerializerMixin +from ..models import SystemUser __all__ = [ 'SystemUserSerializer', 'MiniSystemUserSerializer', - 'SystemUserSimpleSerializer', 'SystemUserAssetRelationSerializer', - 'SystemUserNodeRelationSerializer', 'SystemUserTaskSerializer', - 'SystemUserUserRelationSerializer', 'SystemUserWithAuthInfoSerializer', - 'SystemUserTempAuthSerializer', 'RelationMixin', + 'SystemUserSimpleSerializer', ] -class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): +class SystemUserSerializer(BulkOrgResourceModelSerializer): """ 系统用户 """ - password = EncryptedField( - label=_('Password'), required=False, allow_blank=True, allow_null=True, max_length=1024, - trim_whitespace=False, validators=[validate_password_for_ansible], - write_only=True - ) - auto_generate_key = serializers.BooleanField(initial=True, required=False, write_only=True) - type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display')) - ssh_key_fingerprint = serializers.ReadOnlyField(label=_('SSH key fingerprint')) - token = EncryptedField( - label=_('Token'), required=False, write_only=True, style={'base_template': 'textarea.html'} - ) - applications_amount = serializers.IntegerField( - source='apps_amount', read_only=True, label=_('Apps amount') - ) class Meta: model = SystemUser - fields_mini = ['id', 'name', 'username'] - fields_write_only = ['password', 'public_key', 'private_key', 'passphrase'] - fields_small = fields_mini + fields_write_only + [ - 'token', 'ssh_key_fingerprint', - 'type', 'type_display', 'protocol', 'is_asset_protocol', - 'account_template_enabled', 'login_mode', 'login_mode_display', 'priority', - 'sudo', 'shell', 'sftp_root', 'home', 'system_groups', 'ad_domain', - 'username_same_with_user', 'auto_push_account', 'auto_generate_key', - 'su_enabled', 'su_from', - 'date_created', 'date_updated', 'comment', 'created_by', + fields_mini = ['id', 'name', 'username', 'protocol'] + fields_small = fields_mini + [ + 'login_mode', 'su_enabled', 'su_from', + 'date_created', 'date_updated', 'comment', + 'created_by', ] - fields_m2m = ['cmd_filters', 'assets_amount', 'applications_amount', 'nodes'] - fields = fields_small + fields_m2m + fields = fields_small extra_kwargs = { 'cmd_filters': {"required": False, 'label': _('Command filter')}, - 'public_key': {"write_only": True}, - 'private_key': {"write_only": True}, - 'nodes_amount': {'label': _('Nodes amount')}, - 'assets_amount': {'label': _('Assets amount')}, 'login_mode_display': {'label': _('Login mode display')}, 'created_by': {'read_only': True}, 'ad_domain': {'required': False, 'allow_blank': True, 'label': _('Ad domain')}, - 'is_asset_protocol': {'label': _('Is asset protocol')}, 'su_from': {'help_text': _('Only ssh and automatic login system users are supported')} } - def validate_auto_push(self, value): - login_mode = self.get_initial_value("login_mode") - protocol = self.get_initial_value("protocol") - - if login_mode == SystemUser.LOGIN_MANUAL: - value = False - elif protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS: - value = False - return value - - def validate_auto_generate_key(self, value): - login_mode = self.get_initial_value("login_mode") - protocol = self.get_initial_value("protocol") - - if self.context["request"].method.lower() != "post": - value = False - elif self.instance: - value = False - elif login_mode == SystemUser.LOGIN_MANUAL: - value = False - elif protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS: - value = False - return value - def validate_username_same_with_user(self, username_same_with_user): if not username_same_with_user: return username_same_with_user @@ -132,12 +73,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): raise serializers.ValidationError(msg) return username - def validate_home(self, home): - username_same_with_user = self.get_initial_value("username_same_with_user") - if username_same_with_user: - return '' - return home - @staticmethod def validate_sftp_root(value): if value in ['home', 'tmp']: @@ -147,17 +82,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): raise serializers.ValidationError(error) return value - def validate_password(self, password): - super().validate_password(password) - auto_gen_key = self.get_initial_value('auto_generate_key', False) - private_key = self.get_initial_value('private_key') - login_mode = self.get_initial_value('login_mode') - - if not self.instance and not auto_gen_key and not password and \ - not private_key and login_mode == SystemUser.LOGIN_AUTO: - raise serializers.ValidationError(_("Password or private key required")) - return password - def validate_su_from(self, su_from: SystemUser): # self: su enabled su_enabled = self.get_initial_value('su_enabled', default=False) @@ -181,70 +105,6 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer): raise serializers.ValidationError(error) return su_from - def _validate_admin_user(self, attrs): - if self.instance: - tp = self.instance.type - else: - tp = attrs.get('type') - if tp != SystemUser.Type.admin: - return attrs - attrs['protocol'] = SystemUser.Protocol.ssh - attrs['login_mode'] = SystemUser.LOGIN_AUTO - attrs['username_same_with_user'] = False - attrs['auto_push_account'] = False - return attrs - - def _validate_gen_key(self, attrs): - username = attrs.get('username', 'manual') - auto_gen_key = attrs.pop('auto_generate_key', False) - protocol = attrs.get('protocol') - - if protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS: - return attrs - - # 自动生成 - if auto_gen_key and not self.instance: - password = SystemUser.gen_password() - attrs['password'] = password - if protocol == SystemUser.Protocol.ssh: - private_key, public_key = SystemUser.gen_key(username) - attrs['private_key'] = private_key - attrs['public_key'] = public_key - # 如果设置了private key,没有设置public key则生成 - elif attrs.get('private_key'): - private_key = attrs['private_key'] - password = attrs.get('password') - public_key = ssh_pubkey_gen(private_key, password=password, username=username) - attrs['public_key'] = public_key - return attrs - - def _validate_login_mode(self, attrs): - if 'login_mode' in attrs: - login_mode = attrs['login_mode'] - else: - login_mode = self.instance.login_mode if self.instance else SystemUser.LOGIN_AUTO - - if login_mode == SystemUser.LOGIN_MANUAL: - attrs['password'] = '' - attrs['private_key'] = '' - attrs['public_key'] = '' - - return attrs - - def validate(self, attrs): - attrs = self._validate_admin_user(attrs) - attrs = self._validate_gen_key(attrs) - attrs = self._validate_login_mode(attrs) - return attrs - - @classmethod - def setup_eager_loading(cls, queryset): - """ Perform necessary eager loading of data. """ - queryset = queryset \ - .annotate(assets_amount=Count("assets")) \ - .prefetch_related('nodes', 'cmd_filters') - return queryset - class MiniSystemUserSerializer(serializers.ModelSerializer): class Meta: @@ -252,28 +112,6 @@ class MiniSystemUserSerializer(serializers.ModelSerializer): fields = SystemUserSerializer.Meta.fields_mini -class SystemUserWithAuthInfoSerializer(SecretReadableMixin, SystemUserSerializer): - class Meta(SystemUserSerializer.Meta): - fields_mini = ['id', 'name', 'username'] - fields_write_only = ['password', 'public_key', 'private_key'] - fields_small = fields_mini + fields_write_only + [ - 'protocol', 'login_mode', 'login_mode_display', 'priority', - 'sudo', 'shell', 'ad_domain', 'sftp_root', 'token', - "username_same_with_user", 'auto_push_account', 'auto_generate_key', - 'comment', - ] - fields = fields_small - extra_kwargs = { - 'nodes_amount': {'label': _('Node')}, - 'assets_amount': {'label': _('Asset')}, - 'login_mode_display': {'label': _('Login mode display')}, - 'created_by': {'read_only': True}, - 'password': {'write_only': False}, - 'private_key': {'write_only': False}, - 'token': {'write_only': False} - } - - class SystemUserSimpleSerializer(serializers.ModelSerializer): """ 系统用户最基本信息的数据结构 @@ -284,70 +122,6 @@ class SystemUserSimpleSerializer(serializers.ModelSerializer): fields = ('id', 'name', 'username') -class RelationMixin(BulkSerializerMixin, serializers.Serializer): - systemuser_display = serializers.ReadOnlyField(label=_("System user name")) - org_name = serializers.ReadOnlyField(label=_("Org name")) - - def get_field_names(self, declared_fields, info): - fields = super().get_field_names(declared_fields, info) - fields.extend(['systemuser', "systemuser_display", "org_name"]) - return fields - - -class SystemUserAssetRelationSerializer(RelationMixin, serializers.ModelSerializer): - asset_display = serializers.ReadOnlyField(label=_('Asset hostname')) - - class Meta: - model = SystemUser.assets.through - fields = [ - "id", "asset", "asset_display", - "systemuser", "systemuser_display", - ] - use_model_bulk_create = True - model_bulk_create_kwargs = { - 'ignore_conflicts': True - } - - -class SystemUserNodeRelationSerializer(RelationMixin, serializers.ModelSerializer): - node_display = serializers.SerializerMethodField() - - class Meta: - model = SystemUser.nodes.through - fields = [ - 'id', 'node', "node_display", - ] - - def get_node_display(self, obj): - return obj.node.full_value - - -class SystemUserUserRelationSerializer(RelationMixin, serializers.ModelSerializer): - user_display = serializers.ReadOnlyField() - - class Meta: - model = SystemUser.users.through - fields = [ - 'id', "user", "user_display", - ] - - -class SystemUserTaskSerializer(serializers.Serializer): - ACTION_CHOICES = ( - ("test", "test"), - ("push", "push"), - ) - action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True) - asset = serializers.PrimaryKeyRelatedField( - queryset=Asset.objects, allow_null=True, required=False, write_only=True - ) - assets = serializers.PrimaryKeyRelatedField( - queryset=Asset.objects, allow_null=True, required=False, write_only=True, - many=True - ) - task = serializers.CharField(read_only=True) - - class SystemUserTempAuthSerializer(SystemUserSerializer): instance_id = serializers.CharField() diff --git a/apps/assets/signal_handlers/__init__.py b/apps/assets/signal_handlers/__init__.py index 8a895544f..b337df001 100644 --- a/apps/assets/signal_handlers/__init__.py +++ b/apps/assets/signal_handlers/__init__.py @@ -1,5 +1,4 @@ from .asset import * -from .system_user import * -from .authbook import * +from .account import * from .node_assets_amount import * from .node_assets_mapping import * diff --git a/apps/assets/signal_handlers/authbook.py b/apps/assets/signal_handlers/account.py similarity index 100% rename from apps/assets/signal_handlers/authbook.py rename to apps/assets/signal_handlers/account.py diff --git a/apps/assets/signal_handlers/common.py b/apps/assets/signal_handlers/common.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/assets/signal_handlers/system_user.py b/apps/assets/signal_handlers/system_user.py.bak similarity index 100% rename from apps/assets/signal_handlers/system_user.py rename to apps/assets/signal_handlers/system_user.py.bak diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 1c405a82a..f3f192a3d 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -17,7 +17,6 @@ router.register(r'accounts-history', api.AccountHistoryViewSet, 'account-history router.register(r'account-history-secrets', api.AccountHistorySecretsViewSet, 'account-history-secret') router.register(r'platforms', api.AssetPlatformViewSet, 'platform') router.register(r'system-users', api.SystemUserViewSet, 'system-user') -router.register(r'admin-users', api.AdminUserViewSet, 'admin-user') router.register(r'labels', api.LabelViewSet, 'label') router.register(r'nodes', api.NodeViewSet, 'node') router.register(r'domains', api.DomainViewSet, 'domain') @@ -25,9 +24,6 @@ router.register(r'gateways', api.GatewayViewSet, 'gateway') router.register(r'cmd-filters', api.CommandFilterViewSet, 'cmd-filter') router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user') router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset') -router.register(r'system-users-assets-relations', api.SystemUserAssetRelationViewSet, 'system-users-assets-relation') -router.register(r'system-users-nodes-relations', api.SystemUserNodeRelationViewSet, 'system-users-nodes-relation') -router.register(r'system-users-users-relations', api.SystemUserUserRelationViewSet, 'system-users-users-relation') router.register(r'account-backup-plans', api.AccountBackupPlanViewSet, 'account-backup') router.register(r'account-backup-plan-executions', api.AccountBackupPlanExecutionViewSet, 'account-backup-execution') @@ -45,11 +41,8 @@ urlpatterns = [ path('assets//perm-user-groups/', api.AssetPermUserGroupListApi.as_view(), name='asset-perm-user-group-list'), path('assets//perm-user-groups//permissions/', api.AssetPermUserGroupPermissionsListApi.as_view(), name='asset-perm-user-group-permission-list'), - path('system-users//assets/', api.SystemUserAssetsListView.as_view(), name='system-user-assets'), - path('system-users//applications//auth-info/', api.SystemUserAppAuthInfoApi.as_view(), name='system-user-app-auth-info'), path('system-users//assets//users//account/', api.SystemUserAssetAccountApi.as_view(), name='system-user-asset-account'), path('system-users//assets//users//account-secret/', api.SystemUserAssetAccountSecretApi.as_view(), name='system-user-asset-account-secret'), - path('system-users//tasks/', api.SystemUserTaskApi.as_view(), name='system-user-task-create'), path('system-users//cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='system-user-cmd-filter-rule-list'), path('cmd-filter-rules/', api.SystemUserCommandFilterRuleListApi.as_view(), name='cmd-filter-rules'), diff --git a/apps/audits/signal_handlers.py b/apps/audits/signal_handlers.py index 9ac565bc6..af6959f48 100644 --- a/apps/audits/signal_handlers.py +++ b/apps/audits/signal_handlers.py @@ -69,11 +69,6 @@ M2M_NEED_RECORD = { _('{User} JOINED {UserGroup}'), _('{User} LEFT {UserGroup}') ), - SystemUser.assets.through._meta.object_name: ( - _('Asset and SystemUser'), - _('{Asset} ADD {SystemUser}'), - _('{Asset} REMOVE {SystemUser}') - ), Asset.nodes.through._meta.object_name: ( _('Node and Asset'), _('{Node} ADD {Asset}'), @@ -99,11 +94,6 @@ M2M_NEED_RECORD = { _('{AssetPermission} ADD {Node}'), _('{AssetPermission} REMOVE {Node}'), ), - AssetPermission.system_users.through._meta.object_name: ( - _('Asset permission and SystemUser'), - _('{AssetPermission} ADD {SystemUser}'), - _('{AssetPermission} REMOVE {SystemUser}'), - ), ApplicationPermission.users.through._meta.object_name: ( _('User application permissions'), _('{ApplicationPermission} ADD {User}'), diff --git a/apps/perms/api/application/user_permission/common.py b/apps/perms/api/application/user_permission/common.py index 34fcc4dd1..d4c68662a 100644 --- a/apps/perms/api/application/user_permission/common.py +++ b/apps/perms/api/application/user_permission/common.py @@ -62,20 +62,19 @@ class ValidateUserApplicationPermissionApi(APIView): def get(self, request, *args, **kwargs): user_id = request.query_params.get('user_id', '') application_id = request.query_params.get('application_id', '') - system_user_id = request.query_params.get('system_user_id', '') + account = system_user_id = request.query_params.get('account', '') data = { 'has_permission': False, 'expire_at': int(time.time()), 'actions': [] } - if not all((user_id, application_id, system_user_id)): + if not all((user_id, application_id, account)): return Response(data) user = User.objects.get(id=user_id) application = Application.objects.get(id=application_id) - system_user = SystemUser.objects.get(id=system_user_id) - has_perm, actions, expire_at = validate_permission(user, application, system_user) + has_perm, actions, expire_at = validate_permission(user, application, account) status_code = status.HTTP_200_OK if has_perm else status.HTTP_403_FORBIDDEN data = { 'has_permission': has_perm, diff --git a/apps/perms/api/asset/asset_permission_relation.py b/apps/perms/api/asset/asset_permission_relation.py index b0a67f858..16469695b 100644 --- a/apps/perms/api/asset/asset_permission_relation.py +++ b/apps/perms/api/asset/asset_permission_relation.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- # from rest_framework import generics -from django.db.models import F, Value -from django.db.models.functions import Concat +from django.db.models import F from django.shortcuts import get_object_or_404 from orgs.mixins.api import OrgRelationMixin @@ -15,8 +14,7 @@ from perms.utils.asset.user_permission import UserGrantedAssetsQueryUtils __all__ = [ 'AssetPermissionUserRelationViewSet', 'AssetPermissionUserGroupRelationViewSet', 'AssetPermissionAssetRelationViewSet', 'AssetPermissionNodeRelationViewSet', - 'AssetPermissionSystemUserRelationViewSet', 'AssetPermissionAllAssetListApi', - 'AssetPermissionAllUserListApi', + 'AssetPermissionAllAssetListApi', 'AssetPermissionAllUserListApi', ] @@ -117,21 +115,3 @@ class AssetPermissionNodeRelationViewSet(RelationMixin): .annotate(node_key=F('node__key')) return queryset - -class AssetPermissionSystemUserRelationViewSet(RelationMixin): - serializer_class = serializers.AssetPermissionSystemUserRelationSerializer - m2m_field = models.AssetPermission.system_users.field - filterset_fields = [ - 'id', 'systemuser', 'assetpermission', - ] - search_fields = [ - "assetpermission__name", "systemuser__name", "systemuser__username" - ] - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate( - systemuser_display=Concat( - F('systemuser__name'), Value('('), F('systemuser__username'), Value(')') - )) - return queryset diff --git a/apps/perms/migrations/0029_auto_20220728_1728.py b/apps/perms/migrations/0029_auto_20220728_1728.py new file mode 100644 index 000000000..6f64dfbbd --- /dev/null +++ b/apps/perms/migrations/0029_auto_20220728_1728.py @@ -0,0 +1,28 @@ +# Generated by Django 3.2.14 on 2022-07-28 09:10 + +from django.db import migrations, models + + +def migrate_system_user_to_accounts(apps, schema_editor): + # Todo: 迁移 系统用户为账号 + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('perms', '0028_auto_20220316_2028'), + ] + + operations = [ + migrations.AddField( + model_name='assetpermission', + name='accounts', + field=models.JSONField(default=list, verbose_name='Accounts'), + ), + migrations.RunPython(migrate_system_user_to_accounts), + migrations.RemoveField( + model_name='assetpermission', + name='system_users', + ), + ] diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py index ea795d889..b944e84e3 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -22,14 +22,13 @@ logger = logging.getLogger(__name__) class AssetPermission(BasePermission): assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset")) nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes")) - system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', blank=True, verbose_name=_("System user")) + accounts = models.JSONField(default=list, verbose_name=_("Accounts")) class Meta: unique_together = [('org_id', 'name')] verbose_name = _("Asset permission") ordering = ('name',) - permissions = [ - ] + permissions = [] @lazyproperty def users_amount(self): @@ -47,10 +46,6 @@ class AssetPermission(BasePermission): def nodes_amount(self): return self.nodes.count() - @lazyproperty - def system_users_amount(self): - return self.system_users.count() - @classmethod def get_queryset_with_prefetch(cls): return cls.objects.all().valid().prefetch_related( @@ -80,10 +75,6 @@ class AssetPermission(BasePermission): names = [asset.hostname for asset in self.assets.all()] return names - def system_users_display(self): - names = [system_user.name for system_user in self.system_users.all()] - return names - def nodes_display(self): names = [node.full_value for node in self.nodes.all()] return names diff --git a/apps/perms/serializers/asset/permission.py b/apps/perms/serializers/asset/permission.py index cd6c24723..9476dfaa3 100644 --- a/apps/perms/serializers/asset/permission.py +++ b/apps/perms/serializers/asset/permission.py @@ -22,7 +22,6 @@ class AssetPermissionSerializer(BasePermissionSerializer): user_groups_display = serializers.ListField(child=serializers.CharField(), label=_('User groups display'), required=False) assets_display = serializers.ListField(child=serializers.CharField(), label=_('Assets display'), required=False) nodes_display = serializers.ListField(child=serializers.CharField(), label=_('Nodes display'), required=False) - system_users_display = serializers.ListField(child=serializers.CharField(), label=_('System users display'), required=False) class Meta: model = AssetPermission @@ -34,9 +33,9 @@ class AssetPermissionSerializer(BasePermissionSerializer): ] fields_m2m = [ 'users', 'users_display', 'user_groups', 'user_groups_display', 'assets', - 'assets_display', 'nodes', 'nodes_display', 'system_users', 'system_users_display', + 'assets_display', 'nodes', 'nodes_display', 'accounts', 'users_amount', 'user_groups_amount', 'assets_amount', - 'nodes_amount', 'system_users_amount', + 'nodes_amount', ] fields = fields_small + fields_m2m read_only_fields = ['created_by', 'date_created', 'from_ticket'] @@ -48,30 +47,16 @@ class AssetPermissionSerializer(BasePermissionSerializer): 'user_groups_amount': {'label': _('User groups amount')}, 'assets_amount': {'label': _('Assets amount')}, 'nodes_amount': {'label': _('Nodes amount')}, - 'system_users_amount': {'label': _('System users amount')}, } @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( - 'users', 'user_groups', 'assets', 'nodes', 'system_users' + 'users', 'user_groups', 'assets', 'nodes', ) return queryset - def to_internal_value(self, data): - if 'system_users_display' in data: - # system_users_display 转化为 system_users - system_users = data.get('system_users', []) - system_users_display = data.pop('system_users_display') - - for name in system_users_display: - system_user = SystemUser.objects.filter(name=name).first() - if system_user and system_user.id not in system_users: - system_users.append(system_user.id) - data['system_users'] = system_users - return super().to_internal_value(data) - @staticmethod def perform_display_create(instance, **kwargs): # 用户 diff --git a/apps/perms/serializers/asset/permission_relation.py b/apps/perms/serializers/asset/permission_relation.py index ee1e05112..3bd5e8b1d 100644 --- a/apps/perms/serializers/asset/permission_relation.py +++ b/apps/perms/serializers/asset/permission_relation.py @@ -12,7 +12,6 @@ __all__ = [ 'AssetPermissionUserGroupRelationSerializer', "AssetPermissionAssetRelationSerializer", 'AssetPermissionNodeRelationSerializer', - 'AssetPermissionSystemUserRelationSerializer', 'AssetPermissionAllAssetSerializer', 'AssetPermissionAllUserSerializer', ] @@ -99,13 +98,3 @@ class AssetPermissionNodeRelationSerializer(RelationMixin, serializers.ModelSeri fields = [ 'id', 'node', "node_display", ] - - -class AssetPermissionSystemUserRelationSerializer(RelationMixin, serializers.ModelSerializer): - systemuser_display = serializers.ReadOnlyField() - - class Meta: - model = AssetPermission.system_users.through - fields = [ - 'id', 'systemuser', 'systemuser_display' - ] diff --git a/apps/perms/signal_handlers/asset_permission.py b/apps/perms/signal_handlers/asset_permission.py index e889c318a..68503b6df 100644 --- a/apps/perms/signal_handlers/asset_permission.py +++ b/apps/perms/signal_handlers/asset_permission.py @@ -1,138 +1,10 @@ # -*- coding: utf-8 -*- # -from django.db.models.signals import m2m_changed -from django.dispatch import receiver - -from users.models import User -from assets.models import SystemUser -from common.utils import get_logger -from common.decorator import on_transaction_commit -from common.exceptions import M2MReverseNotAllowed -from common.const.signals import POST_ADD -from perms.models import AssetPermission - - -logger = get_logger(__file__) - - -@receiver(m2m_changed, sender=User.groups.through) -@on_transaction_commit -def on_user_groups_change(sender, instance, action, reverse, pk_set, **kwargs): - """ - UserGroup 增加 User 时,增加的 User 需要与 UserGroup 关联的动态系统用户相关联 - """ - user: User - - if action != POST_ADD: - return - - if not reverse: - # 一个用户添加了多个用户组 - user_ids = [instance.id] - system_users = SystemUser.objects.filter(groups__id__in=pk_set).distinct() - else: - # 一个用户组添加了多个用户 - user_ids = pk_set - system_users = SystemUser.objects.filter(groups__id=instance.pk).distinct() - - for system_user in system_users: - system_user.users.add(*user_ids) - - -@receiver(m2m_changed, sender=AssetPermission.nodes.through) -@on_transaction_commit -def on_permission_nodes_changed(instance, action, reverse, pk_set, model, **kwargs): - if reverse: - raise M2MReverseNotAllowed - if action != POST_ADD: - return - - logger.debug("Asset permission nodes change signal received") - nodes = model.objects.filter(pk__in=pk_set) - system_users = instance.system_users.all() - - # TODO 待优化 - for system_user in system_users: - system_user.nodes.add(*nodes) - - -@receiver(m2m_changed, sender=AssetPermission.assets.through) -@on_transaction_commit -def on_permission_assets_changed(instance, action, reverse, pk_set, model, **kwargs): - if reverse: - raise M2MReverseNotAllowed - if action != POST_ADD: - return - - logger.debug("Asset permission assets change signal received") - assets = model.objects.filter(pk__in=pk_set) - - # TODO 待优化 - system_users = instance.system_users.all() - for system_user in system_users: - system_user: SystemUser - system_user.add_related_assets(assets) - - -@receiver(m2m_changed, sender=AssetPermission.system_users.through) -@on_transaction_commit -def on_asset_permission_system_users_changed(instance, action, reverse, **kwargs): - if reverse: - raise M2MReverseNotAllowed - if action != POST_ADD: - return - - logger.debug("Asset permission system_users change signal received") - system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) - assets = instance.assets.all().values_list('id', flat=True) - nodes = instance.nodes.all().values_list('id', flat=True) - - for system_user in system_users: - system_user.nodes.add(*tuple(nodes)) - system_user.add_related_assets(assets) - - # 动态系统用户,需要关联用户和用户组了 - if system_user.username_same_with_user: - users = instance.users.all().values_list('id', flat=True) - groups = instance.user_groups.all().values_list('id', flat=True) - system_user.groups.add(*tuple(groups)) - system_user.users.add(*tuple(users)) - - -@receiver(m2m_changed, sender=AssetPermission.users.through) -@on_transaction_commit -def on_asset_permission_users_changed(instance, action, reverse, pk_set, model, **kwargs): - if reverse: - raise M2MReverseNotAllowed - if action != POST_ADD: - return - - logger.debug("Asset permission users change signal received") - users = model.objects.filter(pk__in=pk_set) - system_users = instance.system_users.all() - - # TODO 待优化 - for system_user in system_users: - if system_user.username_same_with_user: - system_user.users.add(*tuple(users)) - - -@receiver(m2m_changed, sender=AssetPermission.user_groups.through) -@on_transaction_commit -def on_asset_permission_user_groups_changed(instance, action, pk_set, model, reverse, **kwargs): - if reverse: - raise M2MReverseNotAllowed - if action != POST_ADD: - return - - logger.debug("Asset permission user groups change signal received") - groups = model.objects.filter(pk__in=pk_set) - system_users = instance.system_users.all() - - # TODO 待优化 - for system_user in system_users: - if system_user.username_same_with_user: - system_user.groups.add(*tuple(groups)) + + + + + diff --git a/apps/perms/urls/asset_permission.py b/apps/perms/urls/asset_permission.py index f24b1b8ba..17fb22990 100644 --- a/apps/perms/urls/asset_permission.py +++ b/apps/perms/urls/asset_permission.py @@ -11,7 +11,6 @@ router.register('asset-permissions-users-relations', api.AssetPermissionUserRela router.register('asset-permissions-user-groups-relations', api.AssetPermissionUserGroupRelationViewSet, 'asset-permissions-user-groups-relation') router.register('asset-permissions-assets-relations', api.AssetPermissionAssetRelationViewSet, 'asset-permissions-assets-relation') router.register('asset-permissions-nodes-relations', api.AssetPermissionNodeRelationViewSet, 'asset-permissions-nodes-relation') -router.register('asset-permissions-system-users-relations', api.AssetPermissionSystemUserRelationViewSet, 'asset-permissions-system-users-relation') user_permission_urlpatterns = [ # 统一说明: diff --git a/apps/perms/utils/asset/permission.py b/apps/perms/utils/asset/permission.py index e749e630b..8b9991715 100644 --- a/apps/perms/utils/asset/permission.py +++ b/apps/perms/utils/asset/permission.py @@ -11,11 +11,7 @@ from perms.utils.asset.user_permission import get_user_all_asset_perm_ids logger = get_logger(__file__) -def validate_permission(user, asset, system_user, action='connect'): - - if not system_user.protocol in asset.protocols_as_dict.keys(): - return False, time.time() - +def validate_permission(user, asset, account, action='connect'): asset_perm_ids = get_user_all_asset_perm_ids(user) asset_perm_ids_from_asset = AssetPermission.assets.through.objects.filter( @@ -28,9 +24,7 @@ def validate_permission(user, asset, system_user, action='connect'): for node in nodes: ancestor_keys = node.get_ancestor_keys(with_self=True) node_keys.update(ancestor_keys) - node_ids = Node.objects.filter(key__in=node_keys).values_list('id', flat=True) - - node_ids = set(node_ids) + node_ids = set(Node.objects.filter(key__in=node_keys).values_list('id', flat=True)) asset_perm_ids_from_node = AssetPermission.nodes.through.objects.filter( assetpermission_id__in=asset_perm_ids, @@ -39,16 +33,9 @@ def validate_permission(user, asset, system_user, action='connect'): asset_perm_ids = {*asset_perm_ids_from_asset, *asset_perm_ids_from_node} - asset_perm_ids = AssetPermission.system_users.through.objects.filter( - assetpermission_id__in=asset_perm_ids, - systemuser_id=system_user.id - ).values_list('assetpermission_id', flat=True) - - asset_perm_ids = set(asset_perm_ids) - - asset_perms = AssetPermission.objects.filter( - id__in=asset_perm_ids - ).order_by('-date_expired') + asset_perms = AssetPermission.objects\ + .filter(id__in=asset_perm_ids, accounts__contains=account)\ + .order_by('-date_expired') if asset_perms: actions = set() diff --git a/apps/tickets/migrations/0018_auto_20220728_1125.py b/apps/tickets/migrations/0018_auto_20220728_1125.py new file mode 100644 index 000000000..70bb7c6bd --- /dev/null +++ b/apps/tickets/migrations/0018_auto_20220728_1125.py @@ -0,0 +1,23 @@ +# Generated by Django 3.2.14 on 2022-07-28 03:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0017_auto_20220623_1027'), + ] + + operations = [ + migrations.AlterField( + model_name='applyapplicationticket', + name='apply_permission_name', + field=models.CharField(max_length=128, verbose_name='Permission name'), + ), + migrations.AlterField( + model_name='applyassetticket', + name='apply_permission_name', + field=models.CharField(max_length=128, verbose_name='Permission name'), + ), + ] diff --git a/utils/generate_fake_data/resources/perms.py b/utils/generate_fake_data/resources/perms.py index 953712a5d..e3e866feb 100644 --- a/utils/generate_fake_data/resources/perms.py +++ b/utils/generate_fake_data/resources/perms.py @@ -47,12 +47,6 @@ class AssetPermissionGenerator(FakeDataGenerator): relation_name = 'node_id' self.set_relations(perms, through, relation_name, choices) - def set_system_users(self, perms): - through = AssetPermission.system_users.through - choices = self.system_user_ids - relation_name = 'systemuser_id' - self.set_relations(perms, through, relation_name, choices) - def set_relations(self, perms, through, relation_name, choices, choice_count=None): relations = [] @@ -79,4 +73,3 @@ class AssetPermissionGenerator(FakeDataGenerator): self.set_user_groups(created) self.set_assets(created) self.set_nodes(created) - self.set_system_users(created)