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/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), 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)