diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index aafc98988..805202615 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -1,14 +1,10 @@ from .mixin import * from .platform 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 * -from .cmd_filter import * from .gathered_user import * from .favorite_asset import * from .account_backup 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/signal_handlers/common.py b/apps/assets/api/asset.py similarity index 100% rename from apps/assets/signal_handlers/common.py rename to apps/assets/api/asset.py diff --git a/apps/assets/api/cmd_filter.py b/apps/assets/api/cmd_filter.py deleted file mode 100644 index 0e09d5c73..000000000 --- a/apps/assets/api/cmd_filter.py +++ /dev/null @@ -1,85 +0,0 @@ -# -*- coding: utf-8 -*- -# - -from rest_framework.response import Response -from rest_framework.generics import CreateAPIView -from django.shortcuts import get_object_or_404 - -from common.utils import reverse -from common.utils import lazyproperty -from orgs.mixins.api import OrgBulkModelViewSet -from ..models import CommandFilter, CommandFilterRule -from .. import serializers - -__all__ = [ - 'CommandFilterViewSet', 'CommandFilterRuleViewSet', 'CommandConfirmAPI', -] - - -class CommandFilterViewSet(OrgBulkModelViewSet): - model = CommandFilter - filterset_fields = ("name",) - search_fields = filterset_fields - serializer_class = serializers.CommandFilterSerializer - - -class CommandFilterRuleViewSet(OrgBulkModelViewSet): - model = CommandFilterRule - filterset_fields = ('content',) - search_fields = filterset_fields - serializer_class = serializers.CommandFilterRuleSerializer - - def get_queryset(self): - fpk = self.kwargs.get('filter_pk') - if not fpk: - return CommandFilterRule.objects.none() - cmd_filter = get_object_or_404(CommandFilter, pk=fpk) - return cmd_filter.rules.all() - - -class CommandConfirmAPI(CreateAPIView): - serializer_class = serializers.CommandConfirmSerializer - rbac_perms = { - 'POST': 'tickets.add_superticket' - } - - def create(self, request, *args, **kwargs): - ticket = self.create_command_confirm_ticket() - response_data = self.get_response_data(ticket) - return Response(data=response_data, status=200) - - def create_command_confirm_ticket(self): - ticket = self.serializer.cmd_filter_rule.create_command_confirm_ticket( - run_command=self.serializer.data.get('run_command'), - session=self.serializer.session, - cmd_filter_rule=self.serializer.cmd_filter_rule, - org_id=self.serializer.org.id, - ) - return ticket - - @staticmethod - def get_response_data(ticket): - confirm_status_url = reverse( - view_name='api-tickets:super-ticket-status', - kwargs={'pk': str(ticket.id)} - ) - ticket_detail_url = reverse( - view_name='api-tickets:ticket-detail', - kwargs={'pk': str(ticket.id)}, - external=True, api_to_ui=True - ) - ticket_detail_url = '{url}?type={type}'.format(url=ticket_detail_url, type=ticket.type) - ticket_assignees = ticket.current_step.ticket_assignees.all() - return { - 'check_confirm_status': {'method': 'GET', 'url': confirm_status_url}, - 'close_confirm': {'method': 'DELETE', 'url': confirm_status_url}, - 'ticket_detail_url': ticket_detail_url, - 'reviewers': [str(ticket_assignee.assignee) for ticket_assignee in ticket_assignees] - } - - @lazyproperty - def serializer(self): - serializer = self.get_serializer(data=self.request.data) - serializer.is_valid(raise_exception=True) - return serializer - diff --git a/apps/assets/api/system_user.py b/apps/assets/api/system_user.py index 96141d561..e69de29bb 100644 --- a/apps/assets/api/system_user.py +++ b/apps/assets/api/system_user.py @@ -1,252 +0,0 @@ -# ~*~ coding: utf-8 ~*~ -from django.shortcuts import get_object_or_404 -from rest_framework.response import Response -from rest_framework.decorators import action - -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 assets.const import Protocol -from ..models import SystemUser, CommandFilterRule -from .. import serializers -from ..serializers import SystemUserWithAuthInfoSerializer, SystemUserTempAuthSerializer -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', -] - - -class SystemUserViewSet(SuggestionMixin, OrgBulkModelViewSet): - """ - System user api set, for add,delete,update,list,retrieve resource - """ - model = SystemUser - filterset_fields = { - 'name': ['exact'], - 'username': ['exact'], - 'protocol': ['exact', 'in'], - 'type': ['exact', 'in'], - } - search_fields = filterset_fields - serializer_class = serializers.SystemUserSerializer - serializer_classes = { - 'default': serializers.SystemUserSerializer, - 'suggestion': serializers.MiniSystemUserSerializer - } - ordering_fields = ('name', 'protocol', 'login_mode') - ordering = ('name', ) - rbac_perms = { - 'su_from': 'assets.view_systemuser', - 'su_to': 'assets.view_systemuser', - 'match': 'assets.match_systemuser' - } - - @action(methods=['get'], detail=False, url_path='su-from') - def su_from(self, request, *args, **kwargs): - """ API 获取可选的 su_from 系统用户""" - queryset = self.filter_queryset(self.get_queryset()) - queryset = queryset.filter( - protocol=Protocol.ssh, login_mode=SystemUser.LOGIN_AUTO - ) - return self.get_paginate_response_if_need(queryset) - - @action(methods=['get'], detail=True, url_path='su-to') - def su_to(self, request, *args, **kwargs): - """ 获取系统用户的所有 su_to 系统用户 """ - pk = kwargs.get('pk') - system_user = get_object_or_404(SystemUser, pk=pk) - queryset = system_user.su_to.all() - queryset = self.filter_queryset(queryset) - return self.get_paginate_response_if_need(queryset) - - def get_paginate_response_if_need(self, queryset): - page = self.paginate_queryset(queryset) - if page is not None: - serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(serializer.data) - serializer = self.get_serializer(queryset, many=True) - return Response(serializer.data) - - -class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView): - """ - Get system user auth info - """ - model = SystemUser - serializer_class = SystemUserWithAuthInfoSerializer - rbac_perms = { - 'retrieve': 'assets.view_systemusersecret', - 'list': 'assets.view_systemusersecret', - 'change': 'assets.change_systemuser', - 'destroy': 'assets.change_systemuser', - } - - def destroy(self, request, *args, **kwargs): - instance = self.get_object() - instance.clear_auth() - return Response(status=204) - - -class SystemUserTempAuthInfoApi(generics.CreateAPIView): - model = SystemUser - permission_classes = (IsValidUser,) - serializer_class = 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 = 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 = 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' - } - - def get_serializer_class(self): - from ..serializers import CommandFilterRuleSerializer - return CommandFilterRuleSerializer - - def get_queryset(self): - user_id = self.request.query_params.get('user_id') - user_group_id = self.request.query_params.get('user_group_id') - system_user_id = self.kwargs.get('pk', None) - system_user = get_object_or_none(SystemUser, pk=system_user_id) - if not system_user: - system_user_id = self.request.query_params.get('system_user_id') - asset_id = self.request.query_params.get('asset_id') - application_id = self.request.query_params.get('application_id') - rules = CommandFilterRule.get_queryset( - user_id=user_id, - user_group_id=user_group_id, - system_user_id=system_user_id, - asset_id=asset_id, - application_id=application_id - ) - 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/0001_initial.py b/apps/assets/migrations/0001_initial.py index 7c0a9e95a..03c945c57 100644 --- a/apps/assets/migrations/0001_initial.py +++ b/apps/assets/migrations/0001_initial.py @@ -16,14 +16,6 @@ def add_default_group(apps, schema_editor): ) -def add_default_cluster(apps, schema_editor): - cluster_model = apps.get_model("assets", "Cluster") - db_alias = schema_editor.connection.alias - cluster_model.objects.using(db_alias).create( - name="Default" - ) - - class Migration(migrations.Migration): initial = True @@ -163,6 +155,5 @@ class Migration(migrations.Migration): unique_together=set([('ip', 'port')]), ), - migrations.RunPython(add_default_cluster), migrations.RunPython(add_default_group), ] diff --git a/apps/assets/migrations/0003_auto_20180109_2331.py b/apps/assets/migrations/0003_auto_20180109_2331.py index 960032331..097bc607a 100644 --- a/apps/assets/migrations/0003_auto_20180109_2331.py +++ b/apps/assets/migrations/0003_auto_20180109_2331.py @@ -14,9 +14,4 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AlterField( - model_name='asset', - name='cluster', - field=models.ForeignKey(default=assets.models.default_cluster, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='assets', to='assets.Cluster', verbose_name='Cluster'), - ), ] diff --git a/apps/assets/migrations/0092_auto_20220711_1409.py b/apps/assets/migrations/0092_auto_20220711_1409.py index 8036cc09f..6515392a0 100644 --- a/apps/assets/migrations/0092_auto_20220711_1409.py +++ b/apps/assets/migrations/0092_auto_20220711_1409.py @@ -1,7 +1,6 @@ # Generated by Django 3.2.12 on 2022-07-11 08:59 import assets.models.base -import assets.models.user import common.db.fields from django.conf import settings from django.db import migrations, models @@ -36,7 +35,7 @@ class Migration(migrations.Migration): ('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')), ('protocol', models.CharField(choices=[('ssh', 'SSH'), ('rdp', 'RDP'), ('telnet', 'Telnet'), ('vnc', 'VNC'), ('mysql', 'MySQL'), ('oracle', 'Oracle'), ('mariadb', 'MariaDB'), ('postgresql', 'PostgreSQL'), ('sqlserver', 'SQLServer'), ('redis', 'Redis'), ('mongodb', 'MongoDB'), ('k8s', 'K8S')], default='ssh', max_length=16, verbose_name='Protocol')), ('type', models.CharField(choices=[('common', 'Common user'), ('admin', 'Admin user')], default='common', max_length=16, verbose_name='Type')), - ('version', models.IntegerField(default=1, verbose_name='Version')), + ('version', models.IntegerField(default=0, verbose_name='Version')), ('history_id', models.AutoField(primary_key=True, serialize=False)), ('history_date', models.DateTimeField(db_index=True)), ('history_change_reason', models.CharField(max_length=100, null=True)), @@ -75,9 +74,9 @@ class Migration(migrations.Migration): ], options={ 'verbose_name': 'Account', - 'permissions': [('view_assetaccountsecret', 'Can view asset account secret'), ('change_assetaccountsecret', 'Can change asset account secret'), ('view_assethistoryaccount', 'Can view asset history account'), ('view_assethistoryaccountsecret', 'Can view asset history account secret')], + 'permissions': [('view_accountsecret', 'Can view asset account secret'), ('change_accountsecret', 'Can change asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret')], 'unique_together': {('username', 'asset')}, }, - bases=(models.Model, assets.models.base.AuthMixin, assets.models.user.ProtocolMixin), + bases=(models.Model, assets.models.base.AuthMixin, assets.models.protocol.ProtocolMixin), ), ] 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_20220803_1448.py b/apps/assets/migrations/0094_auto_20220803_1448.py new file mode 100644 index 000000000..eafb9caca --- /dev/null +++ b/apps/assets/migrations/0094_auto_20220803_1448.py @@ -0,0 +1,16 @@ +# Generated by Django 3.2.14 on 2022-08-03 06:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0093_auto_20220711_1413'), + ] + + operations = [ + 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 308202190..a785c5410 100644 --- a/apps/assets/models/__init__.py +++ b/apps/assets/models/__init__.py @@ -1,17 +1,17 @@ from .base import * from .platform import * +from ._user import * from .asset import * from .label import Label -from .user import * -from .cluster import * from .group import * from .domain import * from .node import * -from .cmd_filter import * -from .authbook import * from .utils import * -from .authbook import * from .gathered_user import * from .favorite_asset import * -from .backup import * from .account import * +from .backup import * +# 废弃以下 +from ._authbook import * +from .protocol import * +from .cmd_filter import * diff --git a/apps/assets/models/authbook.py b/apps/assets/models/_authbook.py similarity index 96% rename from apps/assets/models/authbook.py rename to apps/assets/models/_authbook.py index f5d9e457d..e96196d22 100644 --- a/apps/assets/models/authbook.py +++ b/apps/assets/models/_authbook.py @@ -125,9 +125,8 @@ class AuthBook(BaseUser, AbsConnectivity): logger.debug('Update asset admin user: {} {}'.format(self.asset, self.systemuser)) @classmethod - def get_queryset(cls, is_history_model=False): - model = cls.history.model if is_history_model else cls - queryset = model.objects.all() \ + def get_queryset(cls): + queryset = cls.objects.all() \ .annotate(ip=F('asset__ip')) \ .annotate(hostname=F('asset__hostname')) \ .annotate(platform=F('asset__platform__name')) \ @@ -136,5 +135,3 @@ class AuthBook(BaseUser, AbsConnectivity): def __str__(self): return self.smart_name - - diff --git a/apps/assets/models/user.py b/apps/assets/models/_user.py similarity index 72% rename from apps/assets/models/user.py rename to apps/assets/models/_user.py index 0aecfe665..6baffa3d0 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/_user.py @@ -11,68 +11,18 @@ from django.core.validators import MinValueValidator, MaxValueValidator from assets.const import Protocol from common.utils import signer 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 - Protocol = Protocol - - 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' LOGIN_MODE_CHOICES = ( - (LOGIN_AUTO, _('使用账号')), + (LOGIN_AUTO, _('Automatic managed')), (LOGIN_MANUAL, _('Manually input')) ) @@ -84,34 +34,24 @@ class SystemUser(ProtocolMixin, BaseUser): nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes")) assets = models.ManyToManyField( 'assets.Asset', blank=True, verbose_name=_("Assets"), + through='assets.AuthBook', through_fields=['systemuser', 'asset'], 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=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')) + 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')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo')) 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")) 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')) su_from = models.ForeignKey('self', on_delete=models.SET_NULL, related_name='su_to', null=True, verbose_name=_("Switch from")) @@ -130,7 +70,7 @@ class SystemUser(ProtocolMixin, BaseUser): return self.get_login_mode_display() def is_need_push(self): - if self.auto_push_account and self.is_protocol_support_push: + if self.auto_push and self.is_protocol_support_push: return True else: return False @@ -165,7 +105,7 @@ class SystemUser(ProtocolMixin, BaseUser): return True, None def get_all_assets(self): - from assets.models import Node + from assets.models import Node, Asset 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) diff --git a/apps/assets/models/account.py b/apps/assets/models/account.py index 7660ff682..641ba1a76 100644 --- a/apps/assets/models/account.py +++ b/apps/assets/models/account.py @@ -2,7 +2,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from simple_history.models import HistoricalRecords -from .user import ProtocolMixin +from .protocol import ProtocolMixin from .base import BaseUser, AbsConnectivity @@ -14,8 +14,10 @@ class Account(BaseUser, AbsConnectivity, ProtocolMixin): common = 'common', _('Common user') admin = 'admin', _('Admin user') - 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') + ) type = models.CharField(max_length=16, choices=Type.choices, default=Type.common, verbose_name=_("Type")) asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset')) version = models.IntegerField(default=0, verbose_name=_('Version')) @@ -25,10 +27,10 @@ class Account(BaseUser, AbsConnectivity, ProtocolMixin): verbose_name = _('Account') unique_together = [('username', 'asset')] permissions = [ - ('view_assetaccountsecret', _('Can view asset account secret')), - ('change_assetaccountsecret', _('Can change asset account secret')), - ('view_assethistoryaccount', _('Can view asset history account')), - ('view_assethistoryaccountsecret', _('Can view asset history account secret')), + ('view_accountsecret', _('Can view asset account secret')), + ('change_accountsecret', _('Can change asset account secret')), + ('view_historyaccount', _('Can view asset history account')), + ('view_historyaccountsecret', _('Can view asset history account secret')), ] def __str__(self): diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 0e3f743f0..56348f4b2 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -9,7 +9,6 @@ from collections import OrderedDict from django.db import models from django.utils.translation import ugettext_lazy as _ -from rest_framework.exceptions import ValidationError from common.db.fields import JsonDictTextField from common.utils import lazyproperty @@ -22,16 +21,6 @@ __all__ = ['Asset', 'ProtocolsMixin', 'AssetQuerySet', 'default_node', 'default_ logger = logging.getLogger(__name__) -def default_cluster(): - from assets.models import Cluster - name = "Default" - defaults = {"name": name} - cluster, created = Cluster.objects.get_or_create( - defaults=defaults, name=name - ) - return cluster.id - - def default_node(): try: from assets.models import Node 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/cmd_filter.py b/apps/assets/models/cmd_filter.py index b17a4263d..86e613d3d 100644 --- a/apps/assets/models/cmd_filter.py +++ b/apps/assets/models/cmd_filter.py @@ -9,7 +9,6 @@ from django.core.validators import MinValueValidator, MaxValueValidator from django.utils.translation import ugettext_lazy as _ from users.models import User, UserGroup -from applications.models import Application from ..models import SystemUser, Asset from common.utils import lazyproperty, get_logger, get_object_or_none @@ -125,6 +124,9 @@ class CommandFilterRule(OrgModelMixin): regex.append(cmd) continue + if not cmd: + continue + # 如果是单个字符 if cmd[-1].isalpha(): regex.append(r'\b{0}\b'.format(cmd)) @@ -187,6 +189,7 @@ class CommandFilterRule(OrgModelMixin): @classmethod def get_queryset(cls, user_id=None, user_group_id=None, system_user_id=None, asset_id=None, application_id=None, org_id=None): + from applications.models import Application user_groups = [] user = get_object_or_none(User, pk=user_id) if user: diff --git a/apps/assets/models/protocol.py b/apps/assets/models/protocol.py index b2e1b750b..b6bf4cf6a 100644 --- a/apps/assets/models/protocol.py +++ b/apps/assets/models/protocol.py @@ -7,3 +7,67 @@ from common.db.models import JMSBaseModel class Protocol(JMSBaseModel): name = models.CharField(max_length=32, verbose_name=_("Name")) port = models.IntegerField(verbose_name=_("Port")) + + +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/serializers/__init__.py b/apps/assets/serializers/__init__.py index 33835d832..f29c81d21 100644 --- a/apps/assets/serializers/__init__.py +++ b/apps/assets/serializers/__init__.py @@ -2,12 +2,10 @@ # from .asset import * -from .admin_user import * from .label import * from .system_user import * from .node import * from .domain import * -from .cmd_filter import * from .gathered_user import * from .favorite_asset import * from .account 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/common.py b/apps/assets/serializers/asset/common.py index 5284168f5..d3ca2b257 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -94,8 +94,7 @@ class AssetSerializer(CategoryDisplayMixin, OrgResourceModelSerializerMixin): 'public_ip', 'number', 'comment', ] fields_fk = [ - 'domain', 'domain_display', 'platform', 'platform_display', - 'admin_user', 'admin_user_display' + 'domain', 'domain_display', 'platform', ] fields_m2m = [ 'nodes', 'nodes_display', 'labels', 'labels_display', 'accounts' @@ -137,7 +136,7 @@ class AssetSerializer(CategoryDisplayMixin, OrgResourceModelSerializerMixin): @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 @@ -173,7 +172,6 @@ class AssetSerializer(CategoryDisplayMixin, OrgResourceModelSerializerMixin): 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/cmd_filter.py b/apps/assets/serializers/cmd_filter.py deleted file mode 100644 index 9a33dd6fa..000000000 --- a/apps/assets/serializers/cmd_filter.py +++ /dev/null @@ -1,110 +0,0 @@ -# -*- coding: utf-8 -*- -# -import re -from rest_framework import serializers - -from django.utils.translation import ugettext_lazy as _ -from ..models import CommandFilter, CommandFilterRule -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from orgs.utils import tmp_to_root_org -from common.utils import get_object_or_none, lazyproperty -from terminal.models import Session - - -class CommandFilterSerializer(BulkOrgResourceModelSerializer): - class Meta: - model = CommandFilter - fields_mini = ['id', 'name'] - fields_small = fields_mini + [ - 'org_id', 'org_name', 'is_active', - 'date_created', 'date_updated', - 'comment', 'created_by', - ] - fields_fk = ['rules'] - fields_m2m = ['users', 'user_groups', 'system_users', 'assets', 'applications'] - fields = fields_small + fields_fk + fields_m2m - extra_kwargs = { - 'rules': {'read_only': True}, - 'date_created': {'label': _("Date created")}, - 'date_updated': {'label': _("Date updated")}, - } - - -class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer): - type_display = serializers.ReadOnlyField(source='get_type_display', label=_("Type display")) - action_display = serializers.ReadOnlyField(source='get_action_display', label=_("Action display")) - - class Meta: - model = CommandFilterRule - fields_mini = ['id'] - fields_small = fields_mini + [ - 'type', 'type_display', 'content', 'ignore_case', 'pattern', - 'priority', 'action', 'action_display', 'reviewers', - 'date_created', 'date_updated', 'comment', 'created_by', - ] - fields_fk = ['filter'] - fields = fields_small + fields_fk - extra_kwargs = { - 'date_created': {'label': _("Date created")}, - 'date_updated': {'label': _("Date updated")}, - 'action_display': {'label': _("Action display")}, - 'pattern': {'label': _("Pattern")} - } - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.set_action_choices() - - def set_action_choices(self): - from django.conf import settings - action = self.fields.get('action') - if not action: - return - choices = action._choices - if not settings.XPACK_ENABLED: - choices.pop(CommandFilterRule.ActionChoices.confirm, None) - action._choices = choices - - def validate_content(self, content): - tp = self.initial_data.get("type") - if tp == CommandFilterRule.TYPE_COMMAND: - regex = CommandFilterRule.construct_command_regex(content) - else: - regex = content - ignore_case = self.initial_data.get('ignore_case') - succeed, error, pattern = CommandFilterRule.compile_regex(regex, ignore_case) - if not succeed: - raise serializers.ValidationError(error) - return content - - -class CommandConfirmSerializer(serializers.Serializer): - session_id = serializers.UUIDField(required=True, allow_null=False) - cmd_filter_rule_id = serializers.UUIDField(required=True, allow_null=False) - run_command = serializers.CharField(required=True, allow_null=False) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.session = None - self.cmd_filter_rule = None - - def validate_session_id(self, session_id): - self.session = self.validate_object_exist(Session, session_id) - return session_id - - def validate_cmd_filter_rule_id(self, cmd_filter_rule_id): - self.cmd_filter_rule = self.validate_object_exist(CommandFilterRule, cmd_filter_rule_id) - return cmd_filter_rule_id - - @staticmethod - def validate_object_exist(model, field_id): - with tmp_to_root_org(): - obj = get_object_or_none(model, id=field_id) - if not obj: - error = '{} Model object does not exist'.format(model.__name__) - raise serializers.ValidationError(error) - return obj - - @lazyproperty - def org(self): - return self.session.org diff --git a/apps/assets/serializers/system_user.py b/apps/assets/serializers/system_user.py index 9d5b454c0..f4ca404f3 100644 --- a/apps/assets/serializers/system_user.py +++ b/apps/assets/serializers/system_user.py @@ -1,98 +1,37 @@ 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 assets.const import Protocol -from ..models import SystemUser, Asset -from .utils import validate_password_for_ansible -from .base import AuthSerializerMixin __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 @@ -133,12 +72,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']: @@ -148,17 +81,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) @@ -182,70 +104,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'] = 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 == 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: @@ -253,28 +111,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): """ 系统用户最基本信息的数据结构 @@ -285,70 +121,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 - fields = [ - "id", "asset", "asset_display", 'systemuser', 'systemuser_display', - "connectivity", 'date_verified', 'org_id' - ] - 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/asset.py b/apps/assets/signal_handlers/asset.py index a7e466c9b..5aac26319 100644 --- a/apps/assets/signal_handlers/asset.py +++ b/apps/assets/signal_handlers/asset.py @@ -8,11 +8,10 @@ from django.dispatch import receiver from common.const.signals import POST_ADD, POST_REMOVE, PRE_REMOVE from common.utils import get_logger from common.decorator import on_transaction_commit -from assets.models import Asset, SystemUser, Node +from assets.models import Asset, Node from assets.tasks import ( update_assets_hardware_info_util, test_asset_connectivity_util, - push_system_user_to_assets, ) logger = get_logger(__file__) @@ -77,15 +76,15 @@ def on_asset_nodes_add(instance, action, reverse, pk_set, **kwargs): nodes_ancestors_keys.update(Node.get_node_ancestor_keys(node, with_self=True)) # 查询所有祖先节点关联的系统用户,都是要跟资产建立关系的 - system_user_ids = SystemUser.objects.filter( - nodes__key__in=nodes_ancestors_keys - ).distinct().values_list('id', flat=True) + # system_user_ids = SystemUser.objects.filter( + # nodes__key__in=nodes_ancestors_keys + # ).distinct().values_list('id', flat=True) # 查询所有已存在的关系 - m2m_model = SystemUser.assets.through - exist = set(m2m_model.objects.filter( - systemuser_id__in=system_user_ids, asset_id__in=asset_ids - ).values_list('systemuser_id', 'asset_id')) + # m2m_model = SystemUser.assets.through + # exist = set(m2m_model.objects.filter( + # systemuser_id__in=system_user_ids, asset_id__in=asset_ids + # ).values_list('systemuser_id', 'asset_id')) # TODO 优化 # to_create = [] # for system_user_id in system_user_ids: diff --git a/apps/assets/signal_handlers/system_user.py b/apps/assets/signal_handlers/system_user.py deleted file mode 100644 index 64a656c29..000000000 --- a/apps/assets/signal_handlers/system_user.py +++ /dev/null @@ -1,115 +0,0 @@ -# -*- coding: utf-8 -*- -# -from django.db.models.signals import ( - post_save, m2m_changed, pre_save, pre_delete, post_delete -) -from django.dispatch import receiver - -from common.exceptions import M2MReverseNotAllowed -from common.const.signals import POST_ADD -from common.utils import get_logger -from common.decorator import on_transaction_commit -from assets.models import Asset, SystemUser, Node -from users.models import User -from assets.tasks import ( - push_system_user_to_assets_manual, - push_system_user_to_assets, - add_nodes_assets_to_system_users -) - -logger = get_logger(__file__) - - -@receiver(m2m_changed, sender=SystemUser.assets.through) -@on_transaction_commit -def on_system_user_assets_change(instance, action, model, pk_set, **kwargs): - """ - 当系统用户和资产关系发生变化时,应该重新推送系统用户到新添加的资产中 - """ - logger.debug("System user assets change signal recv: {}".format(instance)) - - if not instance: - logger.debug('No system user found') - return - - if model == Asset: - system_user_ids = [instance.id] - asset_ids = pk_set - else: - system_user_ids = pk_set - asset_ids = [instance.id] - # todo: Auto create account if need - - -@receiver(m2m_changed, sender=SystemUser.users.through) -@on_transaction_commit -def on_system_user_users_change(sender, instance: SystemUser, action, model, pk_set, reverse, **kwargs): - """ - 当系统用户和用户关系发生变化时,应该重新推送系统用户资产中 - """ - if action != POST_ADD: - return - - if reverse: - raise M2MReverseNotAllowed - - if not instance.username_same_with_user: - return - - logger.debug("System user users change signal recv: {}".format(instance)) - usernames = model.objects.filter(pk__in=pk_set).values_list('username', flat=True) - - for username in usernames: - push_system_user_to_assets_manual.delay(instance, username) - - -@receiver(m2m_changed, sender=SystemUser.nodes.through) -@on_transaction_commit -def on_system_user_nodes_change(sender, instance=None, action=None, model=None, pk_set=None, **kwargs): - """ - 当系统用户和节点关系发生变化时,应该将节点下资产关联到新的系统用户上 - """ - if action != POST_ADD: - return - logger.info("System user nodes update signal recv: {}".format(instance)) - - queryset = model.objects.filter(pk__in=pk_set) - if model == Node: - nodes_keys = queryset.values_list('key', flat=True) - system_users = [instance] - else: - nodes_keys = [instance.key] - system_users = queryset - add_nodes_assets_to_system_users.delay(nodes_keys, system_users) - - -@receiver(m2m_changed, sender=SystemUser.groups.through) -def on_system_user_groups_change(instance, action, pk_set, reverse, **kwargs): - """ - 当系统用户和用户组关系发生变化时,应该将组下用户关联到新的系统用户上 - """ - if action != POST_ADD: - return - if reverse: - raise M2MReverseNotAllowed - logger.info("System user groups update signal recv: {}".format(instance)) - - users = User.objects.filter(groups__id__in=pk_set).distinct() - instance.users.add(*users) - - -@receiver(post_save, sender=SystemUser, dispatch_uid="jms") -@on_transaction_commit -def on_system_user_update(instance: SystemUser, created, **kwargs): - """ - 当系统用户更新时,可能更新了密钥,用户名等,这时要自动推送系统用户到资产上, - 其实应该当 用户名,密码,密钥 sudo等更新时再推送,这里偷个懒, - 这里直接取了 instance.assets 因为nodes和系统用户发生变化时,会自动将nodes下的资产 - 关联到上面 - """ - if instance and not created: - logger.info("System user update signal recv: {}".format(instance)) - assets = instance.assets.all().valid() - push_system_user_to_assets.delay(instance.id, [_asset.id for _asset in assets]) - # add assets to su_from - instance.add_related_assets_to_su_from_if_need(assets) diff --git a/apps/assets/tasks/__init__.py b/apps/assets/tasks/__init__.py index 22ccbf503..fcd5fbe46 100644 --- a/apps/assets/tasks/__init__.py +++ b/apps/assets/tasks/__init__.py @@ -6,7 +6,5 @@ from .asset_connectivity import * from .account_connectivity import * from .gather_asset_users import * from .gather_asset_hardware_info import * -from .push_system_user import * -from .system_user_connectivity import * from .nodes_amount import * from .backup import * diff --git a/apps/assets/tasks/common.py b/apps/assets/tasks/common.py index 6ad17b4c3..ec51c5a2b 100644 --- a/apps/assets/tasks/common.py +++ b/apps/assets/tasks/common.py @@ -1,38 +1,2 @@ # -*- coding: utf-8 -*- # - -from celery import shared_task - -from orgs.utils import tmp_to_root_org -from assets.models import AuthBook - -__all__ = ['add_nodes_assets_to_system_users'] - - -# Todo: 等待优化 -@shared_task -@tmp_to_root_org() -def add_nodes_assets_to_system_users(nodes_keys, system_users): - from ..models import Node - from assets.tasks import push_system_user_to_assets - - nodes = Node.objects.filter(key__in=nodes_keys) - assets = Node.get_nodes_all_assets(*nodes) - - for system_user in system_users: - """ 解决资产和节点进行关联时,已经关联过的节点不会触发 authbook post_save 信号, - 无法更新节点下所有资产的管理用户的问题 """ - need_push_asset_ids = [] - for asset in assets: - defaults = {'asset': asset, 'systemuser': system_user, 'org_id': asset.org_id} - instance, created = AuthBook.objects.update_or_create( - defaults=defaults, asset=asset, systemuser=system_user - ) - if created: - need_push_asset_ids.append(asset.id) - # 不再自动更新资产管理用户,只允许用户手动指定。 - # 只要关联都需要更新资产的管理用户 - # instance.update_asset_admin_user_if_need() - - if need_push_asset_ids: - push_system_user_to_assets.delay(system_user.id, need_push_asset_ids) diff --git a/apps/assets/tasks/push_system_user.py b/apps/assets/tasks/push_system_user.py deleted file mode 100644 index 8834a29e9..000000000 --- a/apps/assets/tasks/push_system_user.py +++ /dev/null @@ -1,307 +0,0 @@ -# ~*~ coding: utf-8 ~*~ - -from itertools import groupby -from celery import shared_task -from common.db.utils import get_object_if_need, get_objects -from django.utils.translation import ugettext as _, gettext_noop -from django.db.models import Empty - -from common.utils import encrypt_password, get_logger -from assets.models import SystemUser, Asset -from orgs.utils import org_aware_func, tmp_to_root_org -from . import const -from .utils import clean_ansible_task_hosts, group_asset_by_platform - - -logger = get_logger(__file__) -__all__ = [ - 'push_system_user_util', 'push_system_user_to_assets', - 'push_system_user_to_assets_manual', 'push_system_user_a_asset_manual', - 'push_system_users_a_asset' -] - - -def _split_by_comma(raw: str): - try: - return [i.strip() for i in raw.split(',')] - except AttributeError: - return [] - - -def _dump_args(args: dict): - return ' '.join([f'{k}={v}' for k, v in args.items() if v is not Empty]) - - -def get_push_unixlike_system_user_tasks(system_user, username=None, **kwargs): - algorithm = kwargs.get('algorithm') - if username is None: - username = system_user.username - - comment = system_user.name - if system_user.username_same_with_user: - from users.models import User - user = User.objects.filter(username=username).only('name', 'username').first() - if user: - comment = f'{system_user.name}[{str(user)}]' - comment = comment.replace(' ', '') - - password = system_user.password - public_key = system_user.public_key - - groups = _split_by_comma(system_user.system_groups) - - if groups: - groups = '"%s"' % ','.join(groups) - - add_user_args = { - 'name': username, - 'shell': system_user.shell or Empty, - 'state': 'present', - 'home': system_user.home or Empty, - 'expires': -1, - 'groups': groups or Empty, - 'comment': comment - } - - tasks = [ - { - 'name': 'Add user {}'.format(username), - 'action': { - 'module': 'user', - 'args': _dump_args(add_user_args), - } - }, - { - 'name': 'Add group {}'.format(username), - 'action': { - 'module': 'group', - 'args': 'name={} state=present'.format(username), - } - } - ] - if not system_user.home: - tasks.extend([ - { - 'name': 'Check home dir exists', - 'action': { - 'module': 'stat', - 'args': 'path=/home/{}'.format(username) - }, - 'register': 'home_existed' - }, - { - 'name': "Set home dir permission", - 'action': { - 'module': 'file', - 'args': "path=/home/{0} owner={0} group={0} mode=700".format(username) - }, - 'when': 'home_existed.stat.exists == true' - } - ]) - if password: - tasks.append({ - 'name': 'Set {} password'.format(username), - 'action': { - 'module': 'user', - 'args': 'name={} shell={} state=present password={}'.format( - username, system_user.shell, - encrypt_password(password, salt="K3mIlKK", algorithm=algorithm), - ), - } - }) - if public_key: - tasks.append({ - 'name': 'Set {} authorized key'.format(username), - 'action': { - 'module': 'authorized_key', - 'args': "user={} state=present key='{}'".format( - username, public_key - ) - } - }) - if system_user.sudo: - sudo = system_user.sudo.replace('\r\n', '\n').replace('\r', '\n') - sudo_list = sudo.split('\n') - sudo_tmp = [] - for s in sudo_list: - sudo_tmp.append(s.strip(',')) - sudo = ','.join(sudo_tmp) - tasks.append({ - 'name': 'Set {} sudo setting'.format(username), - 'action': { - 'module': 'lineinfile', - 'args': "dest=/etc/sudoers state=present regexp='^{0} ALL=' " - "line='{0} ALL=(ALL) NOPASSWD: {1}' " - "validate='visudo -cf %s'".format(username, sudo) - } - }) - - return tasks - - -def get_push_windows_system_user_tasks(system_user: SystemUser, username=None, **kwargs): - if username is None: - username = system_user.username - password = system_user.password - groups = {'Users', 'Remote Desktop Users'} - if system_user.system_groups: - groups.update(_split_by_comma(system_user.system_groups)) - groups = ','.join(groups) - - tasks = [] - if not password: - logger.error("Error: no password found") - return tasks - - if system_user.ad_domain: - logger.error('System user with AD domain do not support push.') - return tasks - - task = { - 'name': 'Add user {}'.format(username), - 'action': { - 'module': 'win_user', - 'args': 'fullname={} ' - 'name={} ' - 'password={} ' - 'state=present ' - 'update_password=always ' - 'password_expired=no ' - 'password_never_expires=yes ' - 'groups="{}" ' - 'groups_action=add ' - ''.format(username, username, password, groups), - } - } - tasks.append(task) - return tasks - - -def get_push_system_user_tasks(system_user, platform="unixlike", username=None, algorithm=None): - """ - 获取推送系统用户的 ansible 命令,跟资产无关 - :param system_user: - :param platform: - :param username: 当动态时,近推送某个 - :return: - """ - get_task_map = { - "unixlike": get_push_unixlike_system_user_tasks, - "windows": get_push_windows_system_user_tasks, - } - get_tasks = get_task_map.get(platform, get_push_unixlike_system_user_tasks) - if not system_user.username_same_with_user: - return get_tasks(system_user, algorithm=algorithm) - tasks = [] - # 仅推送这个username - if username is not None: - tasks.extend(get_tasks(system_user, username, algorithm=algorithm)) - return tasks - users = system_user.users.all().values_list('username', flat=True) - print(_("System user is dynamic: {}").format(list(users))) - for _username in users: - tasks.extend(get_tasks(system_user, _username, algorithm=algorithm)) - return tasks - - -@org_aware_func("system_user") -def push_system_user_util(system_user, assets, task_name, username=None): - from ops.utils import update_or_create_ansible_task - assets = clean_ansible_task_hosts(assets, system_user=system_user) - if not assets: - return {} - - # 资产按平台分类 - assets_sorted = sorted(assets, key=group_asset_by_platform) - platform_hosts = groupby(assets_sorted, key=group_asset_by_platform) - - if system_user.username_same_with_user: - if username is None: - # 动态系统用户,但是没有指定 username - usernames = list(system_user.users.all().values_list('username', flat=True).distinct()) - else: - usernames = [username] - else: - # 非动态系统用户指定 username 无效 - assert username is None, 'Only Dynamic user can assign `username`' - usernames = [system_user.username] - - def run_task(_tasks, _hosts): - if not _tasks: - return - task, created = update_or_create_ansible_task( - task_name=task_name, hosts=_hosts, tasks=_tasks, pattern='all', - options=const.TASK_OPTIONS, run_as_admin=True, - ) - task.run() - - for platform, _assets in platform_hosts: - _assets = list(_assets) - if not _assets: - continue - print(_("Start push system user for platform: [{}]").format(platform)) - print(_("Hosts count: {}").format(len(_assets))) - - for u in usernames: - for a in _assets: - system_user.load_asset_special_auth(a, u) - algorithm = 'des' if a.platform.name == 'AIX' else 'sha512' - tasks = get_push_system_user_tasks( - system_user, platform, username=u, - algorithm=algorithm - ) - run_task(tasks, [a]) - - -@shared_task(queue="ansible") -@tmp_to_root_org() -def push_system_user_to_assets_manual(system_user, username=None): - """ - 将系统用户推送到与它关联的所有资产上 - """ - system_user = get_object_if_need(SystemUser, system_user) - assets = system_user.get_related_assets() - task_name = gettext_noop("Push system users to assets: ") + system_user.name - return push_system_user_util(system_user, assets, task_name=task_name, username=username) - - -@shared_task(queue="ansible") -@tmp_to_root_org() -def push_system_user_a_asset_manual(system_user, asset, username=None): - """ - 将系统用户推送到一个资产上 - """ - # if username is None: - # username = system_user.username - task_name = gettext_noop("Push system users to asset: ") + "{}({}) => {}".format( - system_user.name, username or system_user.username, asset - ) - return push_system_user_util(system_user, [asset], task_name=task_name, username=username) - - -@shared_task(queue="ansible") -@tmp_to_root_org() -def push_system_users_a_asset(system_users, asset): - for system_user in system_users: - push_system_user_a_asset_manual(system_user, asset) - - -@shared_task(queue="ansible") -@tmp_to_root_org() -def push_system_user_to_assets(system_user_id, asset_ids, username=None): - """ - 推送系统用户到指定的若干资产上 - """ - system_user = SystemUser.objects.get(id=system_user_id) - assets = get_objects(Asset, asset_ids) - task_name = gettext_noop("Push system users to assets: ") + system_user.name - - return push_system_user_util(system_user, assets, task_name, username=username) - -# @shared_task -# @register_as_period_task(interval=3600) -# @after_app_ready_start -# @after_app_shutdown_clean_periodic -# def push_system_user_period(): -# for system_user in SystemUser.objects.all(): -# push_system_user_related_nodes(system_user) diff --git a/apps/assets/tasks/system_user_connectivity.py b/apps/assets/tasks/system_user_connectivity.py deleted file mode 100644 index 2213bfa26..000000000 --- a/apps/assets/tasks/system_user_connectivity.py +++ /dev/null @@ -1,151 +0,0 @@ - -from itertools import groupby -from collections import defaultdict - -from celery import shared_task -from django.utils.translation import ugettext as _, gettext_noop - -from assets.models import Asset -from common.utils import get_logger -from orgs.utils import tmp_to_org, org_aware_func -from ..models import SystemUser, Connectivity, Account -from . import const -from .utils import ( - clean_ansible_task_hosts, group_asset_by_platform -) - -logger = get_logger(__name__) -__all__ = [ - 'test_system_user_connectivity_util', 'test_system_user_connectivity_manual', - 'test_system_user_connectivity_period', 'test_system_user_connectivity_a_asset', - 'test_system_users_connectivity_a_asset' -] - - -def set_assets_accounts_connectivity(system_user, assets, results_summary): - asset_ids_ok = set() - asset_ids_failed = set() - - asset_hostnames_ok = results_summary.get('contacted', {}).keys() - - for asset in assets: - if asset.hostname in asset_hostnames_ok: - asset_ids_ok.add(asset.id) - else: - asset_ids_failed.add(asset.id) - - accounts_ok = Account.objects.filter(asset_id__in=asset_ids_ok, systemuser=system_user) - accounts_failed = Account.objects.filter(asset_id__in=asset_ids_failed, systemuser=system_user) - - Account.bulk_set_connectivity(accounts_ok, Connectivity.ok) - Account.bulk_set_connectivity(accounts_failed, Connectivity.failed) - - -@org_aware_func("system_user") -def test_system_user_connectivity_util(system_user, assets, task_name): - """ - Test system cant connect his assets or not. - :param system_user: - :param assets: - :param task_name: - :return: - """ - from ops.utils import update_or_create_ansible_task - - if system_user.username_same_with_user: - logger.error(_("Dynamic system user not support test")) - return - - # hosts = clean_ansible_task_hosts(assets, system_user=system_user) - # TODO: 这里不传递系统用户,因为clean_ansible_task_hosts会通过system_user来判断是否可以推送, - # 不符合测试可连接性逻辑, 后面需要优化此逻辑 - hosts = clean_ansible_task_hosts(assets) - if not hosts: - return {} - platform_hosts_map = {} - hosts_sorted = sorted(hosts, key=group_asset_by_platform) - platform_hosts = groupby(hosts_sorted, key=group_asset_by_platform) - for i in platform_hosts: - platform_hosts_map[i[0]] = list(i[1]) - - platform_tasks_map = { - "unixlike": const.PING_UNIXLIKE_TASKS, - "windows": const.PING_WINDOWS_TASKS - } - - results_summary = dict( - contacted=defaultdict(dict), dark=defaultdict(dict), success=True - ) - - def run_task(_tasks, _hosts, _username): - old_name = "{}".format(system_user) - new_name = "{}({})".format(system_user.name, _username) - _task_name = task_name.replace(old_name, new_name) - _task, created = update_or_create_ansible_task( - task_name=_task_name, hosts=_hosts, tasks=_tasks, - pattern='all', options=const.TASK_OPTIONS, - run_as=_username, system_user=system_user - ) - raw, summary = _task.run() - success = summary.get('success', False) - contacted = summary.get('contacted', {}) - dark = summary.get('dark', {}) - - results_summary['success'] &= success - results_summary['contacted'].update(contacted) - results_summary['dark'].update(dark) - - for platform, _hosts in platform_hosts_map.items(): - if not _hosts: - continue - if platform not in ["unixlike", "windows"]: - continue - - tasks = platform_tasks_map[platform] - print(_("Start test system user connectivity for platform: [{}]").format(platform)) - print(_("Hosts count: {}").format(len(_hosts))) - # 用户名不是动态的,用户名则是一个 - logger.debug("System user not has special auth") - run_task(tasks, _hosts, system_user.username) - - set_assets_accounts_connectivity(system_user, hosts, results_summary) - return results_summary - - -@shared_task(queue="ansible") -@org_aware_func("system_user") -def test_system_user_connectivity_manual(system_user, asset_ids=None): - task_name = gettext_noop("Test system user connectivity: ") + str(system_user) - if asset_ids: - assets = Asset.objects.filter(id__in=asset_ids) - else: - assets = system_user.get_related_assets() - test_system_user_connectivity_util(system_user, assets, task_name) - - -@shared_task(queue="ansible") -@org_aware_func("system_user") -def test_system_user_connectivity_a_asset(system_user, asset): - task_name = gettext_noop("Test system user connectivity: ") + "{} => {}".format( - system_user, asset - ) - test_system_user_connectivity_util(system_user, [asset], task_name) - - -@shared_task(queue="ansible") -def test_system_users_connectivity_a_asset(system_users, asset): - for system_user in system_users: - test_system_user_connectivity_a_asset(system_user, asset) - - -@shared_task(queue="ansible") -def test_system_user_connectivity_period(): - if not const.PERIOD_TASK_ENABLED: - logger.debug("Period task disabled, test system user connectivity pass") - return - queryset_map = SystemUser.objects.all_group_by_org() - for org, system_user in queryset_map.items(): - task_name = gettext_noop("Test system user connectivity period: ") + str(system_user) - with tmp_to_org(org): - assets = system_user.get_related_assets() - test_system_user_connectivity_util(system_user, assets, task_name) diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index c464649b1..5070cd44d 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -18,24 +18,15 @@ router.register(r'account-secrets', api.AccountSecretsViewSet, 'account-secret') 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') 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') -cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter') -cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule') - urlpatterns = [ # path('assets//gateways/', api.AssetGatewayListApi.as_view(), name='asset-gateway-list'), @@ -46,15 +37,6 @@ 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//auth-info/', api.SystemUserAuthInfoApi.as_view(), name='system-user-auth-info'), - path('system-users//assets/', api.SystemUserAssetsListView.as_view(), name='system-user-assets'), - path('system-users//assets//auth-info/', api.SystemUserAssetAuthInfoApi.as_view(), name='system-user-asset-auth-info'), - path('system-users//applications//auth-info/', api.SystemUserAppAuthInfoApi.as_view(), name='system-user-app-auth-info'), - path('system-users//temp-auth/', api.SystemUserTempAuthInfoApi.as_view(), name='system-user-asset-temp-info'), - 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'), - path('accounts/tasks/', api.AccountTaskCreateAPI.as_view(), name='account-task-create'), path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'), @@ -69,14 +51,7 @@ urlpatterns = [ path('nodes//tasks/', api.NodeTaskCreateApi.as_view(), name='node-task-create'), path('gateways//test-connective/', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), - - path('cmd-filters/command-confirm/', api.CommandConfirmAPI.as_view(), name='command-confirm'), - ] -old_version_urlpatterns = [ - re_path('(?Padmin-user|system-user|domain|gateway|cmd-filter|asset-user)/.*', capi.redirect_plural_name_api) -] - -urlpatterns += router.urls + cmd_filter_router.urls + old_version_urlpatterns +urlpatterns += router.urls 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/orgs/signal_handlers/common.py b/apps/orgs/signal_handlers/common.py index 48576a828..fc8b4af12 100644 --- a/apps/orgs/signal_handlers/common.py +++ b/apps/orgs/signal_handlers/common.py @@ -20,7 +20,6 @@ from common.decorator import on_transaction_commit from common.signals import django_ready from common.utils import get_logger from common.utils.connection import RedisPubSub -from assets.models import CommandFilterRule from users.signals import post_user_leave_org @@ -141,7 +140,7 @@ def _clear_users_from_org(org, users): for m in models: _remove_users(m, users, org) - _remove_users(CommandFilterRule, users, org, user_field_name='reviewers') + # _remove_users(CommandFilterRule, users, org, user_field_name='reviewers') @receiver(post_save, sender=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 b258e6742..2d683920d 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -7,7 +7,7 @@ from django.db import models from orgs.mixins.models import OrgModelMixin from common.utils import lazyproperty from common.db.models import BaseCreateUpdateModel -from assets.models import Asset, SystemUser, Node, FamilyMixin +from assets.models import Asset, Node, FamilyMixin from .base import BasePermission @@ -23,14 +23,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): @@ -48,16 +47,11 @@ 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( models.Prefetch('nodes', queryset=Node.objects.all().only('key')), models.Prefetch('assets', queryset=Asset.objects.all().only('id')), - models.Prefetch('system_users', queryset=SystemUser.objects.all().only('id')) ).order_by() def get_all_assets(self): @@ -81,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)