From 2432b9a55331bb82355524dcd3bb2a80a97728d5 Mon Sep 17 00:00:00 2001 From: xinwen Date: Mon, 12 Oct 2020 19:29:22 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix(orgs):=20=E7=BB=84=E7=BB=87=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=88=90=E5=91=98bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/orgs/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/orgs/models.py b/apps/orgs/models.py index 0b732506b..b244ddec4 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -231,7 +231,7 @@ def _none2list(*args): def _users2pks(users, admins, auditors): - return [user.pk for user in chain(users, admins, auditors)] + return [user.pk for user in chain(users, admins, auditors) if hasattr(user, 'pk')] class UserRoleMapper(dict): @@ -295,11 +295,11 @@ class OrgMemeberManager(models.Manager): ) oms_add = [] - for users, role in add_mapper: - for user in users: - if isinstance(user, models.Model): - user = user.id - oms_add.append(self.model(org_id=org.id, user_id=user, role=role)) + for _users, _role in add_mapper: + for _user in _users: + if isinstance(_user, models.Model): + _user = _user.id + oms_add.append(self.model(org_id=org.id, user_id=_user, role=_role)) send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False, model=User, pk_set=_users2pks(users, admins, auditors), using=self.db) From 0ad389515b133956bf6309ded7b98fd50f704ac1 Mon Sep 17 00:00:00 2001 From: xinwen Date: Tue, 13 Oct 2020 16:42:41 +0800 Subject: [PATCH 2/6] =?UTF-8?q?fix(assets):=20=E8=B5=84=E4=BA=A7=E6=A0=91?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=88=A0=E9=99=A4=E8=B5=84=E4=BA=A7=E6=95=B0?= =?UTF-8?q?=E9=87=8F=E4=B8=8D=E5=AF=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/signals_handler.py | 35 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/apps/assets/signals_handler.py b/apps/assets/signals_handler.py index 41b60d30a..a90636161 100644 --- a/apps/assets/signals_handler.py +++ b/apps/assets/signals_handler.py @@ -9,9 +9,8 @@ from django.db.models.signals import ( from django.db.models import Q, F from django.dispatch import receiver -from common.local import thread_local from common.exceptions import M2MReverseNotAllowed -from common.const.signals import PRE_ADD, POST_ADD, POST_REMOVE, PRE_CLEAR +from common.const.signals import PRE_ADD, POST_ADD, POST_REMOVE, PRE_CLEAR, PRE_REMOVE from common.utils import get_logger from common.decorator import on_transaction_commit from .models import Asset, SystemUser, Node, compute_parent_key @@ -321,26 +320,26 @@ def update_nodes_assets_amount(action, instance, reverse, pk_set, **kwargs): _update_nodes_asset_amount(node_keys, asset_pk, operator) -ASSET_DELETE_SIGNAL_FOR_NODE_TREE_PARAMS = 'asset_delete_signal_for_node_tree_params' +RELATED_NODE_IDS = '_related_node_ids' @receiver(pre_delete, sender=Asset) -def on_asset_delete(instance: Asset, **kwargs): - node_keys = Node.objects.filter( +def on_asset_delete(instance: Asset, using, **kwargs): + node_ids = set(Node.objects.filter( assets=instance - ).distinct().values_list('key', flat=True) - - params = { - 'node_keys': set(node_keys), - 'asset_pk': instance.id, - 'operator': sub - } - - setattr(thread_local, ASSET_DELETE_SIGNAL_FOR_NODE_TREE_PARAMS, params) + ).distinct().values_list('id', flat=True)) + setattr(instance, RELATED_NODE_IDS, node_ids) + m2m_changed.send( + sender=Asset.nodes.through, instance=instance, reverse=False, + model=Node, pk_set=node_ids, using=using, action=PRE_REMOVE + ) @receiver(post_delete, sender=Asset) -def on_asset_post_delete(instance: Asset, **kwargs): - params = getattr(thread_local, ASSET_DELETE_SIGNAL_FOR_NODE_TREE_PARAMS, None) - if params and params.get('asset_pk') == instance.id: - _update_nodes_asset_amount(**params) +def on_asset_post_delete(instance: Asset, using, **kwargs): + node_ids = getattr(instance, RELATED_NODE_IDS, None) + if node_ids: + m2m_changed.send( + sender=Asset.nodes.through, instance=instance, reverse=False, + model=Node, pk_set=node_ids, using=using, action=POST_REMOVE + ) From ef86a49c1e81db41e7a51f2105b99ac3ad85f0cb Mon Sep 17 00:00:00 2001 From: xinwen Date: Tue, 13 Oct 2020 19:04:26 +0800 Subject: [PATCH 3/6] =?UTF-8?q?fix(perms):=20=E4=BF=AE=E5=A4=8D=E5=A4=B1?= =?UTF-8?q?=E6=95=88=E8=B5=84=E4=BA=A7=E6=8E=88=E6=9D=83action=20=E8=BF=98?= =?UTF-8?q?=E5=9C=A8=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/perms/utils/asset_permission.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/perms/utils/asset_permission.py b/apps/perms/utils/asset_permission.py index 6474b4334..09218de04 100644 --- a/apps/perms/utils/asset_permission.py +++ b/apps/perms/utils/asset_permission.py @@ -37,12 +37,12 @@ def get_asset_system_users_id_with_actions(asset_perm_queryset: BasePermissionQu def get_asset_system_users_id_with_actions_by_user(user: User, asset: Asset): queryset = AssetPermission.objects.filter( Q(users=user) | Q(user_groups__users=user) - ) + ).valid() return get_asset_system_users_id_with_actions(queryset, asset) def get_asset_system_users_id_with_actions_by_group(group: UserGroup, asset: Asset): queryset = AssetPermission.objects.filter( user_groups=group - ) + ).valid() return get_asset_system_users_id_with_actions(queryset, asset) From 459c5c07c959f264971dfdc6ff5c0407476177aa Mon Sep 17 00:00:00 2001 From: xinwen Date: Tue, 13 Oct 2020 15:50:11 +0800 Subject: [PATCH 4/6] =?UTF-8?q?fix(perms):=20=E6=9C=AA=E6=BF=80=E6=B4=BB?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E4=B8=8D=E8=83=BD=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/mixin.py | 1 + apps/perms/serializers/user_permission.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/assets/api/mixin.py b/apps/assets/api/mixin.py index 89555324b..4374f399a 100644 --- a/apps/assets/api/mixin.py +++ b/apps/assets/api/mixin.py @@ -60,6 +60,7 @@ class SerializeToTreeNodeMixin: 'isParent': False, 'open': False, 'iconSkin': self.get_platform(asset), + 'chkDisabled': not asset.is_active, 'meta': { 'type': 'asset', 'asset': { diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index 40334786f..bfc273775 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -75,7 +75,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer): model = Asset only_fields = [ "id", "hostname", "ip", "protocols", "os", 'domain', - "platform", "comment", "org_id", + "platform", "comment", "org_id", "is_active" ] fields = only_fields + ['org_name'] read_only_fields = fields From 52f1dcf66267d44cbb77a59392076e09689b9147 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 14 Oct 2020 15:17:06 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix(users):=20=E4=BF=AE=E5=A4=8D=E9=82=80?= =?UTF-8?q?=E8=AF=B7=E7=94=A8=E6=88=B7=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/orgs/api.py | 6 ++---- apps/orgs/models.py | 2 +- apps/users/api/user.py | 26 +++++++++++++++++++++++--- apps/users/serializers/user.py | 12 +++++++++--- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/apps/orgs/api.py b/apps/orgs/api.py index e7c83fa36..0b63eeabf 100644 --- a/apps/orgs/api.py +++ b/apps/orgs/api.py @@ -2,14 +2,12 @@ # from django.utils.translation import ugettext as _ -from django.db.models import Q -from rest_framework import status, generics +from rest_framework import status from rest_framework.views import Response from rest_framework_bulk import BulkModelViewSet -from rest_framework.mixins import CreateModelMixin from common.permissions import IsSuperUserOrAppUser -from common.drf.api import JMSBulkRelationModelViewSet +from common.drf.api import JMSBulkRelationModelViewSet, JMSModelViewSet from .models import Organization, ROLE from .serializers import ( OrgSerializer, OrgReadSerializer, diff --git a/apps/orgs/models.py b/apps/orgs/models.py index b244ddec4..5fb2d7ba6 100644 --- a/apps/orgs/models.py +++ b/apps/orgs/models.py @@ -305,7 +305,7 @@ class OrgMemeberManager(models.Manager): model=User, pk_set=_users2pks(users, admins, auditors), using=self.db) send(action='pre_add') - self.bulk_create(oms_add) + self.bulk_create(oms_add, ignore_conflicts=True) send(action='post_add') def _get_remove_add_set(self, new_users, old_users): diff --git a/apps/users/api/user.py b/apps/users/api/user.py index ebf652117..42cfa0f04 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -17,7 +17,7 @@ from common.utils import get_logger from orgs.utils import current_org from orgs.models import ROLE as ORG_ROLE, OrganizationMember from .. import serializers -from ..serializers import UserSerializer, UserRetrieveSerializer, MiniUserSerializer +from ..serializers import UserSerializer, UserRetrieveSerializer, MiniUserSerializer, InviteSerializer from .mixins import UserQuerysetMixin from ..models import User from ..signals import post_user_create @@ -38,7 +38,8 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet): serializer_classes = { 'default': UserSerializer, 'retrieve': UserRetrieveSerializer, - 'suggestion': MiniUserSerializer + 'suggestion': MiniUserSerializer, + 'invite': InviteSerializer, } extra_filter_backends = [OrgRoleUserFilterBackend] @@ -118,7 +119,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet): self.check_object_permissions(self.request, user) return super().perform_bulk_update(serializer) - @action(methods=['get'], detail=False, permission_classes=(IsOrgAdminOrAppUser,)) + @action(methods=['get'], detail=False, permission_classes=(IsOrgAdmin,)) def suggestion(self, request): queryset = User.objects.exclude(role=User.ROLE.APP) queryset = self.filter_queryset(queryset) @@ -132,6 +133,25 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet): serializer = self.get_serializer(queryset, many=True) return Response(serializer.data) + @action(methods=['post'], detail=False, permission_classes=(IsOrgAdmin,)) + def invite(self, request): + data = request.data + if not isinstance(data, list): + data = [request.data] + if not current_org or not current_org.is_real(): + error = {"error": "Not a valid org"} + return Response(error, status=400) + + serializer_cls = self.get_serializer_class() + serializer = serializer_cls(data=data, many=True) + serializer.is_valid(raise_exception=True) + validated_data = serializer.validated_data + for i in validated_data: + i['org_id'] = current_org.org_id() + relations = [OrganizationMember(**i) for i in validated_data] + OrganizationMember.objects.bulk_create(relations, ignore_conflicts=True) + return Response(serializer.data, status=201) + class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView): permission_classes = (IsOrgAdmin,) diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index ccb269350..af2cf596d 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -8,9 +8,8 @@ from rest_framework import serializers from common.utils import validate_ssh_public_key from common.mixins import CommonBulkSerializerMixin from common.permissions import CanUpdateDeleteUser -from common.drf.fields import GroupConcatedPrimaryKeyRelatedField from orgs.models import ROLE as ORG_ROLE -from ..models import User, UserGroup +from ..models import User __all__ = [ @@ -18,7 +17,7 @@ __all__ = [ 'ChangeUserPasswordSerializer', 'ResetOTPSerializer', 'UserProfileSerializer', 'UserOrgSerializer', 'UserUpdatePasswordSerializer', 'UserUpdatePublicKeySerializer', - 'UserRetrieveSerializer', 'MiniUserSerializer', + 'UserRetrieveSerializer', 'MiniUserSerializer', 'InviteSerializer' ] @@ -339,3 +338,10 @@ class MiniUserSerializer(serializers.ModelSerializer): class Meta: model = User fields = ['id', 'name', 'username'] + + +class InviteSerializer(serializers.Serializer): + user = serializers.PrimaryKeyRelatedField( + queryset=User.objects.exclude(role=User.ROLE.APP) + ) + role = serializers.ChoiceField(choices=ORG_ROLE.choices) From cfd0098019640bf07f9a10abcf21a5f53e7eb3ca Mon Sep 17 00:00:00 2001 From: xinwen Date: Wed, 14 Oct 2020 15:40:22 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix(perms):=20=E4=BF=AE=E5=A4=8D=E4=B8=80?= =?UTF-8?q?=E6=AC=A1=E6=80=A7=E8=8E=B7=E5=8F=96=E6=89=80=E6=9C=89=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E4=B8=8E=E8=8A=82=E7=82=B9sql=E6=B3=9B=E6=BB=A5?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/user_permission/user_permission_nodes_with_assets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/perms/api/user_permission/user_permission_nodes_with_assets.py b/apps/perms/api/user_permission/user_permission_nodes_with_assets.py index ae9f00536..3829f0beb 100644 --- a/apps/perms/api/user_permission/user_permission_nodes_with_assets.py +++ b/apps/perms/api/user_permission/user_permission_nodes_with_assets.py @@ -43,6 +43,7 @@ class MyGrantedNodesWithAssetsAsTreeApi(SerializeToTreeNodeMixin, ListAPIView): all_nodes = get_user_granted_nodes_list_via_mapping_node(user) all_assets = get_user_granted_all_assets(user) all_assets = all_assets.annotate(parent_key=F('nodes__key')) + all_assets = all_assets.prefetch_related('platform') data = [ *self.serialize_nodes(all_nodes, with_asset_amount=True),