mirror of https://github.com/jumpserver/jumpserver
Asset favor (#3352)
* [Update] 拆分filter org * [Update] 修改session支持protocol搜索 * [Bugfix] 修复判断问题 * [Update] 支持收藏资产 * [update] 修改org resource queryset * [Update] 修改form serializer 对应的多对多字段 * [Bugfix] 修复其他组织取消收藏的bug * [Update] 去掉debug信息 * [Update] 修改remote app get queryset * [Update] 修改remote app get queryset * [Update] 修改没有授权时显示情况 * [Bugfix] 修复组织管理员查看用户权限失败问题 * [Update] 优化forms assets queryset设置pull/3355/head^2
parent
ccdb770972
commit
f3dc9b886b
|
@ -1,10 +1,8 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
|
||||
from rest_framework import generics
|
||||
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from ..hands import IsOrgAdmin, IsAppUser
|
||||
from ..models import RemoteApp
|
||||
from ..serializers import RemoteAppSerializer, RemoteAppConnectionInfoSerializer
|
||||
|
@ -16,14 +14,14 @@ __all__ = [
|
|||
|
||||
|
||||
class RemoteAppViewSet(OrgBulkModelViewSet):
|
||||
model = RemoteApp
|
||||
filter_fields = ('name',)
|
||||
search_fields = filter_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
queryset = RemoteApp.objects.all()
|
||||
serializer_class = RemoteAppSerializer
|
||||
|
||||
|
||||
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
|
||||
queryset = RemoteApp.objects.all()
|
||||
model = RemoteApp
|
||||
permission_classes = (IsAppUser, )
|
||||
serializer_class = RemoteAppConnectionInfoSerializer
|
||||
|
|
|
@ -7,3 +7,4 @@ from .domain import *
|
|||
from .cmd_filter import *
|
||||
from .asset_user import *
|
||||
from .gathered_user import *
|
||||
from .favorite_asset import *
|
||||
|
|
|
@ -15,11 +15,10 @@
|
|||
|
||||
from django.db import transaction
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
|
||||
from common.mixins import CommonApiMixin
|
||||
from common.utils import get_logger
|
||||
from ..hands import IsOrgAdmin
|
||||
from ..models import AdminUser, Asset
|
||||
|
@ -39,22 +38,21 @@ class AdminUserViewSet(OrgBulkModelViewSet):
|
|||
"""
|
||||
Admin user api set, for add,delete,update,list,retrieve resource
|
||||
"""
|
||||
|
||||
model = AdminUser
|
||||
filter_fields = ("name", "username")
|
||||
search_fields = filter_fields
|
||||
queryset = AdminUser.objects.all()
|
||||
serializer_class = serializers.AdminUserSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||
queryset = AdminUser.objects.all()
|
||||
model = AdminUser
|
||||
serializer_class = serializers.AdminUserAuthSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
||||
queryset = AdminUser.objects.all()
|
||||
model = AdminUser
|
||||
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
@ -79,7 +77,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
|
|||
"""
|
||||
Test asset admin user assets_connectivity
|
||||
"""
|
||||
queryset = AdminUser.objects.all()
|
||||
model = AdminUser
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.TaskIDSerializer
|
||||
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
import random
|
||||
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from ..models import Asset, AdminUser, Node
|
||||
from orgs.mixins import generics
|
||||
from ..models import Asset, Node
|
||||
from .. import serializers
|
||||
from ..tasks import update_asset_hardware_info_manual, \
|
||||
test_asset_connectivity_manual
|
||||
|
@ -29,10 +29,10 @@ class AssetViewSet(OrgBulkModelViewSet):
|
|||
"""
|
||||
API endpoint that allows Asset to be viewed or edited.
|
||||
"""
|
||||
model = Asset
|
||||
filter_fields = ("hostname", "ip", "systemuser__id", "admin_user__id")
|
||||
search_fields = ("hostname", "ip")
|
||||
ordering_fields = ("hostname", "ip", "port", "cpu_cores")
|
||||
queryset = Asset.objects.all()
|
||||
serializer_class = serializers.AssetSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
extra_filter_backends = [AssetByNodeFilterBackend, LabelFilterBackend]
|
||||
|
@ -57,7 +57,7 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
|||
"""
|
||||
Refresh asset hardware info
|
||||
"""
|
||||
queryset = Asset.objects.all()
|
||||
model = Asset
|
||||
serializer_class = serializers.AssetSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
@ -72,7 +72,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
|||
"""
|
||||
Test asset admin user assets_connectivity
|
||||
"""
|
||||
queryset = Asset.objects.all()
|
||||
model = Asset
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.TaskIDSerializer
|
||||
|
||||
|
@ -84,9 +84,9 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
|||
|
||||
|
||||
class AssetGatewayApi(generics.RetrieveAPIView):
|
||||
queryset = Asset.objects.all()
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.GatewayWithAuthSerializer
|
||||
model = Asset
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
asset_id = kwargs.get('pk')
|
||||
|
@ -98,4 +98,4 @@ class AssetGatewayApi(generics.RetrieveAPIView):
|
|||
serializer = serializers.GatewayWithAuthSerializer(instance=gateway)
|
||||
return Response(serializer.data)
|
||||
else:
|
||||
return Response({"msg": "Not have gateway"}, status=404)
|
||||
return Response({"msg": "Not have gateway"}, status=404)
|
||||
|
|
|
@ -13,14 +13,15 @@ __all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
|||
|
||||
|
||||
class CommandFilterViewSet(OrgBulkModelViewSet):
|
||||
model = CommandFilter
|
||||
filter_fields = ("name",)
|
||||
search_fields = filter_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
queryset = CommandFilter.objects.all()
|
||||
serializer_class = serializers.CommandFilterSerializer
|
||||
|
||||
|
||||
class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
||||
model = CommandFilterRule
|
||||
filter_fields = ("content",)
|
||||
search_fields = filter_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
|
|
@ -15,7 +15,7 @@ __all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
|
|||
|
||||
|
||||
class DomainViewSet(OrgBulkModelViewSet):
|
||||
queryset = Domain.objects.all()
|
||||
model = Domain
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.DomainSerializer
|
||||
|
||||
|
@ -26,16 +26,15 @@ class DomainViewSet(OrgBulkModelViewSet):
|
|||
|
||||
|
||||
class GatewayViewSet(OrgBulkModelViewSet):
|
||||
model = Gateway
|
||||
filter_fields = ("domain__name", "name", "username", "ip", "domain")
|
||||
search_fields = filter_fields
|
||||
queryset = Gateway.objects.all()
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.GatewaySerializer
|
||||
|
||||
|
||||
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
model = Gateway
|
||||
object = None
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
|
||||
from common.permissions import IsValidUser
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from ..models import FavoriteAsset
|
||||
from ..serializers import FavoriteAssetSerializer
|
||||
|
||||
__all__ = ['FavoriteAssetViewSet']
|
||||
|
||||
|
||||
class FavoriteAssetViewSet(BulkModelViewSet):
|
||||
serializer_class = FavoriteAssetSerializer
|
||||
permission_classes = (IsValidUser,)
|
||||
filter_fields = ['asset']
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
with tmp_to_root_org():
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = FavoriteAsset.objects.filter(user=self.request.user)
|
||||
return queryset
|
||||
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
return filtered.count() == 1
|
|
@ -13,12 +13,10 @@ __all__ = ['GatheredUserViewSet']
|
|||
|
||||
|
||||
class GatheredUserViewSet(OrgModelViewSet):
|
||||
queryset = GatheredUser.objects.all()
|
||||
model = GatheredUser
|
||||
serializer_class = GatheredUserSerializer
|
||||
permission_classes = [IsOrgAdmin]
|
||||
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
||||
|
||||
filter_fields = ['asset', 'username', 'present']
|
||||
search_fields = ['username', 'asset__ip', 'asset__hostname']
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ __all__ = ['LabelViewSet']
|
|||
|
||||
|
||||
class LabelViewSet(OrgBulkModelViewSet):
|
||||
model = Label
|
||||
filter_fields = ("name", "value")
|
||||
search_fields = filter_fields
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from rest_framework import generics, status
|
||||
from rest_framework import status
|
||||
from rest_framework.serializers import ValidationError
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
|
@ -23,9 +23,12 @@ from django.shortcuts import get_object_or_404
|
|||
from common.utils import get_logger, get_object_or_none
|
||||
from common.tree import TreeNodeSerializer
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from ..hands import IsOrgAdmin
|
||||
from ..models import Node
|
||||
from ..tasks import update_assets_hardware_info_util, test_asset_connectivity_util
|
||||
from ..tasks import (
|
||||
update_assets_hardware_info_util, test_asset_connectivity_util
|
||||
)
|
||||
from .. import serializers
|
||||
|
||||
|
||||
|
@ -40,9 +43,9 @@ __all__ = [
|
|||
|
||||
|
||||
class NodeViewSet(OrgModelViewSet):
|
||||
model = Node
|
||||
filter_fields = ('value', 'key', 'id')
|
||||
search_fields = ('value', )
|
||||
queryset = Node.objects.all()
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeSerializer
|
||||
|
||||
|
@ -79,6 +82,7 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
|||
}
|
||||
]
|
||||
"""
|
||||
model = Node
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = TreeNodeSerializer
|
||||
|
||||
|
@ -87,10 +91,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
|||
queryset = [node.as_tree_node() for node in queryset]
|
||||
return queryset
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = Node.objects.all()
|
||||
return queryset
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
queryset = super().filter_queryset(queryset)
|
||||
queryset = self.to_tree_queryset(queryset)
|
||||
|
@ -98,7 +98,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
|||
|
||||
|
||||
class NodeChildrenApi(generics.ListCreateAPIView):
|
||||
queryset = Node.objects.all()
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeSerializer
|
||||
instance = None
|
||||
|
@ -162,6 +161,7 @@ class NodeChildrenAsTreeApi(NodeChildrenApi):
|
|||
]
|
||||
|
||||
"""
|
||||
model = Node
|
||||
serializer_class = TreeNodeSerializer
|
||||
http_method_names = ['get']
|
||||
|
||||
|
@ -204,7 +204,7 @@ class NodeAssetsApi(generics.ListAPIView):
|
|||
|
||||
|
||||
class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||
queryset = Node.objects.all()
|
||||
model = Node
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeAddChildrenSerializer
|
||||
instance = None
|
||||
|
@ -221,8 +221,8 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
|
|||
|
||||
|
||||
class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
queryset = Node.objects.all()
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
instance = None
|
||||
|
||||
|
@ -233,8 +233,8 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
|
|||
|
||||
|
||||
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
queryset = Node.objects.all()
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
instance = None
|
||||
|
||||
|
@ -249,8 +249,8 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
|||
|
||||
|
||||
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||
model = Node
|
||||
serializer_class = serializers.NodeAssetsSerializer
|
||||
queryset = Node.objects.all()
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
instance = None
|
||||
|
||||
|
@ -262,8 +262,8 @@ class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
|||
|
||||
|
||||
class RefreshNodeHardwareInfoApi(APIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
model = Node
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
node_id = kwargs.get('pk')
|
||||
|
|
|
@ -14,13 +14,13 @@
|
|||
# limitations under the License.
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework import generics
|
||||
from rest_framework.response import Response
|
||||
|
||||
from common.serializers import CeleryTaskSerializer
|
||||
from common.utils import get_logger
|
||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from ..models import SystemUser, Asset
|
||||
from .. import serializers
|
||||
from ..tasks import (
|
||||
|
@ -43,22 +43,18 @@ class SystemUserViewSet(OrgBulkModelViewSet):
|
|||
"""
|
||||
System user api set, for add,delete,update,list,retrieve resource
|
||||
"""
|
||||
model = SystemUser
|
||||
filter_fields = ("name", "username")
|
||||
search_fields = filter_fields
|
||||
queryset = SystemUser.objects.all()
|
||||
serializer_class = serializers.SystemUserSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset().all()
|
||||
return queryset
|
||||
|
||||
|
||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
Get system user auth info
|
||||
"""
|
||||
queryset = SystemUser.objects.all()
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.SystemUserAuthSerializer
|
||||
|
||||
|
@ -72,7 +68,7 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
|
|||
"""
|
||||
Get system user with asset auth info
|
||||
"""
|
||||
queryset = SystemUser.objects.all()
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.SystemUserAuthSerializer
|
||||
|
||||
|
@ -88,7 +84,7 @@ class SystemUserPushApi(generics.RetrieveAPIView):
|
|||
"""
|
||||
Push system user to cluster assets api
|
||||
"""
|
||||
queryset = SystemUser.objects.all()
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = CeleryTaskSerializer
|
||||
|
||||
|
@ -105,7 +101,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
|
|||
"""
|
||||
Push system user to cluster assets api
|
||||
"""
|
||||
queryset = SystemUser.objects.all()
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = CeleryTaskSerializer
|
||||
|
||||
|
@ -132,7 +128,7 @@ class SystemUserAssetsListView(generics.ListAPIView):
|
|||
|
||||
|
||||
class SystemUserPushToAssetApi(generics.RetrieveAPIView):
|
||||
queryset = SystemUser.objects.all()
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.TaskIDSerializer
|
||||
|
||||
|
@ -145,7 +141,7 @@ class SystemUserPushToAssetApi(generics.RetrieveAPIView):
|
|||
|
||||
|
||||
class SystemUserTestAssetConnectivityApi(generics.RetrieveAPIView):
|
||||
queryset = SystemUser.objects.all()
|
||||
model = SystemUser
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.TaskIDSerializer
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ class AssetUpdateForm(OrgModelForm):
|
|||
class AssetBulkUpdateForm(OrgModelForm):
|
||||
assets = forms.ModelMultipleChoiceField(
|
||||
required=True,
|
||||
label=_('Select assets'), queryset=Asset.objects.all(),
|
||||
label=_('Select assets'), queryset=Asset.objects,
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={
|
||||
'class': 'select2',
|
||||
|
@ -155,11 +155,18 @@ class AssetBulkUpdateForm(OrgModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_fields_queryset()
|
||||
|
||||
# 重写其他字段为不再required
|
||||
for name, field in self.fields.items():
|
||||
if name != 'assets':
|
||||
field.required = False
|
||||
|
||||
def set_fields_queryset(self):
|
||||
assets_field = self.fields['assets']
|
||||
if hasattr(self, 'data'):
|
||||
assets_field.queryset = Asset.objects.all()
|
||||
|
||||
def save(self, commit=True):
|
||||
changed_fields = []
|
||||
for field in self._meta.fields:
|
||||
|
|
|
@ -12,7 +12,7 @@ __all__ = ['DomainForm', 'GatewayForm']
|
|||
|
||||
class DomainForm(forms.ModelForm):
|
||||
assets = forms.ModelMultipleChoiceField(
|
||||
queryset=Asset.objects.all(), label=_('Asset'), required=False,
|
||||
queryset=Asset.objects, label=_('Asset'), required=False,
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
||||
)
|
||||
|
@ -23,19 +23,23 @@ class DomainForm(forms.ModelForm):
|
|||
fields = ['name', 'comment', 'assets']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if kwargs.get('instance', None):
|
||||
initial = kwargs.get('initial', {})
|
||||
initial['assets'] = kwargs['instance'].assets.all()
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_fields_queryset()
|
||||
|
||||
# 前端渲染优化, 防止过多资产
|
||||
def set_fields_queryset(self):
|
||||
assets_field = self.fields.get('assets')
|
||||
|
||||
# 没有data代表是渲染表单, 有data代表是提交创建/更新表单
|
||||
if not self.data:
|
||||
instance = kwargs.get('instance')
|
||||
if instance:
|
||||
assets_field.queryset = instance.assets.all()
|
||||
# 有instance 代表渲染更新表单, 否则是创建表单
|
||||
# 前端渲染优化, 防止过多资产, 设置assets queryset为none
|
||||
if self.instance:
|
||||
assets_field.initial = self.instance.assets.all()
|
||||
assets_field.queryset = self.instance.assets.all()
|
||||
else:
|
||||
assets_field.queryset = Asset.objects.none()
|
||||
else:
|
||||
assets_field.queryset = Asset.objects.all()
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=commit)
|
||||
|
|
|
@ -10,7 +10,7 @@ __all__ = ['LabelForm']
|
|||
|
||||
class LabelForm(forms.ModelForm):
|
||||
assets = forms.ModelMultipleChoiceField(
|
||||
queryset=Asset.objects.all(), label=_('Asset'), required=False,
|
||||
queryset=Asset.objects.none(), label=_('Asset'), required=False,
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
||||
)
|
||||
|
@ -21,19 +21,23 @@ class LabelForm(forms.ModelForm):
|
|||
fields = ['name', 'value', 'assets']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if kwargs.get('instance', None):
|
||||
initial = kwargs.get('initial', {})
|
||||
initial['assets'] = kwargs['instance'].assets.all()
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_fields_queryset()
|
||||
|
||||
# 前端渲染优化, 防止过多资产
|
||||
def set_fields_queryset(self):
|
||||
assets_field = self.fields.get('assets')
|
||||
|
||||
# 没有data代表是渲染表单, 有data代表是提交创建/更新表单
|
||||
if not self.data:
|
||||
instance = kwargs.get('instance')
|
||||
if instance:
|
||||
assets_field.queryset = instance.assets.all()
|
||||
# 有instance 代表渲染更新表单, 否则是创建表单
|
||||
# 前端渲染优化, 防止过多资产, 设置assets queryset为none
|
||||
if self.instance:
|
||||
assets_field.initial = self.instance.assets.all()
|
||||
assets_field.queryset = self.instance.assets.all()
|
||||
else:
|
||||
assets_field.queryset = Asset.objects.none()
|
||||
else:
|
||||
assets_field.queryset = Asset.objects.all()
|
||||
|
||||
def save(self, commit=True):
|
||||
label = super().save(commit=commit)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 2.2.5 on 2019-10-16 08:38
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('assets', '0041_gathereduser'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FavoriteAsset',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('asset', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Asset')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('user', 'asset')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -10,3 +10,4 @@ from .authbook import *
|
|||
from .utils import *
|
||||
from .authbook import *
|
||||
from .gathered_user import *
|
||||
from .favorite_asset import *
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from django.db import models
|
||||
|
||||
from common.mixins.models import CommonModelMixin
|
||||
|
||||
|
||||
__all__ = ['FavoriteAsset']
|
||||
|
||||
|
||||
class FavoriteAsset(CommonModelMixin):
|
||||
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
|
||||
asset = models.ForeignKey('assets.Asset', on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('user', 'asset')
|
||||
|
||||
@classmethod
|
||||
def get_user_favorite_assets_id(cls, user):
|
||||
return cls.objects.filter(user=user).values_list('asset', flat=True)
|
|
@ -324,6 +324,8 @@ class SomeNodesMixin:
|
|||
ungrouped_value = _('ungrouped')
|
||||
empty_key = '-11'
|
||||
empty_value = _("empty")
|
||||
favorite_key = '-12'
|
||||
favorite_value = _("favorite")
|
||||
|
||||
def is_default_node(self):
|
||||
return self.key == self.default_key
|
||||
|
@ -363,7 +365,7 @@ class SomeNodesMixin:
|
|||
@classmethod
|
||||
def ungrouped_node(cls):
|
||||
with tmp_to_org(Organization.system()):
|
||||
defaults = {'value': cls.ungrouped_key}
|
||||
defaults = {'value': cls.ungrouped_value}
|
||||
obj, created = cls.objects.get_or_create(
|
||||
defaults=defaults, key=cls.ungrouped_key
|
||||
)
|
||||
|
@ -387,11 +389,21 @@ class SomeNodesMixin:
|
|||
)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def favorite_node(cls):
|
||||
with tmp_to_org(Organization.system()):
|
||||
defaults = {'value': cls.favorite_value}
|
||||
obj, created = cls.objects.get_or_create(
|
||||
defaults=defaults, key=cls.favorite_key
|
||||
)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def initial_some_nodes(cls):
|
||||
cls.default_node()
|
||||
cls.empty_node()
|
||||
cls.ungrouped_node()
|
||||
cls.favorite_node()
|
||||
|
||||
|
||||
class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
|
||||
|
@ -412,11 +424,11 @@ class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin
|
|||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
def __eq__(self, other):
|
||||
if not other:
|
||||
return False
|
||||
return self.id == other.id
|
||||
|
||||
# def __eq__(self, other):
|
||||
# if not other:
|
||||
# return False
|
||||
# return self.id == other.id
|
||||
#
|
||||
def __gt__(self, other):
|
||||
self_key = [int(k) for k in self.key.split(':')]
|
||||
other_key = [int(k) for k in other.key.split(':')]
|
||||
|
|
|
@ -10,3 +10,4 @@ from .domain import *
|
|||
from .cmd_filter import *
|
||||
from .asset_user import *
|
||||
from .gathered_user import *
|
||||
from .favorite_asset import *
|
||||
|
|
|
@ -45,7 +45,7 @@ class ReplaceNodeAdminUserSerializer(serializers.ModelSerializer):
|
|||
管理用户更新关联到的集群
|
||||
"""
|
||||
nodes = serializers.PrimaryKeyRelatedField(
|
||||
many=True, queryset=Node.objects.all()
|
||||
many=True, queryset=Node.objects
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -79,7 +79,7 @@ class AssetUserAuthInfoSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class AssetUserPushSerializer(serializers.Serializer):
|
||||
asset = serializers.PrimaryKeyRelatedField(queryset=Asset.objects.all(), label=_("Asset"))
|
||||
asset = serializers.PrimaryKeyRelatedField(queryset=Asset.objects, label=_("Asset"))
|
||||
username = serializers.CharField(max_length=1024)
|
||||
|
||||
def create(self, validated_data):
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from orgs.utils import tmp_to_root_org
|
||||
from common.serializers import AdaptedBulkListSerializer
|
||||
from common.mixins import BulkSerializerMixin
|
||||
from ..models import FavoriteAsset
|
||||
|
||||
|
||||
__all__ = ['FavoriteAssetSerializer']
|
||||
|
||||
|
||||
class FavoriteAssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||
user = serializers.HiddenField(
|
||||
default=serializers.CurrentUserDefault()
|
||||
)
|
||||
|
||||
class Meta:
|
||||
list_serializer_class = AdaptedBulkListSerializer
|
||||
model = FavoriteAsset
|
||||
fields = ['user', 'asset']
|
|
@ -38,8 +38,10 @@ class NodeSerializer(BulkOrgResourceModelSerializer):
|
|||
return data
|
||||
|
||||
|
||||
class NodeAssetsSerializer(serializers.ModelSerializer):
|
||||
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
|
||||
class NodeAssetsSerializer(BulkOrgResourceModelSerializer):
|
||||
assets = serializers.PrimaryKeyRelatedField(
|
||||
many=True, queryset=Asset.objects
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Node
|
||||
|
|
|
@ -25,12 +25,20 @@
|
|||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% include 'assets/_asset_list_modal.html' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
$("#id_assets").parent().find(".select2-selection").on('click', function (e) {
|
||||
if ($(e.target).attr('class') !== 'select2-selection__choice__remove'){
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
$("#asset_list_modal").modal();
|
||||
}
|
||||
})
|
||||
}).on('click', '.field-tag', function() {
|
||||
changeField(this);
|
||||
}).on('click', '#change_all', function () {
|
||||
|
|
|
@ -21,19 +21,46 @@
|
|||
|
||||
{% block custom_foot_js %}
|
||||
<script>
|
||||
var treeUrl = "{% url 'api-perms:my-nodes-children-as-tree' %}?&cache_policy=1";
|
||||
var treeUrl = "{% url 'api-perms:my-nodes-children-as-tree' %}?cache_policy=1";
|
||||
var assetTableUrl = "{% url 'api-perms:my-assets' %}?cache_policy=1";
|
||||
var selectUrl = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}?cache_policy=1&all=1';
|
||||
var systemUsersUrl = "{% url 'api-perms:my-asset-system-users' asset_id=DEFAULT_PK %}?cache_policy=1";
|
||||
var showAssetHref = false; // Need input default true
|
||||
|
||||
var favoriteAssets = [];
|
||||
var favorBtnTmpl = '<a class="btn btn-xs btn-default btn-favor" data-id="ID"><i class="fa fa-star-o"></i></a>';
|
||||
var disfavorBtnTmpl = '<a class="btn btn-xs btn-default btn-disfavor" data-id="ID"><i class="fa fa-star"></i></a>';
|
||||
var actions = {
|
||||
targets: 4, createdCell: function (td, cellData) {
|
||||
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +
|
||||
'" class="btn btn-xs btn-primary" target="_blank">{% trans "Connect" %}</a>';
|
||||
$(td).html(conn_btn)
|
||||
var connBtn = '<a href="{% url "luna-view" %}?login_to=' + cellData +
|
||||
'" class="btn btn-xs btn-primary" target="_blank"><i class="fa fa-terminal"></i></a> ';
|
||||
var favorBtn = favorBtnTmpl.replace("ID", cellData);
|
||||
var disfavorBtn = disfavorBtnTmpl.replace("ID", cellData);
|
||||
|
||||
var btn = connBtn;
|
||||
if (favoriteAssets.indexOf(cellData) === -1) {
|
||||
btn += favorBtn
|
||||
} else {
|
||||
btn += disfavorBtn;
|
||||
}
|
||||
|
||||
$(td).html(btn)
|
||||
}};
|
||||
$(document).ready(function () {
|
||||
initTree();
|
||||
requestApi({
|
||||
method: "GET",
|
||||
url: "{% url 'api-assets:favorite-asset-list' %}",
|
||||
success: function (data) {
|
||||
favoriteAssets = data.map(function (i) {
|
||||
return i.asset;
|
||||
});
|
||||
initTree();
|
||||
},
|
||||
error: function () {
|
||||
initTree();
|
||||
},
|
||||
flash_message: false
|
||||
})
|
||||
}).on('click', '.labels li', function () {
|
||||
var val = $(this).text();
|
||||
$("#user_assets_table_filter input").val(val);
|
||||
|
@ -67,22 +94,33 @@ $(document).ready(function () {
|
|||
};
|
||||
$('#asset_detail_tbody').html(trs)
|
||||
$('#user_asset_detail_modal').modal();
|
||||
})
|
||||
.on('click', '.btn-favor', function () {
|
||||
var $this = $(this);
|
||||
var assetId = $(this).data("id");
|
||||
requestApi({
|
||||
url: "{% url 'api-assets:favorite-asset-list' %}",
|
||||
method: "POST",
|
||||
body: JSON.stringify({asset: assetId}),
|
||||
flash_message: false,
|
||||
success: function (data) {
|
||||
var btn = disfavorBtnTmpl.replace("ID", assetId);
|
||||
$this.replaceWith(btn)
|
||||
}
|
||||
});
|
||||
})
|
||||
.on('click', '.btn-disfavor', function () {
|
||||
var $this = $(this);
|
||||
var assetId = $(this).data("id");
|
||||
requestApi({
|
||||
url: "{% url 'api-assets:favorite-asset-list' %}?asset=" + assetId,
|
||||
method: "DELETE",
|
||||
flash_message: false,
|
||||
success: function (data) {
|
||||
var btn = favorBtnTmpl.replace("ID", assetId);
|
||||
$this.replaceWith(btn)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function toggle() {
|
||||
if (show === 0) {
|
||||
$("#split-left").hide(500, function () {
|
||||
$("#split-right").attr("class", "col-lg-12");
|
||||
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
|
||||
show = 1;
|
||||
});
|
||||
} else {
|
||||
$("#split-right").attr("class", "col-lg-9");
|
||||
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
|
||||
$("#split-left").show(500);
|
||||
show = 0;
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -22,6 +22,7 @@ router.register(r'cmd-filters', api.CommandFilterViewSet, 'cmd-filter')
|
|||
router.register(r'asset-users', api.AssetUserViewSet, 'asset-user')
|
||||
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
|
||||
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
||||
router.register(r'favorite-assets', api.FavoriteAssetViewSet, 'favorite-asset')
|
||||
|
||||
cmd_filter_router = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
|
||||
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from rest_framework import viewsets
|
||||
|
||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from .models import FTPLog
|
||||
from .serializers import FTPLogSerializer
|
||||
|
||||
|
||||
class FTPLogViewSet(viewsets.ModelViewSet):
|
||||
queryset = FTPLog.objects.all()
|
||||
class FTPLogViewSet(OrgModelViewSet):
|
||||
model = FTPLog
|
||||
serializer_class = FTPLogSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
||||
|
||||
|
|
|
@ -83,8 +83,6 @@ class LogTailApi(generics.RetrieveAPIView):
|
|||
return Response({"data": data, 'end': end, 'mark': new_mark})
|
||||
|
||||
|
||||
|
||||
|
||||
class ResourcesIDCacheApi(APIView):
|
||||
def post(self, request, *args, **kwargs):
|
||||
spm = str(uuid.uuid4())
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
import uuid
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
__all__ = ["NoDeleteManager", "NoDeleteModelMixin", "NoDeleteQuerySet"]
|
||||
__all__ = [
|
||||
"NoDeleteManager", "NoDeleteModelMixin", "NoDeleteQuerySet",
|
||||
"CommonModelMixin"
|
||||
]
|
||||
|
||||
|
||||
class NoDeleteQuerySet(models.query.QuerySet):
|
||||
|
@ -40,3 +43,13 @@ class NoDeleteModelMixin(models.Model):
|
|||
self.is_discard = True
|
||||
self.discard_time = timezone.now()
|
||||
return self.save()
|
||||
|
||||
|
||||
class CommonModelMixin(models.Model):
|
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
|
||||
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
|
||||
date_updated = models.DateTimeField(auto_now=True, verbose_name=_('Date updated'))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
|
@ -36,7 +36,7 @@ def on_request_finished_logging_db_query(sender, **kwargs):
|
|||
queries = connection.queries
|
||||
counters = defaultdict(Counter)
|
||||
for query in queries:
|
||||
if not query['sql'].startswith('SELECT'):
|
||||
if not query['sql'] or not query['sql'].startswith('SELECT'):
|
||||
continue
|
||||
tables = pattern.findall(query['sql'])
|
||||
table_name = ''.join(tables)
|
||||
|
|
|
@ -51,6 +51,8 @@ class TreeNode:
|
|||
result = True
|
||||
elif self.pId != other.pId:
|
||||
result = self.pId > other.pId
|
||||
elif self.id.startswith('-') and not other.id.startswith('-'):
|
||||
result = False
|
||||
else:
|
||||
result = self.name > other.name
|
||||
return result
|
||||
|
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: Jumpserver 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2019-10-15 17:37+0800\n"
|
||||
"POT-Creation-Date: 2019-10-17 16:09+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
|
||||
|
@ -193,11 +193,11 @@ msgstr "参数"
|
|||
#: assets/templates/assets/cmd_filter_detail.html:77
|
||||
#: assets/templates/assets/domain_detail.html:72
|
||||
#: assets/templates/assets/system_user_detail.html:100
|
||||
#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:16
|
||||
#: perms/models/base.py:54
|
||||
#: common/mixins/models.py:50 ops/templates/ops/adhoc_detail.html:86
|
||||
#: orgs/models.py:16 perms/models/base.py:54
|
||||
#: perms/templates/perms/asset_permission_detail.html:98
|
||||
#: perms/templates/perms/remote_app_permission_detail.html:90
|
||||
#: users/models/user.py:414 users/serializers/v1.py:141
|
||||
#: users/models/user.py:414 users/serializers/v1.py:143
|
||||
#: users/templates/users/user_detail.html:111
|
||||
#: xpack/plugins/change_auth_plan/models.py:108
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
|
||||
|
@ -216,7 +216,8 @@ msgstr "创建者"
|
|||
#: assets/models/label.py:25 assets/templates/assets/admin_user_detail.html:64
|
||||
#: assets/templates/assets/cmd_filter_detail.html:69
|
||||
#: assets/templates/assets/domain_detail.html:68
|
||||
#: assets/templates/assets/system_user_detail.html:96 ops/models/adhoc.py:45
|
||||
#: assets/templates/assets/system_user_detail.html:96
|
||||
#: common/mixins/models.py:51 ops/models/adhoc.py:45
|
||||
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64
|
||||
#: orgs/models.py:17 perms/models/base.py:55
|
||||
#: perms/templates/perms/asset_permission_detail.html:94
|
||||
|
@ -321,7 +322,6 @@ msgstr "远程应用"
|
|||
#: xpack/plugins/cloud/templates/cloud/sync_instance_task_create_update.html:53
|
||||
#: xpack/plugins/gathered_user/templates/gathered_user/task_create_update.html:44
|
||||
#: xpack/plugins/interface/templates/interface/interface.html:72
|
||||
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
|
||||
#: xpack/plugins/vault/templates/vault/vault_create.html:45
|
||||
msgid "Reset"
|
||||
msgstr "重置"
|
||||
|
@ -524,7 +524,7 @@ msgstr "创建远程应用"
|
|||
#: settings/templates/settings/terminal_setting.html:107
|
||||
#: terminal/templates/terminal/session_list.html:36
|
||||
#: terminal/templates/terminal/terminal_list.html:36
|
||||
#: users/templates/users/_granted_assets.html:29
|
||||
#: users/templates/users/_granted_assets.html:34
|
||||
#: users/templates/users/user_group_list.html:38
|
||||
#: users/templates/users/user_list.html:41
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:60
|
||||
|
@ -539,7 +539,6 @@ msgid "Action"
|
|||
msgstr "动作"
|
||||
|
||||
#: applications/templates/applications/user_remote_app_list.html:52
|
||||
#: assets/templates/assets/user_asset_list.html:32
|
||||
#: perms/models/asset_permission.py:32
|
||||
msgid "Connect"
|
||||
msgstr "连接"
|
||||
|
@ -566,11 +565,11 @@ msgstr "远程应用详情"
|
|||
msgid "My RemoteApp"
|
||||
msgstr "我的远程应用"
|
||||
|
||||
#: assets/api/node.py:58
|
||||
#: assets/api/node.py:61
|
||||
msgid "You can't update the root node name"
|
||||
msgstr "不能修改根节点名称"
|
||||
|
||||
#: assets/api/node.py:65
|
||||
#: assets/api/node.py:68
|
||||
msgid "Deletion failed and the node contains children or assets"
|
||||
msgstr "删除失败,节点包含子节点或资产"
|
||||
|
||||
|
@ -628,13 +627,13 @@ msgstr "标签"
|
|||
#: assets/forms/asset.py:65 assets/forms/asset.py:112
|
||||
#: assets/models/asset.py:144 assets/models/domain.py:26
|
||||
#: assets/models/domain.py:52 assets/templates/assets/asset_detail.html:78
|
||||
#: assets/templates/assets/user_asset_list.html:53
|
||||
#: assets/templates/assets/user_asset_list.html:80
|
||||
#: xpack/plugins/orgs/templates/orgs/org_list.html:18
|
||||
msgid "Domain"
|
||||
msgstr "网域"
|
||||
|
||||
#: assets/forms/asset.py:69 assets/forms/asset.py:103 assets/forms/asset.py:116
|
||||
#: assets/forms/asset.py:152 assets/models/node.py:409
|
||||
#: assets/forms/asset.py:152 assets/models/node.py:421
|
||||
#: assets/templates/assets/asset_create.html:42
|
||||
#: perms/forms/asset_permission.py:83 perms/forms/asset_permission.py:90
|
||||
#: perms/templates/perms/asset_permission_list.html:53
|
||||
|
@ -702,7 +701,7 @@ msgstr "SSH网关,支持代理SSH,RDP和VNC"
|
|||
#: ops/models/adhoc.py:189 perms/templates/perms/asset_permission_list.html:70
|
||||
#: perms/templates/perms/asset_permission_user.html:55
|
||||
#: perms/templates/perms/remote_app_permission_user.html:54
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:14
|
||||
#: settings/templates/settings/_ldap_list_users_modal.html:30 users/forms.py:13
|
||||
#: users/models/user.py:371 users/templates/users/_select_user_modal.html:14
|
||||
#: users/templates/users/user_detail.html:67
|
||||
#: users/templates/users/user_list.html:36
|
||||
|
@ -729,7 +728,7 @@ msgstr "密码或密钥密码"
|
|||
#: authentication/forms.py:15
|
||||
#: authentication/templates/authentication/login.html:68
|
||||
#: authentication/templates/authentication/new_login.html:95
|
||||
#: settings/forms.py:114 users/forms.py:16 users/forms.py:28
|
||||
#: settings/forms.py:114 users/forms.py:15 users/forms.py:27
|
||||
#: users/templates/users/reset_password.html:53
|
||||
#: users/templates/users/user_password_authentication.html:18
|
||||
#: users/templates/users/user_password_update.html:44
|
||||
|
@ -790,10 +789,10 @@ msgstr "使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig"
|
|||
#: assets/templates/assets/asset_detail.html:62
|
||||
#: assets/templates/assets/asset_list.html:97
|
||||
#: assets/templates/assets/domain_gateway_list.html:68
|
||||
#: assets/templates/assets/user_asset_list.html:49
|
||||
#: assets/templates/assets/user_asset_list.html:76
|
||||
#: audits/templates/audits/login_log_list.html:60
|
||||
#: perms/templates/perms/asset_permission_asset.html:58 settings/forms.py:144
|
||||
#: users/templates/users/_granted_assets.html:26
|
||||
#: users/templates/users/_granted_assets.html:31
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:54
|
||||
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:73
|
||||
msgid "IP"
|
||||
|
@ -807,10 +806,10 @@ msgstr "IP"
|
|||
#: assets/templates/assets/_asset_user_list.html:19
|
||||
#: assets/templates/assets/asset_detail.html:58
|
||||
#: assets/templates/assets/asset_list.html:96
|
||||
#: assets/templates/assets/user_asset_list.html:48
|
||||
#: assets/templates/assets/user_asset_list.html:75
|
||||
#: perms/templates/perms/asset_permission_asset.html:57
|
||||
#: perms/templates/perms/asset_permission_list.html:73 settings/forms.py:143
|
||||
#: users/templates/users/_granted_assets.html:25
|
||||
#: users/templates/users/_granted_assets.html:30
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_asset_list.html:53
|
||||
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:72
|
||||
msgid "Hostname"
|
||||
|
@ -822,18 +821,19 @@ msgstr "主机名"
|
|||
#: assets/templates/assets/system_user_detail.html:70
|
||||
#: assets/templates/assets/system_user_list.html:53
|
||||
#: terminal/templates/terminal/session_list.html:31
|
||||
#: terminal/templates/terminal/session_list.html:75
|
||||
msgid "Protocol"
|
||||
msgstr "协议"
|
||||
|
||||
#: assets/models/asset.py:142 assets/serializers/asset.py:68
|
||||
#: assets/templates/assets/asset_create.html:24
|
||||
#: assets/templates/assets/user_asset_list.html:50
|
||||
#: assets/templates/assets/user_asset_list.html:77
|
||||
#: perms/serializers/user_permission.py:48
|
||||
msgid "Protocols"
|
||||
msgstr "协议组"
|
||||
|
||||
#: assets/models/asset.py:143 assets/templates/assets/asset_detail.html:102
|
||||
#: assets/templates/assets/user_asset_list.html:51
|
||||
#: assets/templates/assets/user_asset_list.html:78
|
||||
msgid "Platform"
|
||||
msgstr "系统平台"
|
||||
|
||||
|
@ -940,7 +940,8 @@ msgid "SSH public key"
|
|||
msgstr "ssh公钥"
|
||||
|
||||
#: assets/models/base.py:35 assets/models/gathered_user.py:21
|
||||
#: assets/templates/assets/cmd_filter_detail.html:73 ops/models/adhoc.py:46
|
||||
#: assets/templates/assets/cmd_filter_detail.html:73 common/mixins/models.py:52
|
||||
#: ops/models/adhoc.py:46
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:109
|
||||
#: xpack/plugins/gathered_user/templates/gathered_user/gathered_user_list.html:76
|
||||
msgid "Date updated"
|
||||
|
@ -1107,9 +1108,9 @@ msgstr "默认资产组"
|
|||
#: terminal/models.py:156 terminal/templates/terminal/command_list.html:29
|
||||
#: terminal/templates/terminal/command_list.html:65
|
||||
#: terminal/templates/terminal/session_list.html:27
|
||||
#: terminal/templates/terminal/session_list.html:71 users/forms.py:312
|
||||
#: terminal/templates/terminal/session_list.html:71 users/forms.py:319
|
||||
#: users/models/user.py:127 users/models/user.py:143 users/models/user.py:500
|
||||
#: users/serializers/v1.py:130 users/templates/users/user_group_detail.html:78
|
||||
#: users/serializers/v1.py:132 users/templates/users/user_group_detail.html:78
|
||||
#: users/templates/users/user_group_list.html:36 users/views/user.py:250
|
||||
#: xpack/plugins/orgs/forms.py:28
|
||||
#: xpack/plugins/orgs/templates/orgs/org_detail.html:113
|
||||
|
@ -1117,7 +1118,7 @@ msgstr "默认资产组"
|
|||
msgid "User"
|
||||
msgstr "用户"
|
||||
|
||||
#: assets/models/label.py:19 assets/models/node.py:400
|
||||
#: assets/models/label.py:19 assets/models/node.py:412
|
||||
#: assets/templates/assets/label_list.html:15 settings/models.py:30
|
||||
msgid "Value"
|
||||
msgstr "值"
|
||||
|
@ -1138,7 +1139,11 @@ msgstr "未分组"
|
|||
msgid "empty"
|
||||
msgstr "空"
|
||||
|
||||
#: assets/models/node.py:399
|
||||
#: assets/models/node.py:328
|
||||
msgid "favorite"
|
||||
msgstr "收藏夹"
|
||||
|
||||
#: assets/models/node.py:411
|
||||
msgid "Key"
|
||||
msgstr "键"
|
||||
|
||||
|
@ -1192,7 +1197,7 @@ msgstr "Shell"
|
|||
msgid "Login mode"
|
||||
msgstr "登录模式"
|
||||
|
||||
#: assets/models/user.py:162 assets/templates/assets/user_asset_list.html:52
|
||||
#: assets/models/user.py:162 assets/templates/assets/user_asset_list.html:79
|
||||
#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:52
|
||||
#: audits/templates/audits/ftp_log_list.html:75
|
||||
#: perms/forms/asset_permission.py:86 perms/forms/remote_app_permission.py:43
|
||||
|
@ -1208,7 +1213,7 @@ msgstr "登录模式"
|
|||
#: terminal/templates/terminal/command_list.html:67
|
||||
#: terminal/templates/terminal/session_list.html:29
|
||||
#: terminal/templates/terminal/session_list.html:73
|
||||
#: users/templates/users/_granted_assets.html:27
|
||||
#: users/templates/users/_granted_assets.html:32
|
||||
#: xpack/plugins/orgs/templates/orgs/org_list.html:20
|
||||
msgid "System user"
|
||||
msgstr "系统用户"
|
||||
|
@ -1258,7 +1263,7 @@ msgstr "组织名称"
|
|||
msgid "Backend"
|
||||
msgstr "后端"
|
||||
|
||||
#: assets/serializers/asset_user.py:67 users/forms.py:263
|
||||
#: assets/serializers/asset_user.py:67 users/forms.py:262
|
||||
#: users/models/user.py:403 users/templates/users/first_login.html:42
|
||||
#: users/templates/users/user_password_update.html:49
|
||||
#: users/templates/users/user_profile.html:69
|
||||
|
@ -1872,6 +1877,7 @@ msgstr "删除选择资产"
|
|||
#: users/templates/users/user_group_list.html:118
|
||||
#: users/templates/users/user_list.html:254
|
||||
#: xpack/plugins/interface/templates/interface/interface.html:101
|
||||
#: xpack/plugins/orgs/templates/orgs/org_create_update.html:33
|
||||
msgid "Cancel"
|
||||
msgstr "取消"
|
||||
|
||||
|
@ -2257,7 +2263,7 @@ msgstr "Agent"
|
|||
|
||||
#: audits/models.py:85 audits/templates/audits/login_log_list.html:62
|
||||
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
||||
#: users/forms.py:175 users/models/user.py:395
|
||||
#: users/forms.py:174 users/models/user.py:395
|
||||
#: users/templates/users/first_login.html:45
|
||||
msgid "MFA"
|
||||
msgstr "MFA"
|
||||
|
@ -2480,7 +2486,7 @@ msgid ""
|
|||
"after {} minutes)"
|
||||
msgstr "账号已被锁定(请联系管理员解锁 或 {}分钟后重试)"
|
||||
|
||||
#: authentication/forms.py:66 users/forms.py:22
|
||||
#: authentication/forms.py:66 users/forms.py:21
|
||||
msgid "MFA code"
|
||||
msgstr "MFA 验证码"
|
||||
|
||||
|
@ -2505,7 +2511,7 @@ msgid "Secret"
|
|||
msgstr "密文"
|
||||
|
||||
#: authentication/templates/authentication/_access_key_modal.html:48
|
||||
#: users/templates/users/_granted_assets.html:75
|
||||
#: users/templates/users/_granted_assets.html:80
|
||||
msgid "Show"
|
||||
msgstr "显示"
|
||||
|
||||
|
@ -2713,11 +2719,11 @@ msgstr ""
|
|||
msgid "Encrypt field using Secret Key"
|
||||
msgstr ""
|
||||
|
||||
#: common/mixins/models.py:31
|
||||
#: common/mixins/models.py:34
|
||||
msgid "is discard"
|
||||
msgstr ""
|
||||
|
||||
#: common/mixins/models.py:32
|
||||
#: common/mixins/models.py:35
|
||||
msgid "discard time"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3105,7 +3111,7 @@ msgstr "命令执行列表"
|
|||
msgid "Command execution"
|
||||
msgstr "命令执行"
|
||||
|
||||
#: orgs/mixins/models.py:61 orgs/mixins/serializers.py:26 orgs/models.py:31
|
||||
#: orgs/mixins/models.py:58 orgs/mixins/serializers.py:26 orgs/models.py:31
|
||||
msgid "Organization"
|
||||
msgstr "组织"
|
||||
|
||||
|
@ -3122,7 +3128,7 @@ msgstr "空"
|
|||
#: perms/templates/perms/asset_permission_list.html:71
|
||||
#: perms/templates/perms/asset_permission_list.html:118
|
||||
#: perms/templates/perms/remote_app_permission_list.html:16
|
||||
#: templates/_nav.html:21 users/forms.py:286 users/models/group.py:26
|
||||
#: templates/_nav.html:21 users/forms.py:293 users/models/group.py:26
|
||||
#: users/models/user.py:379 users/templates/users/_select_user_modal.html:16
|
||||
#: users/templates/users/user_detail.html:218
|
||||
#: users/templates/users/user_list.html:38
|
||||
|
@ -3969,7 +3975,7 @@ msgid "Commercial support"
|
|||
msgstr "商业支持"
|
||||
|
||||
#: templates/_header_bar.html:70 templates/_nav.html:30
|
||||
#: templates/_nav_user.html:32 users/forms.py:154
|
||||
#: templates/_nav_user.html:32 users/forms.py:153
|
||||
#: users/templates/users/_user.html:43
|
||||
#: users/templates/users/first_login.html:39
|
||||
#: users/templates/users/user_password_update.html:40
|
||||
|
@ -4390,7 +4396,7 @@ msgstr "线程数"
|
|||
msgid "Boot Time"
|
||||
msgstr "运行时间"
|
||||
|
||||
#: terminal/models.py:162 terminal/templates/terminal/session_list.html:136
|
||||
#: terminal/models.py:162 terminal/templates/terminal/session_list.html:137
|
||||
msgid "Replay"
|
||||
msgstr "回放"
|
||||
|
||||
|
@ -4456,15 +4462,15 @@ msgstr "终断所选"
|
|||
msgid "Confirm finished"
|
||||
msgstr "确认已完成"
|
||||
|
||||
#: terminal/templates/terminal/session_list.html:91
|
||||
#: terminal/templates/terminal/session_list.html:92
|
||||
msgid "Terminate task send, waiting ..."
|
||||
msgstr "终断任务已发送,请等待"
|
||||
|
||||
#: terminal/templates/terminal/session_list.html:142
|
||||
#: terminal/templates/terminal/session_list.html:143
|
||||
msgid "Terminate"
|
||||
msgstr "终断"
|
||||
|
||||
#: terminal/templates/terminal/session_list.html:173
|
||||
#: terminal/templates/terminal/session_list.html:174
|
||||
msgid "Finish session success"
|
||||
msgstr "标记会话完成成功"
|
||||
|
||||
|
@ -4534,7 +4540,7 @@ msgstr "你可以使用ssh客户端工具连接终端"
|
|||
msgid "Could not reset self otp, use profile reset instead"
|
||||
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
|
||||
|
||||
#: users/forms.py:33 users/models/user.py:383
|
||||
#: users/forms.py:32 users/models/user.py:383
|
||||
#: users/templates/users/_select_user_modal.html:15
|
||||
#: users/templates/users/user_detail.html:87
|
||||
#: users/templates/users/user_list.html:37
|
||||
|
@ -4542,44 +4548,44 @@ msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
|
|||
msgid "Role"
|
||||
msgstr "角色"
|
||||
|
||||
#: users/forms.py:36 users/forms.py:233
|
||||
#: users/forms.py:35 users/forms.py:232
|
||||
#: users/templates/users/user_update.html:30
|
||||
msgid "ssh public key"
|
||||
msgstr "ssh公钥"
|
||||
|
||||
#: users/forms.py:37 users/forms.py:234
|
||||
#: users/forms.py:36 users/forms.py:233
|
||||
msgid "ssh-rsa AAAA..."
|
||||
msgstr ""
|
||||
|
||||
#: users/forms.py:38
|
||||
#: users/forms.py:37
|
||||
msgid "Paste user id_rsa.pub here."
|
||||
msgstr "复制用户公钥到这里"
|
||||
|
||||
#: users/forms.py:52 users/templates/users/user_detail.html:226
|
||||
#: users/forms.py:51 users/templates/users/user_detail.html:226
|
||||
msgid "Join user groups"
|
||||
msgstr "添加到用户组"
|
||||
|
||||
#: users/forms.py:87 users/forms.py:248
|
||||
#: users/forms.py:86 users/forms.py:247
|
||||
msgid "Public key should not be the same as your old one."
|
||||
msgstr "不能和原来的密钥相同"
|
||||
|
||||
#: users/forms.py:91 users/forms.py:252 users/serializers/v1.py:116
|
||||
#: users/forms.py:90 users/forms.py:251 users/serializers/v1.py:116
|
||||
msgid "Not a valid ssh public key"
|
||||
msgstr "ssh密钥不合法"
|
||||
|
||||
#: users/forms.py:104 users/views/login.py:114 users/views/user.py:287
|
||||
#: users/forms.py:103 users/views/login.py:114 users/views/user.py:287
|
||||
msgid "* Your password does not meet the requirements"
|
||||
msgstr "* 您的密码不符合要求"
|
||||
|
||||
#: users/forms.py:125
|
||||
#: users/forms.py:124
|
||||
msgid "Reset link will be generated and sent to the user"
|
||||
msgstr "生成重置密码链接,通过邮件发送给用户"
|
||||
|
||||
#: users/forms.py:126
|
||||
#: users/forms.py:125
|
||||
msgid "Set password"
|
||||
msgstr "设置密码"
|
||||
|
||||
#: users/forms.py:133 xpack/plugins/change_auth_plan/models.py:88
|
||||
#: users/forms.py:132 xpack/plugins/change_auth_plan/models.py:88
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_create_update.html:51
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:69
|
||||
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_execution_list.html:57
|
||||
|
@ -4587,7 +4593,7 @@ msgstr "设置密码"
|
|||
msgid "Password strategy"
|
||||
msgstr "密码策略"
|
||||
|
||||
#: users/forms.py:160
|
||||
#: users/forms.py:159
|
||||
msgid ""
|
||||
"When enabled, you will enter the MFA binding process the next time you log "
|
||||
"in. you can also directly bind in \"personal information -> quick "
|
||||
|
@ -4596,11 +4602,11 @@ msgstr ""
|
|||
"启用之后您将会在下次登录时进入MFA绑定流程;您也可以在(个人信息->快速修改->更"
|
||||
"改MFA设置)中直接绑定!"
|
||||
|
||||
#: users/forms.py:170
|
||||
#: users/forms.py:169
|
||||
msgid "* Enable MFA authentication to make the account more secure."
|
||||
msgstr "* 启用MFA认证,使账号更加安全。"
|
||||
|
||||
#: users/forms.py:180
|
||||
#: users/forms.py:179
|
||||
msgid ""
|
||||
"In order to protect you and your company, please keep your account, password "
|
||||
"and key sensitive information properly. (for example: setting complex "
|
||||
|
@ -4609,41 +4615,41 @@ msgstr ""
|
|||
"为了保护您和公司的安全,请妥善保管您的账户、密码和密钥等重要敏感信息;(如:"
|
||||
"设置复杂密码,启用MFA认证)"
|
||||
|
||||
#: users/forms.py:187 users/templates/users/first_login.html:48
|
||||
#: users/forms.py:186 users/templates/users/first_login.html:48
|
||||
#: users/templates/users/first_login.html:110
|
||||
#: users/templates/users/first_login.html:139
|
||||
msgid "Finish"
|
||||
msgstr "完成"
|
||||
|
||||
#: users/forms.py:193
|
||||
#: users/forms.py:192
|
||||
msgid "Old password"
|
||||
msgstr "原来密码"
|
||||
|
||||
#: users/forms.py:198
|
||||
#: users/forms.py:197
|
||||
msgid "New password"
|
||||
msgstr "新密码"
|
||||
|
||||
#: users/forms.py:203
|
||||
#: users/forms.py:202
|
||||
msgid "Confirm password"
|
||||
msgstr "确认密码"
|
||||
|
||||
#: users/forms.py:213
|
||||
#: users/forms.py:212
|
||||
msgid "Old password error"
|
||||
msgstr "原来密码错误"
|
||||
|
||||
#: users/forms.py:221
|
||||
#: users/forms.py:220
|
||||
msgid "Password does not match"
|
||||
msgstr "密码不一致"
|
||||
|
||||
#: users/forms.py:231
|
||||
#: users/forms.py:230
|
||||
msgid "Automatically configure and download the SSH key"
|
||||
msgstr "自动配置并下载SSH密钥"
|
||||
|
||||
#: users/forms.py:235
|
||||
#: users/forms.py:234
|
||||
msgid "Paste your id_rsa.pub here."
|
||||
msgstr "复制你的公钥到这里"
|
||||
|
||||
#: users/forms.py:269 users/forms.py:274 users/forms.py:316
|
||||
#: users/forms.py:268 users/forms.py:273 users/forms.py:323
|
||||
#: xpack/plugins/orgs/forms.py:18
|
||||
msgid "Select users"
|
||||
msgstr "选择用户"
|
||||
|
@ -4736,7 +4742,7 @@ msgstr "角色只能为 {}"
|
|||
msgid "Password does not match security rules"
|
||||
msgstr "密码不满足安全规则"
|
||||
|
||||
#: users/serializers/v1.py:147
|
||||
#: users/serializers/v1.py:157
|
||||
msgid "Auditors cannot be join in the user group"
|
||||
msgstr "审计员不能被加入到用户组"
|
||||
|
||||
|
@ -5933,7 +5939,7 @@ msgstr "更新同步实例任务"
|
|||
#: xpack/plugins/gathered_user/views.py:21
|
||||
#: xpack/plugins/gathered_user/views.py:34
|
||||
#: xpack/plugins/gathered_user/views.py:49
|
||||
#: xpack/plugins/gathered_user/views.py:66
|
||||
#: xpack/plugins/gathered_user/views.py:69
|
||||
msgid "Gathered user"
|
||||
msgstr "收集用户"
|
||||
|
||||
|
@ -5968,7 +5974,7 @@ msgstr "创建任务"
|
|||
msgid "Gathered user list"
|
||||
msgstr "收集用户列表"
|
||||
|
||||
#: xpack/plugins/gathered_user/views.py:67
|
||||
#: xpack/plugins/gathered_user/views.py:70
|
||||
msgid "Update task"
|
||||
msgstr "更新任务"
|
||||
|
||||
|
|
|
@ -5,12 +5,12 @@ from rest_framework.viewsets import ModelViewSet
|
|||
from rest_framework_bulk import BulkModelViewSet
|
||||
from common.mixins import CommonApiMixin
|
||||
|
||||
from ..utils import set_to_root_org
|
||||
from ..utils import set_to_root_org, filter_org_queryset
|
||||
from ..models import Organization
|
||||
|
||||
__all__ = [
|
||||
'RootOrgViewMixin', 'OrgMembershipModelViewSetMixin', 'OrgModelViewSet',
|
||||
'OrgBulkModelViewSet',
|
||||
'OrgBulkModelViewSet', 'OrgQuerySetMixin',
|
||||
]
|
||||
|
||||
|
||||
|
@ -22,7 +22,15 @@ class RootOrgViewMixin:
|
|||
|
||||
class OrgQuerySetMixin:
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset().all()
|
||||
if hasattr(self, 'model'):
|
||||
queryset = self.model.objects.all()
|
||||
else:
|
||||
assert self.queryset is None, (
|
||||
"'%s' should not include a `queryset` attribute"
|
||||
% self.__class__.__name__
|
||||
)
|
||||
queryset = super().get_queryset()
|
||||
|
||||
if hasattr(self, 'swagger_fake_view'):
|
||||
return queryset[:1]
|
||||
if hasattr(self, 'action') and self.action == 'list' and \
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
from rest_framework import generics
|
||||
|
||||
from .api import OrgQuerySetMixin
|
||||
|
||||
|
||||
class ListAPIView(OrgQuerySetMixin, generics.ListAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class RetrieveAPIView(OrgQuerySetMixin, generics.RetrieveAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class CreateAPIView(OrgQuerySetMixin, generics.CreateAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class DestroyAPIView(OrgQuerySetMixin, generics.DestroyAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class ListCreateAPIView(OrgQuerySetMixin, generics.ListCreateAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class UpdateAPIView(OrgQuerySetMixin, generics.UpdateAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class RetrieveUpdateAPIView(OrgQuerySetMixin, generics.RetrieveUpdateAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class RetrieveDestroyAPIView(OrgQuerySetMixin, generics.RetrieveDestroyAPIView):
|
||||
pass
|
||||
|
||||
|
||||
class RetrieveUpdateDestroyAPIView(OrgQuerySetMixin, generics.RetrieveUpdateDestroyAPIView):
|
||||
pass
|
|
@ -9,6 +9,7 @@ from django.core.exceptions import ValidationError
|
|||
from common.utils import get_logger
|
||||
from ..utils import (
|
||||
set_current_org, get_current_org, current_org,
|
||||
get_org_filters
|
||||
)
|
||||
from ..models import Organization
|
||||
|
||||
|
@ -19,42 +20,38 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class OrgQuerySet(models.QuerySet):
|
||||
pass
|
||||
|
||||
|
||||
class OrgManager(models.Manager):
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super(OrgManager, self).get_queryset()
|
||||
kwargs = {}
|
||||
|
||||
_current_org = get_current_org()
|
||||
if _current_org is None:
|
||||
kwargs['id'] = None
|
||||
elif _current_org.is_real():
|
||||
kwargs['org_id'] = _current_org.id
|
||||
elif _current_org.is_default():
|
||||
queryset = queryset.filter(org_id="")
|
||||
#
|
||||
# lines = traceback.format_stack()
|
||||
# print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
||||
# for line in lines[-10:-1]:
|
||||
# print(line)
|
||||
# print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
||||
|
||||
queryset = queryset.filter(**kwargs)
|
||||
queryset = super().get_queryset()
|
||||
kwargs = get_org_filters()
|
||||
if kwargs:
|
||||
return queryset.filter(**kwargs)
|
||||
return queryset
|
||||
|
||||
def all(self):
|
||||
if not current_org:
|
||||
msg = 'You can `objects.set_current_org(org).all()` then run it'
|
||||
return self
|
||||
else:
|
||||
return super(OrgManager, self).all()
|
||||
|
||||
def set_current_org(self, org):
|
||||
if isinstance(org, str):
|
||||
org = Organization.get_instance(org)
|
||||
set_current_org(org)
|
||||
return self
|
||||
|
||||
def all(self):
|
||||
# print("Call all: {}".format(current_org))
|
||||
#
|
||||
# lines = traceback.format_stack()
|
||||
# print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
||||
# for line in lines[-10:-1]:
|
||||
# print(line)
|
||||
# print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
||||
if not current_org:
|
||||
msg = 'You can `objects.set_current_org(org).all()` then run it'
|
||||
return self
|
||||
else:
|
||||
return super().all()
|
||||
|
||||
|
||||
class OrgModelMixin(models.Model):
|
||||
org_id = models.CharField(max_length=36, blank=True, default='',
|
||||
|
@ -65,9 +62,12 @@ class OrgModelMixin(models.Model):
|
|||
|
||||
def save(self, *args, **kwargs):
|
||||
org = get_current_org()
|
||||
if org is not None and (org.is_real() or org.is_system()):
|
||||
if org is None:
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
if org.is_real() or org.is_system():
|
||||
self.org_id = org.id
|
||||
elif org is not None and org.is_default():
|
||||
elif org.is_default():
|
||||
self.org_id = ''
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import traceback
|
||||
from werkzeug.local import LocalProxy
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
@ -82,4 +83,31 @@ def tmp_to_org(org):
|
|||
set_current_org(ori_org)
|
||||
|
||||
|
||||
def get_org_filters():
|
||||
kwargs = {}
|
||||
|
||||
_current_org = get_current_org()
|
||||
if _current_org is None:
|
||||
return kwargs
|
||||
|
||||
if _current_org.is_real():
|
||||
kwargs['org_id'] = _current_org.id
|
||||
elif _current_org.is_default():
|
||||
kwargs["org_id"] = ''
|
||||
return kwargs
|
||||
|
||||
|
||||
def filter_org_queryset(queryset):
|
||||
kwargs = get_org_filters()
|
||||
|
||||
#
|
||||
# lines = traceback.format_stack()
|
||||
# print(">>>>>>>>>>>>>>>>>>>>>>>>>>>>")
|
||||
# for line in lines[-10:-1]:
|
||||
# print(line)
|
||||
# print("<<<<<<<<<<<<<<<<<<<<<<<<<<<<")
|
||||
queryset = queryset.filter(**kwargs)
|
||||
return queryset
|
||||
|
||||
|
||||
current_org = LocalProxy(get_current_org)
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.utils import timezone
|
||||
from django.db.models import Q
|
||||
from rest_framework.views import Response
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView
|
||||
from rest_framework import viewsets
|
||||
|
||||
from common.permissions import IsOrgAdmin
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from common.utils import get_object_or_none
|
||||
from ..models import AssetPermission
|
||||
from ..hands import (
|
||||
|
@ -24,15 +23,21 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||
class AssetPermissionViewSet(OrgModelViewSet):
|
||||
"""
|
||||
资产授权列表的增删改查api
|
||||
"""
|
||||
queryset = AssetPermission.objects.all()
|
||||
model = AssetPermission
|
||||
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
||||
filter_fields = ['name']
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset().prefetch_related(
|
||||
"nodes", "assets", "users", "user_groups", "system_users"
|
||||
)
|
||||
return queryset
|
||||
|
||||
def get_serializer_class(self):
|
||||
if self.action in ("list", 'retrieve') and \
|
||||
self.request.query_params.get("display"):
|
||||
|
@ -160,19 +165,14 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
|||
queryset = queryset.distinct()
|
||||
return queryset
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.all().prefetch_related(
|
||||
"nodes", "assets", "users", "user_groups", "system_users"
|
||||
)
|
||||
|
||||
|
||||
class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
|
||||
class AssetPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
||||
"""
|
||||
将用户从授权中移除,Detail页面会调用
|
||||
"""
|
||||
model = AssetPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
||||
queryset = AssetPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -187,10 +187,10 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
|
|||
return Response({"error": serializer.errors})
|
||||
|
||||
|
||||
class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
|
||||
class AssetPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
||||
model = AssetPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
||||
queryset = AssetPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -205,13 +205,13 @@ class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
|
|||
return Response({"error": serializer.errors})
|
||||
|
||||
|
||||
class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
|
||||
class AssetPermissionRemoveAssetApi(generics.RetrieveUpdateAPIView):
|
||||
"""
|
||||
将用户从授权中移除,Detail页面会调用
|
||||
"""
|
||||
model = AssetPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
||||
queryset = AssetPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -226,10 +226,10 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
|
|||
return Response({"error": serializer.errors})
|
||||
|
||||
|
||||
class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
|
||||
class AssetPermissionAddAssetApi(generics.RetrieveUpdateAPIView):
|
||||
model = AssetPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
||||
queryset = AssetPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -244,7 +244,7 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
|
|||
return Response({"error": serializer.errors})
|
||||
|
||||
|
||||
class AssetPermissionAssetsApi(ListAPIView):
|
||||
class AssetPermissionAssetsApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.AssetPermissionAssetsSerializer
|
||||
filter_fields = ("hostname", "ip")
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
from rest_framework.generics import get_object_or_404
|
||||
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
||||
from common.utils import get_logger
|
||||
from orgs.utils import set_to_root_org
|
||||
from orgs.utils import set_to_root_org, get_current_org, set_current_org, tmp_to_root_org
|
||||
from ..hands import User, UserGroup
|
||||
|
||||
|
||||
|
@ -17,15 +17,24 @@ __all__ = [
|
|||
|
||||
class UserPermissionMixin:
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
current_org = None
|
||||
obj = None
|
||||
|
||||
def initial(self, *args, **kwargs):
|
||||
super().initial(*args, *kwargs)
|
||||
self.current_org = get_current_org()
|
||||
set_to_root_org()
|
||||
self.obj = self.get_obj()
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
set_to_root_org()
|
||||
return super().get(request, *args, **kwargs)
|
||||
# def dispatch(self, request, *args, **kwargs):
|
||||
# """不能这么做,校验权限时拿不到组织了"""
|
||||
# with tmp_to_root_org():
|
||||
# return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
# def get(self, request, *args, **kwargs):
|
||||
# """有的api重写了get方法"""
|
||||
# with tmp_to_root_org():
|
||||
# return super().get(request, *args, **kwargs)
|
||||
|
||||
def get_obj(self):
|
||||
user_id = self.kwargs.get('pk', '')
|
||||
|
@ -40,6 +49,13 @@ class UserPermissionMixin:
|
|||
self.permission_classes = (IsValidUser,)
|
||||
return super().get_permissions()
|
||||
|
||||
def finalize_response(self, request, response, *args, **kwargs):
|
||||
response = super().finalize_response(request, response, *args, **kwargs)
|
||||
org = getattr(self, 'current_org', None)
|
||||
if org:
|
||||
set_current_org(org)
|
||||
return response
|
||||
|
||||
|
||||
class UserGroupPermissionMixin:
|
||||
obj = None
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
# coding: utf-8
|
||||
#
|
||||
|
||||
from rest_framework import viewsets, generics
|
||||
from rest_framework.views import Response
|
||||
|
||||
from common.permissions import IsOrgAdmin
|
||||
from orgs.mixins.api import OrgModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from ..models import RemoteAppPermission
|
||||
from ..serializers import (
|
||||
RemoteAppPermissionSerializer,
|
||||
|
@ -20,18 +21,18 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class RemoteAppPermissionViewSet(viewsets.ModelViewSet):
|
||||
class RemoteAppPermissionViewSet(OrgModelViewSet):
|
||||
model = RemoteAppPermission
|
||||
filter_fields = ('name', )
|
||||
search_fields = filter_fields
|
||||
queryset = RemoteAppPermission.objects.all()
|
||||
serializer_class = RemoteAppPermissionSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
||||
model = RemoteAppPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = RemoteAppPermissionUpdateUserSerializer
|
||||
queryset = RemoteAppPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -46,9 +47,9 @@ class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
|||
|
||||
|
||||
class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
||||
model = RemoteAppPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = RemoteAppPermissionUpdateUserSerializer
|
||||
queryset = RemoteAppPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -63,9 +64,9 @@ class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
|||
|
||||
|
||||
class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
|
||||
model = RemoteAppPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
|
||||
queryset = RemoteAppPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
@ -80,9 +81,9 @@ class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
|
|||
|
||||
|
||||
class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView):
|
||||
model = RemoteAppPermission
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
|
||||
queryset = RemoteAppPermission.objects.all()
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
perm = self.get_object()
|
||||
|
|
|
@ -34,7 +34,7 @@ class UserNodeTreeMixin:
|
|||
|
||||
for node in nodes:
|
||||
assets_amount = self.tree.valid_assets_amount(node.key)
|
||||
if assets_amount == 0 and node.key != Node.empty_key:
|
||||
if assets_amount == 0 and not node.key.startswith('-'):
|
||||
continue
|
||||
node.assets_amount = assets_amount
|
||||
data = ParserNode.parse_node_to_tree_node(node)
|
||||
|
|
|
@ -3,12 +3,10 @@
|
|||
import uuid
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.views import APIView, Response
|
||||
from rest_framework.generics import (
|
||||
ListAPIView, get_object_or_404,
|
||||
)
|
||||
|
||||
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
||||
from common.tree import TreeNodeSerializer
|
||||
from orgs.mixins import generics
|
||||
from ..utils import (
|
||||
RemoteAppPermissionUtil, construct_remote_apps_tree_root,
|
||||
parse_remote_app_to_tree_node,
|
||||
|
@ -25,7 +23,7 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class UserGrantedRemoteAppsApi(ListAPIView):
|
||||
class UserGrantedRemoteAppsApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = RemoteAppSerializer
|
||||
filter_fields = ['name', 'id']
|
||||
|
@ -68,7 +66,7 @@ class UserGrantedRemoteAppsAsTreeApi(UserGrantedRemoteAppsApi):
|
|||
return super().get_serializer(data, many=True)
|
||||
|
||||
|
||||
class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, ListAPIView):
|
||||
class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser,)
|
||||
serializer_class = serializers.RemoteAppSystemUserSerializer
|
||||
only_fields = serializers.RemoteAppSystemUserSerializer.Meta.only_fields
|
||||
|
@ -110,7 +108,7 @@ class ValidateUserRemoteAppPermissionApi(APIView):
|
|||
|
||||
# RemoteApp permission
|
||||
|
||||
class UserGroupGrantedRemoteAppsApi(ListAPIView):
|
||||
class UserGroupGrantedRemoteAppsApi(generics.ListAPIView):
|
||||
permission_classes = (IsOrgAdminOrAppUser, )
|
||||
serializer_class = RemoteAppSerializer
|
||||
|
||||
|
|
|
@ -2,10 +2,15 @@
|
|||
#
|
||||
|
||||
from users.models import User, UserGroup
|
||||
from assets.models import Asset, SystemUser, Node, Label
|
||||
from assets.models import Asset, SystemUser, Node, Label, FavoriteAsset
|
||||
from assets.serializers import NodeSerializer
|
||||
from applications.serializers import RemoteAppSerializer
|
||||
from applications.models import RemoteApp
|
||||
|
||||
|
||||
__all__ = [
|
||||
'User', 'UserGroup',
|
||||
'Asset', 'SystemUser', 'Node', 'Label', 'FavoriteAsset',
|
||||
'NodeSerializer', 'RemoteAppSerializer',
|
||||
'RemoteApp'
|
||||
]
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ from common.utils import get_logger, timeit, lazyproperty
|
|||
from common.tree import TreeNode
|
||||
from assets.utils import TreeService
|
||||
from ..models import AssetPermission
|
||||
from ..hands import Node, Asset, SystemUser
|
||||
from ..hands import Node, Asset, SystemUser, User, FavoriteAsset
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
@ -293,6 +293,20 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
|
|||
parent=user_tree.root,
|
||||
)
|
||||
|
||||
def add_favorite_node_if_need(self, user_tree):
|
||||
if not isinstance(self.object, User):
|
||||
return
|
||||
node_key = Node.favorite_key
|
||||
node_value = Node.favorite_value
|
||||
user_tree.create_node(
|
||||
identifier=node_key, tag=node_value,
|
||||
parent=user_tree.root,
|
||||
)
|
||||
assets_id = FavoriteAsset.get_user_favorite_assets_id(self.object)
|
||||
all_valid_assets = user_tree.all_valid_assets(user_tree.root)
|
||||
valid_assets_id = set(assets_id) & all_valid_assets
|
||||
user_tree.set_assets(node_key, valid_assets_id)
|
||||
|
||||
def set_user_tree_to_local(self, user_tree):
|
||||
self._user_tree = user_tree
|
||||
self._user_tree_filter_id = self._filter_id
|
||||
|
@ -323,6 +337,7 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
|
|||
self.add_single_assets_node_to_user_tree(user_tree)
|
||||
self.parse_user_tree_to_full_tree(user_tree)
|
||||
self.add_empty_node_if_need(user_tree)
|
||||
self.add_favorite_node_if_need(user_tree)
|
||||
self.set_user_tree_to_cache_if_need(user_tree)
|
||||
self.set_user_tree_to_local(user_tree)
|
||||
return user_tree
|
||||
|
|
|
@ -267,7 +267,7 @@ function requestApi(props) {
|
|||
$.ajax({
|
||||
url: props.url,
|
||||
type: props.method || "PATCH",
|
||||
data: props.body,
|
||||
data: props.body || props.data,
|
||||
contentType: props.content_type || "application/json; charset=utf-8",
|
||||
dataType: props.data_type || "json"
|
||||
}).done(function (data, textStatue, jqXHR) {
|
||||
|
@ -579,6 +579,9 @@ jumpserver.initServerSideDataTable = function (options) {
|
|||
ajax: {
|
||||
url: options.ajax_url,
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
if (jqXHR.responseText && jqXHR.responseText.indexOf("%(value)s") !== -1 ) {
|
||||
return
|
||||
}
|
||||
var msg = gettext("Unknown error occur");
|
||||
if (jqXHR.responseJSON) {
|
||||
if (jqXHR.responseJSON.error) {
|
||||
|
@ -953,8 +956,13 @@ function initPopover($container, $progress, $idPassword, $el, password_check_rul
|
|||
function rootNodeAddDom(ztree, callback) {
|
||||
var refreshIcon = "<a id='tree-refresh'><i class='fa fa-refresh'></i></a>";
|
||||
var rootNode = ztree.getNodes()[0];
|
||||
var $rootNodeRef = $("#" + rootNode.tId + "_a");
|
||||
$rootNodeRef.after(refreshIcon);
|
||||
if (rootNode) {
|
||||
var $rootNodeRef = $("#" + rootNode.tId + "_a");
|
||||
$rootNodeRef.after(refreshIcon);
|
||||
} else {
|
||||
$rootNodeRef = $('#' + ztree.setting.treeId);
|
||||
$rootNodeRef.html(refreshIcon);
|
||||
}
|
||||
var refreshIconRef = $('#tree-refresh');
|
||||
refreshIconRef.bind('click', function () {
|
||||
ztree.destroy();
|
||||
|
|
|
@ -24,10 +24,10 @@ logger = get_logger(__name__)
|
|||
|
||||
|
||||
class SessionViewSet(OrgBulkModelViewSet):
|
||||
queryset = Session.objects.all()
|
||||
model = Session
|
||||
serializer_class = serializers.SessionSerializer
|
||||
permission_classes = (IsOrgAdminOrAppUser, )
|
||||
filter_fields = [
|
||||
filterset_fields = [
|
||||
"user", "asset", "system_user", "remote_addr",
|
||||
"protocol", "terminal", "is_finished",
|
||||
]
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
<li><a class="search-item" data-value="asset">{% trans 'Asset' %}</a></li>
|
||||
<li><a class="search-item" data-value="system_user">{% trans 'System user' %}</a></li>
|
||||
<li><a class="search-item" data-value="remote_addr">{% trans 'Remote addr' %}</a></li>
|
||||
<li><a class="search-item" data-value="protocol">{% trans 'Protocol' %}</a></li>
|
||||
{# <li><a class="search-item" data-value="protocol">{% trans 'Protocol' %}</a></li>#}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from rest_framework import generics
|
||||
|
||||
from ..serializers import (
|
||||
UserGroupSerializer,
|
||||
UserGroupListSerializer,
|
||||
|
@ -10,6 +8,7 @@ from ..serializers import (
|
|||
)
|
||||
from ..models import UserGroup
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from common.permissions import IsOrgAdmin
|
||||
|
||||
|
||||
|
@ -17,9 +16,9 @@ __all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi']
|
|||
|
||||
|
||||
class UserGroupViewSet(OrgBulkModelViewSet):
|
||||
model = UserGroup
|
||||
filter_fields = ("name",)
|
||||
search_fields = filter_fields
|
||||
queryset = UserGroup.objects.all()
|
||||
serializer_class = UserGroupSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
@ -31,6 +30,6 @@ class UserGroupViewSet(OrgBulkModelViewSet):
|
|||
|
||||
|
||||
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
||||
queryset = UserGroup.objects.all()
|
||||
model = UserGroup
|
||||
serializer_class = UserGroupUpdateMemberSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
|
|
@ -17,7 +17,7 @@ from common.permissions import (
|
|||
from common.mixins import CommonApiMixin
|
||||
from common.utils import get_logger
|
||||
from orgs.utils import current_org
|
||||
from .. import serializers
|
||||
from .. import serializers, utils
|
||||
from ..models import User
|
||||
from ..signals import post_user_create
|
||||
|
||||
|
@ -30,13 +30,21 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
class UserViewSet(CommonApiMixin, BulkModelViewSet):
|
||||
class UserQuerysetMixin:
|
||||
def get_queryset(self):
|
||||
queryset = utils.get_current_org_members()
|
||||
return queryset
|
||||
|
||||
|
||||
class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
||||
filter_fields = ('username', 'email', 'name', 'id')
|
||||
search_fields = filter_fields
|
||||
queryset = User.objects.exclude(role=User.ROLE_APP)
|
||||
serializer_class = serializers.UserSerializer
|
||||
permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().prefetch_related('groups')
|
||||
|
||||
def send_created_signal(self, users):
|
||||
if not isinstance(users, list):
|
||||
users = [users]
|
||||
|
@ -51,11 +59,6 @@ class UserViewSet(CommonApiMixin, BulkModelViewSet):
|
|||
current_org.users.add(*users)
|
||||
self.send_created_signal(users)
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = current_org.get_org_members()\
|
||||
.prefetch_related('groups')
|
||||
return queryset
|
||||
|
||||
def get_permissions(self):
|
||||
if self.action in ["retrieve", "list"]:
|
||||
self.permission_classes = (IsOrgAdminOrAppUser,)
|
||||
|
@ -79,9 +82,8 @@ class UserViewSet(CommonApiMixin, BulkModelViewSet):
|
|||
return super().perform_bulk_update(serializer)
|
||||
|
||||
|
||||
class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
|
||||
class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.ChangeUserPasswordSerializer
|
||||
|
||||
def perform_update(self, serializer):
|
||||
|
@ -90,13 +92,12 @@ class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
|
|||
user.save()
|
||||
|
||||
|
||||
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
class UserUpdateGroupApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
|
||||
serializer_class = serializers.UserUpdateGroupSerializer
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
|
||||
|
||||
class UserResetPasswordApi(generics.UpdateAPIView):
|
||||
class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = serializers.UserSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
@ -111,8 +112,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
|
|||
send_reset_password_mail(user)
|
||||
|
||||
|
||||
class UserResetPKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||
serializer_class = serializers.UserSerializer
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
|
@ -125,8 +125,7 @@ class UserResetPKApi(generics.UpdateAPIView):
|
|||
|
||||
|
||||
# 废弃
|
||||
class UserUpdatePKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
class UserUpdatePKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||
serializer_class = serializers.UserPKUpdateSerializer
|
||||
permission_classes = (IsCurrentUserOrReadOnly,)
|
||||
|
||||
|
@ -136,8 +135,7 @@ class UserUpdatePKApi(generics.UpdateAPIView):
|
|||
user.save()
|
||||
|
||||
|
||||
class UserUnblockPKApi(generics.UpdateAPIView):
|
||||
queryset = User.objects.all()
|
||||
class UserUnblockPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.UserSerializer
|
||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||
|
@ -165,8 +163,7 @@ class UserProfileApi(generics.RetrieveAPIView):
|
|||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
|
||||
class UserResetOTPApi(generics.RetrieveAPIView):
|
||||
queryset = User.objects.all()
|
||||
class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView):
|
||||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.ResetOTPSerializer
|
||||
|
||||
|
|
|
@ -5,9 +5,8 @@ from django.utils.translation import gettext_lazy as _
|
|||
|
||||
from common.utils import validate_ssh_public_key
|
||||
from orgs.mixins.forms import OrgModelForm
|
||||
from orgs.utils import current_org
|
||||
from .models import User, UserGroup
|
||||
from .utils import check_password_rules
|
||||
from .utils import check_password_rules, get_current_org_members
|
||||
|
||||
|
||||
class UserCheckPasswordForm(forms.Form):
|
||||
|
@ -267,15 +266,23 @@ class UserBulkUpdateForm(OrgModelForm):
|
|||
users = forms.ModelMultipleChoiceField(
|
||||
required=True,
|
||||
label=_('Select users'),
|
||||
queryset=User.objects.all(),
|
||||
queryset=User.objects.none(),
|
||||
widget=forms.SelectMultiple(
|
||||
attrs={
|
||||
'class': 'select2',
|
||||
'class': 'users-select2',
|
||||
'data-placeholder': _('Select users')
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_fields_queryset()
|
||||
|
||||
def set_fields_queryset(self):
|
||||
users_field = self.fields['users']
|
||||
users_field.queryset = get_current_org_members()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ['users', 'groups', 'date_expired']
|
||||
|
@ -320,25 +327,19 @@ class UserGroupForm(OrgModelForm):
|
|||
)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
instance = kwargs.get('instance')
|
||||
if instance:
|
||||
initial = kwargs.get('initial', {})
|
||||
initial.update({'users': instance.users.all()})
|
||||
kwargs['initial'] = initial
|
||||
super().__init__(**kwargs)
|
||||
if 'initial' not in kwargs:
|
||||
return
|
||||
self.set_fields_queryset()
|
||||
|
||||
def set_fields_queryset(self):
|
||||
users_field = self.fields.get('users')
|
||||
if instance:
|
||||
users_field.queryset = instance.users.all()
|
||||
if self.instance:
|
||||
users_field.initial = self.instance.users.all()
|
||||
users_field.queryset = self.instance.users.all()
|
||||
else:
|
||||
users_field.queryset = User.objects.none()
|
||||
|
||||
def save(self, commit=True):
|
||||
group = super().save(commit=commit)
|
||||
users = self.cleaned_data['users']
|
||||
group.users.set(users)
|
||||
return group
|
||||
raise Exception("Save by restful api")
|
||||
|
||||
class Meta:
|
||||
model = UserGroup
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import copy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from rest_framework import serializers
|
||||
|
@ -12,6 +11,7 @@ from common.serializers import AdaptedBulkListSerializer
|
|||
from common.permissions import CanUpdateDeleteUser
|
||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||
from ..models import User, UserGroup
|
||||
from .. import utils
|
||||
|
||||
|
||||
__all__ = [
|
||||
|
@ -118,7 +118,9 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class UserUpdateGroupSerializer(serializers.ModelSerializer):
|
||||
groups = serializers.PrimaryKeyRelatedField(many=True, queryset=UserGroup.objects.all())
|
||||
groups = serializers.PrimaryKeyRelatedField(
|
||||
many=True, queryset=UserGroup.objects
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
|
@ -127,7 +129,7 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer):
|
|||
|
||||
class UserGroupSerializer(BulkOrgResourceModelSerializer):
|
||||
users = serializers.PrimaryKeyRelatedField(
|
||||
required=False, many=True, queryset=User.objects.all(), label=_('User')
|
||||
required=False, many=True, queryset=User.objects, label=_('User')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -141,6 +143,14 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer):
|
|||
'created_by': {'label': _('Created by'), 'read_only': True}
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_fields_queryset()
|
||||
|
||||
def set_fields_queryset(self):
|
||||
users_field = self.fields['users']
|
||||
users_field.child_relation.queryset = utils.get_current_org_members()
|
||||
|
||||
def validate_users(self, users):
|
||||
for user in users:
|
||||
if user.is_super_auditor:
|
||||
|
@ -154,12 +164,20 @@ class UserGroupListSerializer(UserGroupSerializer):
|
|||
|
||||
|
||||
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
|
||||
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all())
|
||||
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects)
|
||||
|
||||
class Meta:
|
||||
model = UserGroup
|
||||
fields = ['id', 'users']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.set_fields_queryset()
|
||||
|
||||
def set_fields_queryset(self):
|
||||
users_field = self.fields['users']
|
||||
users_field.child_relation.queryset = utils.get_current_org_members()
|
||||
|
||||
|
||||
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{% load i18n %}
|
||||
<div class="col-lg-3" style="padding-left: 0px">
|
||||
<div class="col-lg-3" style="padding-left: 0" id="split-left">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
||||
<div class="file-manager ">
|
||||
|
@ -11,7 +11,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-9 animated fadeInRight">
|
||||
<div class="col-lg-9 animated fadeInRight" id="split-right">
|
||||
<div class="tree-toggle">
|
||||
<div class="btn btn-sm btn-primary tree-toggle-btn" onclick="toggle()">
|
||||
<i class="fa fa-angle-left fa-x" id="toggle-icon"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mail-box-header">
|
||||
{# <div class="btn-group" style="float: right">#}
|
||||
{# <button data-toggle="dropdown" class="btn btn-default btn-sm labels dropdown-toggle">{% trans 'Label' %} <span class="caret"></span></button>#}
|
||||
|
@ -174,6 +179,27 @@ function loadLabels() {
|
|||
}
|
||||
}
|
||||
|
||||
var show = 0;
|
||||
|
||||
function toggle() {
|
||||
if (show === 0) {
|
||||
$("#split-left").hide(500, function () {
|
||||
$("#split-right").attr("class", "col-lg-12");
|
||||
$("#toggle-icon").attr("class", "fa fa-angle-right fa-x");
|
||||
show = 1;
|
||||
});
|
||||
} else {
|
||||
$("#split-right").attr("class", "col-lg-9");
|
||||
$("#toggle-icon").attr("class", "fa fa-angle-left fa-x");
|
||||
$("#split-left").show(500);
|
||||
show = 0;
|
||||
}
|
||||
setTimeout(function () {
|
||||
$(".table").css("width", "100%");
|
||||
{#assetTable.columns.adjust();#}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
{#loadLabels()#}
|
||||
}).on('click', '.labels-menu li', function () {
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.select2').select2();
|
||||
usersSelect2Init('.users-select2')
|
||||
}).on('click', '.field-tag', function() {
|
||||
changeField(this);
|
||||
}).on('click', '#change_all', function () {
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
{% block custom_foot_js %}
|
||||
<script>
|
||||
var assetTableUrl = "{% url 'api-perms:user-group-assets' pk=object.id %}?cache_policy=1";
|
||||
var selectUrl = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}?&cache_policy=1&all=1';
|
||||
var selectUrl = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}?cache_policy=1&all=1';
|
||||
var treeUrl = "{% url 'api-perms:user-group-nodes-children-as-tree' pk=object.id %}?cache_policy=1";
|
||||
var systemUsersUrl = "{% url 'api-perms:user-group-asset-system-users' pk=object.id asset_id=DEFAULT_PK %}?cache_policy=1";
|
||||
var showAssetHref = true; // Need input default true
|
||||
|
|
|
@ -325,3 +325,7 @@ def construct_user_email(username, email):
|
|||
email = '{}@{}'.format(username, settings.EMAIL_SUFFIX)
|
||||
return email
|
||||
|
||||
|
||||
def get_current_org_members(exclude=()):
|
||||
from orgs.utils import current_org
|
||||
return current_org.get_org_members(exclude=exclude)
|
||||
|
|
Loading…
Reference in New Issue