Merge branch 'dev' of github.com:jumpserver/jumpserver into dev

This commit is contained in:
ibuler 2020-10-14 15:48:32 +08:00
commit a69bba8702
9 changed files with 63 additions and 38 deletions

View File

@ -60,6 +60,7 @@ class SerializeToTreeNodeMixin:
'isParent': False, 'isParent': False,
'open': False, 'open': False,
'iconSkin': self.get_platform(asset), 'iconSkin': self.get_platform(asset),
'chkDisabled': not asset.is_active,
'meta': { 'meta': {
'type': 'asset', 'type': 'asset',
'asset': { 'asset': {

View File

@ -9,9 +9,8 @@ from django.db.models.signals import (
from django.db.models import Q, F from django.db.models import Q, F
from django.dispatch import receiver from django.dispatch import receiver
from common.local import thread_local
from common.exceptions import M2MReverseNotAllowed 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.utils import get_logger
from common.decorator import on_transaction_commit from common.decorator import on_transaction_commit
from .models import Asset, SystemUser, Node, compute_parent_key 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) _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) @receiver(pre_delete, sender=Asset)
def on_asset_delete(instance: Asset, **kwargs): def on_asset_delete(instance: Asset, using, **kwargs):
node_keys = Node.objects.filter( node_ids = set(Node.objects.filter(
assets=instance assets=instance
).distinct().values_list('key', flat=True) ).distinct().values_list('id', flat=True))
setattr(instance, RELATED_NODE_IDS, node_ids)
params = { m2m_changed.send(
'node_keys': set(node_keys), sender=Asset.nodes.through, instance=instance, reverse=False,
'asset_pk': instance.id, model=Node, pk_set=node_ids, using=using, action=PRE_REMOVE
'operator': sub )
}
setattr(thread_local, ASSET_DELETE_SIGNAL_FOR_NODE_TREE_PARAMS, params)
@receiver(post_delete, sender=Asset) @receiver(post_delete, sender=Asset)
def on_asset_post_delete(instance: Asset, **kwargs): def on_asset_post_delete(instance: Asset, using, **kwargs):
params = getattr(thread_local, ASSET_DELETE_SIGNAL_FOR_NODE_TREE_PARAMS, None) node_ids = getattr(instance, RELATED_NODE_IDS, None)
if params and params.get('asset_pk') == instance.id: if node_ids:
_update_nodes_asset_amount(**params) m2m_changed.send(
sender=Asset.nodes.through, instance=instance, reverse=False,
model=Node, pk_set=node_ids, using=using, action=POST_REMOVE
)

View File

@ -2,14 +2,12 @@
# #
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db.models import Q from rest_framework import status
from rest_framework import status, generics
from rest_framework.views import Response from rest_framework.views import Response
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
from rest_framework.mixins import CreateModelMixin
from common.permissions import IsSuperUserOrAppUser from common.permissions import IsSuperUserOrAppUser
from common.drf.api import JMSBulkRelationModelViewSet from common.drf.api import JMSBulkRelationModelViewSet, JMSModelViewSet
from .models import Organization, ROLE from .models import Organization, ROLE
from .serializers import ( from .serializers import (
OrgSerializer, OrgReadSerializer, OrgSerializer, OrgReadSerializer,

View File

@ -231,7 +231,7 @@ def _none2list(*args):
def _users2pks(users, admins, auditors): 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): class UserRoleMapper(dict):
@ -295,17 +295,17 @@ class OrgMemeberManager(models.Manager):
) )
oms_add = [] oms_add = []
for users, role in add_mapper: for _users, _role in add_mapper:
for user in users: for _user in _users:
if isinstance(user, models.Model): if isinstance(_user, models.Model):
user = user.id _user = _user.id
oms_add.append(self.model(org_id=org.id, user_id=user, role=role)) 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, send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
model=User, pk_set=_users2pks(users, admins, auditors), using=self.db) model=User, pk_set=_users2pks(users, admins, auditors), using=self.db)
send(action='pre_add') send(action='pre_add')
self.bulk_create(oms_add) self.bulk_create(oms_add, ignore_conflicts=True)
send(action='post_add') send(action='post_add')
def _get_remove_add_set(self, new_users, old_users): def _get_remove_add_set(self, new_users, old_users):

View File

@ -43,6 +43,7 @@ class MyGrantedNodesWithAssetsAsTreeApi(SerializeToTreeNodeMixin, ListAPIView):
all_nodes = get_user_granted_nodes_list_via_mapping_node(user) all_nodes = get_user_granted_nodes_list_via_mapping_node(user)
all_assets = get_user_granted_all_assets(user) all_assets = get_user_granted_all_assets(user)
all_assets = all_assets.annotate(parent_key=F('nodes__key')) all_assets = all_assets.annotate(parent_key=F('nodes__key'))
all_assets = all_assets.prefetch_related('platform')
data = [ data = [
*self.serialize_nodes(all_nodes, with_asset_amount=True), *self.serialize_nodes(all_nodes, with_asset_amount=True),

View File

@ -75,7 +75,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
model = Asset model = Asset
only_fields = [ only_fields = [
"id", "hostname", "ip", "protocols", "os", 'domain', "id", "hostname", "ip", "protocols", "os", 'domain',
"platform", "comment", "org_id", "platform", "comment", "org_id", "is_active"
] ]
fields = only_fields + ['org_name'] fields = only_fields + ['org_name']
read_only_fields = fields read_only_fields = fields

View File

@ -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): def get_asset_system_users_id_with_actions_by_user(user: User, asset: Asset):
queryset = AssetPermission.objects.filter( queryset = AssetPermission.objects.filter(
Q(users=user) | Q(user_groups__users=user) Q(users=user) | Q(user_groups__users=user)
) ).valid()
return get_asset_system_users_id_with_actions(queryset, asset) return get_asset_system_users_id_with_actions(queryset, asset)
def get_asset_system_users_id_with_actions_by_group(group: UserGroup, asset: Asset): def get_asset_system_users_id_with_actions_by_group(group: UserGroup, asset: Asset):
queryset = AssetPermission.objects.filter( queryset = AssetPermission.objects.filter(
user_groups=group user_groups=group
) ).valid()
return get_asset_system_users_id_with_actions(queryset, asset) return get_asset_system_users_id_with_actions(queryset, asset)

View File

@ -17,7 +17,7 @@ from common.utils import get_logger
from orgs.utils import current_org from orgs.utils import current_org
from orgs.models import ROLE as ORG_ROLE, OrganizationMember from orgs.models import ROLE as ORG_ROLE, OrganizationMember
from .. import serializers from .. import serializers
from ..serializers import UserSerializer, UserRetrieveSerializer, MiniUserSerializer from ..serializers import UserSerializer, UserRetrieveSerializer, MiniUserSerializer, InviteSerializer
from .mixins import UserQuerysetMixin from .mixins import UserQuerysetMixin
from ..models import User from ..models import User
from ..signals import post_user_create from ..signals import post_user_create
@ -38,7 +38,8 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
serializer_classes = { serializer_classes = {
'default': UserSerializer, 'default': UserSerializer,
'retrieve': UserRetrieveSerializer, 'retrieve': UserRetrieveSerializer,
'suggestion': MiniUserSerializer 'suggestion': MiniUserSerializer,
'invite': InviteSerializer,
} }
extra_filter_backends = [OrgRoleUserFilterBackend] extra_filter_backends = [OrgRoleUserFilterBackend]
@ -118,7 +119,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
self.check_object_permissions(self.request, user) self.check_object_permissions(self.request, user)
return super().perform_bulk_update(serializer) 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): def suggestion(self, request):
queryset = User.objects.exclude(role=User.ROLE.APP) queryset = User.objects.exclude(role=User.ROLE.APP)
queryset = self.filter_queryset(queryset) queryset = self.filter_queryset(queryset)
@ -132,6 +133,25 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
serializer = self.get_serializer(queryset, many=True) serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data) 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): class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdmin,)

View File

@ -8,9 +8,8 @@ from rest_framework import serializers
from common.utils import validate_ssh_public_key from common.utils import validate_ssh_public_key
from common.mixins import CommonBulkSerializerMixin from common.mixins import CommonBulkSerializerMixin
from common.permissions import CanUpdateDeleteUser from common.permissions import CanUpdateDeleteUser
from common.drf.fields import GroupConcatedPrimaryKeyRelatedField
from orgs.models import ROLE as ORG_ROLE from orgs.models import ROLE as ORG_ROLE
from ..models import User, UserGroup from ..models import User
__all__ = [ __all__ = [
@ -18,7 +17,7 @@ __all__ = [
'ChangeUserPasswordSerializer', 'ResetOTPSerializer', 'ChangeUserPasswordSerializer', 'ResetOTPSerializer',
'UserProfileSerializer', 'UserOrgSerializer', 'UserProfileSerializer', 'UserOrgSerializer',
'UserUpdatePasswordSerializer', 'UserUpdatePublicKeySerializer', 'UserUpdatePasswordSerializer', 'UserUpdatePublicKeySerializer',
'UserRetrieveSerializer', 'MiniUserSerializer', 'UserRetrieveSerializer', 'MiniUserSerializer', 'InviteSerializer'
] ]
@ -339,3 +338,10 @@ class MiniUserSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['id', 'name', 'username'] 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)