mirror of https://github.com/jumpserver/jumpserver
commit
20f8c12576
|
@ -1,10 +1,8 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
from rest_framework import generics
|
|
||||||
|
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from ..hands import IsOrgAdmin, IsAppUser
|
from ..hands import IsOrgAdmin, IsAppUser
|
||||||
from ..models import RemoteApp
|
from ..models import RemoteApp
|
||||||
from ..serializers import RemoteAppSerializer, RemoteAppConnectionInfoSerializer
|
from ..serializers import RemoteAppSerializer, RemoteAppConnectionInfoSerializer
|
||||||
|
@ -16,14 +14,14 @@ __all__ = [
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppViewSet(OrgBulkModelViewSet):
|
class RemoteAppViewSet(OrgBulkModelViewSet):
|
||||||
|
model = RemoteApp
|
||||||
filter_fields = ('name',)
|
filter_fields = ('name',)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
queryset = RemoteApp.objects.all()
|
|
||||||
serializer_class = RemoteAppSerializer
|
serializer_class = RemoteAppSerializer
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
|
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
|
||||||
queryset = RemoteApp.objects.all()
|
model = RemoteApp
|
||||||
permission_classes = (IsAppUser, )
|
permission_classes = (IsAppUser, )
|
||||||
serializer_class = RemoteAppConnectionInfoSerializer
|
serializer_class = RemoteAppConnectionInfoSerializer
|
||||||
|
|
|
@ -7,3 +7,4 @@ from .domain import *
|
||||||
from .cmd_filter import *
|
from .cmd_filter import *
|
||||||
from .asset_user import *
|
from .asset_user import *
|
||||||
from .gathered_user import *
|
from .gathered_user import *
|
||||||
|
from .favorite_asset import *
|
||||||
|
|
|
@ -15,11 +15,10 @@
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework import generics
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
|
|
||||||
from common.mixins import CommonApiMixin
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import AdminUser, Asset
|
from ..models import AdminUser, Asset
|
||||||
|
@ -39,22 +38,21 @@ class AdminUserViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
Admin user api set, for add,delete,update,list,retrieve resource
|
Admin user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
model = AdminUser
|
||||||
filter_fields = ("name", "username")
|
filter_fields = ("name", "username")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = AdminUser.objects.all()
|
|
||||||
serializer_class = serializers.AdminUserSerializer
|
serializer_class = serializers.AdminUserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||||
queryset = AdminUser.objects.all()
|
model = AdminUser
|
||||||
serializer_class = serializers.AdminUserAuthSerializer
|
serializer_class = serializers.AdminUserAuthSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
||||||
queryset = AdminUser.objects.all()
|
model = AdminUser
|
||||||
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
@ -79,7 +77,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Test asset admin user assets_connectivity
|
Test asset admin user assets_connectivity
|
||||||
"""
|
"""
|
||||||
queryset = AdminUser.objects.all()
|
model = AdminUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.TaskIDSerializer
|
serializer_class = serializers.TaskIDSerializer
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from rest_framework import generics
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
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 .. import serializers
|
||||||
from ..tasks import update_asset_hardware_info_manual, \
|
from ..tasks import update_asset_hardware_info_manual, \
|
||||||
test_asset_connectivity_manual
|
test_asset_connectivity_manual
|
||||||
|
@ -29,10 +29,10 @@ class AssetViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint that allows Asset to be viewed or edited.
|
API endpoint that allows Asset to be viewed or edited.
|
||||||
"""
|
"""
|
||||||
|
model = Asset
|
||||||
filter_fields = ("hostname", "ip", "systemuser__id", "admin_user__id")
|
filter_fields = ("hostname", "ip", "systemuser__id", "admin_user__id")
|
||||||
search_fields = ("hostname", "ip")
|
search_fields = ("hostname", "ip")
|
||||||
ordering_fields = ("hostname", "ip", "port", "cpu_cores")
|
ordering_fields = ("hostname", "ip", "port", "cpu_cores")
|
||||||
queryset = Asset.objects.all()
|
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
extra_filter_backends = [AssetByNodeFilterBackend, LabelFilterBackend]
|
extra_filter_backends = [AssetByNodeFilterBackend, LabelFilterBackend]
|
||||||
|
@ -57,7 +57,7 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Refresh asset hardware info
|
Refresh asset hardware info
|
||||||
"""
|
"""
|
||||||
queryset = Asset.objects.all()
|
model = Asset
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Test asset admin user assets_connectivity
|
Test asset admin user assets_connectivity
|
||||||
"""
|
"""
|
||||||
queryset = Asset.objects.all()
|
model = Asset
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.TaskIDSerializer
|
serializer_class = serializers.TaskIDSerializer
|
||||||
|
|
||||||
|
@ -84,9 +84,9 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class AssetGatewayApi(generics.RetrieveAPIView):
|
class AssetGatewayApi(generics.RetrieveAPIView):
|
||||||
queryset = Asset.objects.all()
|
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.GatewayWithAuthSerializer
|
serializer_class = serializers.GatewayWithAuthSerializer
|
||||||
|
model = Asset
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
asset_id = kwargs.get('pk')
|
asset_id = kwargs.get('pk')
|
||||||
|
|
|
@ -13,14 +13,15 @@ __all__ = ['CommandFilterViewSet', 'CommandFilterRuleViewSet']
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterViewSet(OrgBulkModelViewSet):
|
class CommandFilterViewSet(OrgBulkModelViewSet):
|
||||||
|
model = CommandFilter
|
||||||
filter_fields = ("name",)
|
filter_fields = ("name",)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
queryset = CommandFilter.objects.all()
|
|
||||||
serializer_class = serializers.CommandFilterSerializer
|
serializer_class = serializers.CommandFilterSerializer
|
||||||
|
|
||||||
|
|
||||||
class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
class CommandFilterRuleViewSet(OrgBulkModelViewSet):
|
||||||
|
model = CommandFilterRule
|
||||||
filter_fields = ("content",)
|
filter_fields = ("content",)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
|
@ -15,7 +15,7 @@ __all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
|
||||||
|
|
||||||
|
|
||||||
class DomainViewSet(OrgBulkModelViewSet):
|
class DomainViewSet(OrgBulkModelViewSet):
|
||||||
queryset = Domain.objects.all()
|
model = Domain
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.DomainSerializer
|
serializer_class = serializers.DomainSerializer
|
||||||
|
|
||||||
|
@ -26,16 +26,15 @@ class DomainViewSet(OrgBulkModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class GatewayViewSet(OrgBulkModelViewSet):
|
class GatewayViewSet(OrgBulkModelViewSet):
|
||||||
|
model = Gateway
|
||||||
filter_fields = ("domain__name", "name", "username", "ip", "domain")
|
filter_fields = ("domain__name", "name", "username", "ip", "domain")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = Gateway.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.GatewaySerializer
|
serializer_class = serializers.GatewaySerializer
|
||||||
|
|
||||||
|
|
||||||
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
model = Gateway
|
|
||||||
object = None
|
object = None
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
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):
|
class GatheredUserViewSet(OrgModelViewSet):
|
||||||
queryset = GatheredUser.objects.all()
|
model = GatheredUser
|
||||||
serializer_class = GatheredUserSerializer
|
serializer_class = GatheredUserSerializer
|
||||||
permission_classes = [IsOrgAdmin]
|
permission_classes = [IsOrgAdmin]
|
||||||
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
extra_filter_backends = [AssetRelatedByNodeFilterBackend]
|
||||||
|
|
||||||
filter_fields = ['asset', 'username', 'present']
|
filter_fields = ['asset', 'username', 'present']
|
||||||
search_fields = ['username', 'asset__ip', 'asset__hostname']
|
search_fields = ['username', 'asset__ip', 'asset__hostname']
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ __all__ = ['LabelViewSet']
|
||||||
|
|
||||||
|
|
||||||
class LabelViewSet(OrgBulkModelViewSet):
|
class LabelViewSet(OrgBulkModelViewSet):
|
||||||
|
model = Label
|
||||||
filter_fields = ("name", "value")
|
filter_fields = ("name", "value")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import status
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
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.utils import get_logger, get_object_or_none
|
||||||
from common.tree import TreeNodeSerializer
|
from common.tree import TreeNodeSerializer
|
||||||
from orgs.mixins.api import OrgModelViewSet
|
from orgs.mixins.api import OrgModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from ..hands import IsOrgAdmin
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import Node
|
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
|
from .. import serializers
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,9 +43,9 @@ __all__ = [
|
||||||
|
|
||||||
|
|
||||||
class NodeViewSet(OrgModelViewSet):
|
class NodeViewSet(OrgModelViewSet):
|
||||||
|
model = Node
|
||||||
filter_fields = ('value', 'key', 'id')
|
filter_fields = ('value', 'key', 'id')
|
||||||
search_fields = ('value', )
|
search_fields = ('value', )
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeSerializer
|
serializer_class = serializers.NodeSerializer
|
||||||
|
|
||||||
|
@ -59,6 +62,13 @@ class NodeViewSet(OrgModelViewSet):
|
||||||
raise ValidationError({"error": msg})
|
raise ValidationError({"error": msg})
|
||||||
return super().perform_update(serializer)
|
return super().perform_update(serializer)
|
||||||
|
|
||||||
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
node = self.get_object()
|
||||||
|
if node.has_children_or_contains_assets():
|
||||||
|
msg = _("Deletion failed and the node contains children or assets")
|
||||||
|
return Response(data={'msg': msg}, status=status.HTTP_403_FORBIDDEN)
|
||||||
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class NodeListAsTreeApi(generics.ListAPIView):
|
class NodeListAsTreeApi(generics.ListAPIView):
|
||||||
"""
|
"""
|
||||||
|
@ -72,6 +82,7 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""
|
"""
|
||||||
|
model = Node
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = TreeNodeSerializer
|
serializer_class = TreeNodeSerializer
|
||||||
|
|
||||||
|
@ -80,10 +91,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
||||||
queryset = [node.as_tree_node() for node in queryset]
|
queryset = [node.as_tree_node() for node in queryset]
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = Node.objects.all()
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
def filter_queryset(self, queryset):
|
||||||
queryset = super().filter_queryset(queryset)
|
queryset = super().filter_queryset(queryset)
|
||||||
queryset = self.to_tree_queryset(queryset)
|
queryset = self.to_tree_queryset(queryset)
|
||||||
|
@ -91,7 +98,6 @@ class NodeListAsTreeApi(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenApi(generics.ListCreateAPIView):
|
class NodeChildrenApi(generics.ListCreateAPIView):
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeSerializer
|
serializer_class = serializers.NodeSerializer
|
||||||
instance = None
|
instance = None
|
||||||
|
@ -155,6 +161,7 @@ class NodeChildrenAsTreeApi(NodeChildrenApi):
|
||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
model = Node
|
||||||
serializer_class = TreeNodeSerializer
|
serializer_class = TreeNodeSerializer
|
||||||
http_method_names = ['get']
|
http_method_names = ['get']
|
||||||
|
|
||||||
|
@ -197,7 +204,7 @@ class NodeAssetsApi(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class NodeAddChildrenApi(generics.UpdateAPIView):
|
class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||||
queryset = Node.objects.all()
|
model = Node
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeAddChildrenSerializer
|
serializer_class = serializers.NodeAddChildrenSerializer
|
||||||
instance = None
|
instance = None
|
||||||
|
@ -214,8 +221,8 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class NodeAddAssetsApi(generics.UpdateAPIView):
|
class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||||
|
model = Node
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
|
@ -226,8 +233,8 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||||
|
model = Node
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
|
@ -242,8 +249,8 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||||
|
model = Node
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
|
@ -255,8 +262,8 @@ class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RefreshNodeHardwareInfoApi(APIView):
|
class RefreshNodeHardwareInfoApi(APIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
|
||||||
model = Node
|
model = Node
|
||||||
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
node_id = kwargs.get('pk')
|
node_id = kwargs.get('pk')
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework import generics
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from common.serializers import CeleryTaskSerializer
|
from common.serializers import CeleryTaskSerializer
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from ..models import SystemUser, Asset
|
from ..models import SystemUser, Asset
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import (
|
from ..tasks import (
|
||||||
|
@ -43,22 +43,18 @@ class SystemUserViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
System user api set, for add,delete,update,list,retrieve resource
|
System user api set, for add,delete,update,list,retrieve resource
|
||||||
"""
|
"""
|
||||||
|
model = SystemUser
|
||||||
filter_fields = ("name", "username")
|
filter_fields = ("name", "username")
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = SystemUser.objects.all()
|
|
||||||
serializer_class = serializers.SystemUserSerializer
|
serializer_class = serializers.SystemUserSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
queryset = super().get_queryset().all()
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
Get system user auth info
|
Get system user auth info
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.SystemUserAuthSerializer
|
serializer_class = serializers.SystemUserAuthSerializer
|
||||||
|
|
||||||
|
@ -72,7 +68,7 @@ class SystemUserAssetAuthInfoApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Get system user with asset auth info
|
Get system user with asset auth info
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.SystemUserAuthSerializer
|
serializer_class = serializers.SystemUserAuthSerializer
|
||||||
|
|
||||||
|
@ -88,7 +84,7 @@ class SystemUserPushApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Push system user to cluster assets api
|
Push system user to cluster assets api
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = CeleryTaskSerializer
|
serializer_class = CeleryTaskSerializer
|
||||||
|
|
||||||
|
@ -105,7 +101,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
Push system user to cluster assets api
|
Push system user to cluster assets api
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = CeleryTaskSerializer
|
serializer_class = CeleryTaskSerializer
|
||||||
|
|
||||||
|
@ -132,7 +128,7 @@ class SystemUserAssetsListView(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class SystemUserPushToAssetApi(generics.RetrieveAPIView):
|
class SystemUserPushToAssetApi(generics.RetrieveAPIView):
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.TaskIDSerializer
|
serializer_class = serializers.TaskIDSerializer
|
||||||
|
|
||||||
|
@ -145,7 +141,7 @@ class SystemUserPushToAssetApi(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
|
||||||
class SystemUserTestAssetConnectivityApi(generics.RetrieveAPIView):
|
class SystemUserTestAssetConnectivityApi(generics.RetrieveAPIView):
|
||||||
queryset = SystemUser.objects.all()
|
model = SystemUser
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.TaskIDSerializer
|
serializer_class = serializers.TaskIDSerializer
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_HELP_TEXT = _(
|
||||||
|
'Only Numbers、letters、 chinese and characters ( {} ) are allowed'
|
||||||
|
).format(" ".join(['.', '_', '@', '-']))
|
||||||
|
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN = r"^[\._@\w-]+$"
|
||||||
|
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG = \
|
||||||
|
_("* The contains characters that are not allowed")
|
|
@ -53,7 +53,7 @@ class AssetByNodeFilterBackend(filters.BaseFilterBackend):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
if node is None:
|
if node is None:
|
||||||
return queryset.none()
|
return queryset
|
||||||
query_all = self.is_query_all(request)
|
query_all = self.is_query_all(request)
|
||||||
if query_all:
|
if query_all:
|
||||||
pattern = node.get_all_children_pattern(with_self=True)
|
pattern = node.get_all_children_pattern(with_self=True)
|
||||||
|
|
|
@ -7,6 +7,7 @@ from common.utils import get_logger
|
||||||
from orgs.mixins.forms import OrgModelForm
|
from orgs.mixins.forms import OrgModelForm
|
||||||
|
|
||||||
from ..models import Asset, Node
|
from ..models import Asset, Node
|
||||||
|
from ..const import GENERAL_LIMIT_SPECIAL_CHARACTERS_HELP_TEXT
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
@ -14,10 +15,6 @@ __all__ = [
|
||||||
'AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm', 'ProtocolForm',
|
'AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm', 'ProtocolForm',
|
||||||
]
|
]
|
||||||
|
|
||||||
HELP_TEXTS_ASSET_HOSTNAME = _(
|
|
||||||
'Only Numbers、letters、 chinese and characters ( {} ) are allowed'
|
|
||||||
).format(" ".join(['.', '_', '@']))
|
|
||||||
|
|
||||||
|
|
||||||
class ProtocolForm(forms.Form):
|
class ProtocolForm(forms.Form):
|
||||||
name = forms.ChoiceField(
|
name = forms.ChoiceField(
|
||||||
|
@ -72,7 +69,7 @@ class AssetCreateForm(OrgModelForm):
|
||||||
'nodes': _("Node"),
|
'nodes': _("Node"),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'hostname': HELP_TEXTS_ASSET_HOSTNAME,
|
'hostname': GENERAL_LIMIT_SPECIAL_CHARACTERS_HELP_TEXT,
|
||||||
'admin_user': _(
|
'admin_user': _(
|
||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
'root or other NOPASSWD sudo privilege user existed in asset,'
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
'If asset is windows or other set any one, more see admin user left menu'
|
||||||
|
@ -119,7 +116,7 @@ class AssetUpdateForm(OrgModelForm):
|
||||||
'nodes': _("Node"),
|
'nodes': _("Node"),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'hostname': HELP_TEXTS_ASSET_HOSTNAME,
|
'hostname': GENERAL_LIMIT_SPECIAL_CHARACTERS_HELP_TEXT,
|
||||||
'admin_user': _(
|
'admin_user': _(
|
||||||
'root or other NOPASSWD sudo privilege user existed in asset,'
|
'root or other NOPASSWD sudo privilege user existed in asset,'
|
||||||
'If asset is windows or other set any one, more see admin user left menu'
|
'If asset is windows or other set any one, more see admin user left menu'
|
||||||
|
@ -132,7 +129,7 @@ class AssetUpdateForm(OrgModelForm):
|
||||||
class AssetBulkUpdateForm(OrgModelForm):
|
class AssetBulkUpdateForm(OrgModelForm):
|
||||||
assets = forms.ModelMultipleChoiceField(
|
assets = forms.ModelMultipleChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label=_('Select assets'), queryset=Asset.objects.all(),
|
label=_('Select assets'), queryset=Asset.objects,
|
||||||
widget=forms.SelectMultiple(
|
widget=forms.SelectMultiple(
|
||||||
attrs={
|
attrs={
|
||||||
'class': 'select2',
|
'class': 'select2',
|
||||||
|
@ -158,11 +155,18 @@ class AssetBulkUpdateForm(OrgModelForm):
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.set_fields_queryset()
|
||||||
|
|
||||||
# 重写其他字段为不再required
|
# 重写其他字段为不再required
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
if name != 'assets':
|
if name != 'assets':
|
||||||
field.required = False
|
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):
|
def save(self, commit=True):
|
||||||
changed_fields = []
|
changed_fields = []
|
||||||
for field in self._meta.fields:
|
for field in self._meta.fields:
|
||||||
|
|
|
@ -12,7 +12,7 @@ __all__ = ['DomainForm', 'GatewayForm']
|
||||||
|
|
||||||
class DomainForm(forms.ModelForm):
|
class DomainForm(forms.ModelForm):
|
||||||
assets = forms.ModelMultipleChoiceField(
|
assets = forms.ModelMultipleChoiceField(
|
||||||
queryset=Asset.objects.all(), label=_('Asset'), required=False,
|
queryset=Asset.objects, label=_('Asset'), required=False,
|
||||||
widget=forms.SelectMultiple(
|
widget=forms.SelectMultiple(
|
||||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
||||||
)
|
)
|
||||||
|
@ -23,19 +23,23 @@ class DomainForm(forms.ModelForm):
|
||||||
fields = ['name', 'comment', 'assets']
|
fields = ['name', 'comment', 'assets']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if kwargs.get('instance', None):
|
|
||||||
initial = kwargs.get('initial', {})
|
|
||||||
initial['assets'] = kwargs['instance'].assets.all()
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.set_fields_queryset()
|
||||||
|
|
||||||
# 前端渲染优化, 防止过多资产
|
def set_fields_queryset(self):
|
||||||
assets_field = self.fields.get('assets')
|
assets_field = self.fields.get('assets')
|
||||||
|
|
||||||
|
# 没有data代表是渲染表单, 有data代表是提交创建/更新表单
|
||||||
if not self.data:
|
if not self.data:
|
||||||
instance = kwargs.get('instance')
|
# 有instance 代表渲染更新表单, 否则是创建表单
|
||||||
if instance:
|
# 前端渲染优化, 防止过多资产, 设置assets queryset为none
|
||||||
assets_field.queryset = instance.assets.all()
|
if self.instance:
|
||||||
|
assets_field.initial = self.instance.assets.all()
|
||||||
|
assets_field.queryset = self.instance.assets.all()
|
||||||
else:
|
else:
|
||||||
assets_field.queryset = Asset.objects.none()
|
assets_field.queryset = Asset.objects.none()
|
||||||
|
else:
|
||||||
|
assets_field.queryset = Asset.objects.all()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
instance = super().save(commit=commit)
|
instance = super().save(commit=commit)
|
||||||
|
|
|
@ -10,7 +10,7 @@ __all__ = ['LabelForm']
|
||||||
|
|
||||||
class LabelForm(forms.ModelForm):
|
class LabelForm(forms.ModelForm):
|
||||||
assets = forms.ModelMultipleChoiceField(
|
assets = forms.ModelMultipleChoiceField(
|
||||||
queryset=Asset.objects.all(), label=_('Asset'), required=False,
|
queryset=Asset.objects.none(), label=_('Asset'), required=False,
|
||||||
widget=forms.SelectMultiple(
|
widget=forms.SelectMultiple(
|
||||||
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
|
||||||
)
|
)
|
||||||
|
@ -21,19 +21,23 @@ class LabelForm(forms.ModelForm):
|
||||||
fields = ['name', 'value', 'assets']
|
fields = ['name', 'value', 'assets']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if kwargs.get('instance', None):
|
|
||||||
initial = kwargs.get('initial', {})
|
|
||||||
initial['assets'] = kwargs['instance'].assets.all()
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
self.set_fields_queryset()
|
||||||
|
|
||||||
# 前端渲染优化, 防止过多资产
|
def set_fields_queryset(self):
|
||||||
assets_field = self.fields.get('assets')
|
assets_field = self.fields.get('assets')
|
||||||
|
|
||||||
|
# 没有data代表是渲染表单, 有data代表是提交创建/更新表单
|
||||||
if not self.data:
|
if not self.data:
|
||||||
instance = kwargs.get('instance')
|
# 有instance 代表渲染更新表单, 否则是创建表单
|
||||||
if instance:
|
# 前端渲染优化, 防止过多资产, 设置assets queryset为none
|
||||||
assets_field.queryset = instance.assets.all()
|
if self.instance:
|
||||||
|
assets_field.initial = self.instance.assets.all()
|
||||||
|
assets_field.queryset = self.instance.assets.all()
|
||||||
else:
|
else:
|
||||||
assets_field.queryset = Asset.objects.none()
|
assets_field.queryset = Asset.objects.none()
|
||||||
|
else:
|
||||||
|
assets_field.queryset = Asset.objects.all()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
label = super().save(commit=commit)
|
label = super().save(commit=commit)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
|
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, get_logger
|
||||||
from orgs.mixins.forms import OrgModelForm
|
from orgs.mixins.forms import OrgModelForm
|
||||||
from ..models import AdminUser, SystemUser
|
from ..models import AdminUser, SystemUser
|
||||||
|
from ..const import GENERAL_LIMIT_SPECIAL_CHARACTERS_HELP_TEXT
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -98,6 +99,7 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
help_texts = {
|
help_texts = {
|
||||||
|
'name': GENERAL_LIMIT_SPECIAL_CHARACTERS_HELP_TEXT,
|
||||||
'auto_push': _('Auto push system user to asset'),
|
'auto_push': _('Auto push system user to asset'),
|
||||||
'priority': _('1-100, High level will be using login asset as default, '
|
'priority': _('1-100, High level will be using login asset as default, '
|
||||||
'if user was granted more than 2 system user'),
|
'if user was granted more than 2 system user'),
|
||||||
|
|
|
@ -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 .utils import *
|
||||||
from .authbook import *
|
from .authbook import *
|
||||||
from .gathered_user import *
|
from .gathered_user import *
|
||||||
|
from .favorite_asset import *
|
||||||
|
|
|
@ -7,6 +7,7 @@ from django.db import models
|
||||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from common.utils import lazyproperty
|
||||||
from orgs.mixins.models import OrgModelMixin
|
from orgs.mixins.models import OrgModelMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,25 +58,30 @@ class CommandFilterRule(OrgModelMixin):
|
||||||
date_updated = models.DateTimeField(auto_now=True)
|
date_updated = models.DateTimeField(auto_now=True)
|
||||||
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
|
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
|
||||||
|
|
||||||
__pattern = None
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('-priority', 'action')
|
ordering = ('-priority', 'action')
|
||||||
verbose_name = _("Command filter rule")
|
verbose_name = _("Command filter rule")
|
||||||
|
|
||||||
@property
|
@lazyproperty
|
||||||
def _pattern(self):
|
def _pattern(self):
|
||||||
if self.__pattern:
|
|
||||||
return self.__pattern
|
|
||||||
if self.type == 'command':
|
if self.type == 'command':
|
||||||
regex = []
|
regex = []
|
||||||
for cmd in self.content.split('\r\n'):
|
content = self.content.replace('\r\n', '\n')
|
||||||
cmd = cmd.replace(' ', '\s+')
|
for cmd in content.split('\n'):
|
||||||
regex.append(r'\b{0}\b'.format(cmd))
|
cmd = re.escape(cmd)
|
||||||
self.__pattern = re.compile(r'{}'.format('|'.join(regex)))
|
cmd = cmd.replace('\\ ', '\s+')
|
||||||
|
if cmd[-1].isalpha():
|
||||||
|
regex.append(r'\b{0}\b'.format(cmd))
|
||||||
|
else:
|
||||||
|
regex.append(r'\b{0}'.format(cmd))
|
||||||
|
s = r'{}'.format('|'.join(regex))
|
||||||
else:
|
else:
|
||||||
self.__pattern = re.compile(r'{0}'.format(self.content))
|
s = r'{0}'.format(self.content)
|
||||||
return self.__pattern
|
try:
|
||||||
|
_pattern = re.compile(s)
|
||||||
|
except:
|
||||||
|
_pattern = ''
|
||||||
|
return _pattern
|
||||||
|
|
||||||
def match(self, data):
|
def match(self, data):
|
||||||
found = self._pattern.search(data)
|
found = self._pattern.search(data)
|
||||||
|
|
|
@ -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')
|
ungrouped_value = _('ungrouped')
|
||||||
empty_key = '-11'
|
empty_key = '-11'
|
||||||
empty_value = _("empty")
|
empty_value = _("empty")
|
||||||
|
favorite_key = '-12'
|
||||||
|
favorite_value = _("favorite")
|
||||||
|
|
||||||
def is_default_node(self):
|
def is_default_node(self):
|
||||||
return self.key == self.default_key
|
return self.key == self.default_key
|
||||||
|
@ -363,7 +365,7 @@ class SomeNodesMixin:
|
||||||
@classmethod
|
@classmethod
|
||||||
def ungrouped_node(cls):
|
def ungrouped_node(cls):
|
||||||
with tmp_to_org(Organization.system()):
|
with tmp_to_org(Organization.system()):
|
||||||
defaults = {'value': cls.ungrouped_key}
|
defaults = {'value': cls.ungrouped_value}
|
||||||
obj, created = cls.objects.get_or_create(
|
obj, created = cls.objects.get_or_create(
|
||||||
defaults=defaults, key=cls.ungrouped_key
|
defaults=defaults, key=cls.ungrouped_key
|
||||||
)
|
)
|
||||||
|
@ -387,11 +389,21 @@ class SomeNodesMixin:
|
||||||
)
|
)
|
||||||
return obj
|
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
|
@classmethod
|
||||||
def initial_some_nodes(cls):
|
def initial_some_nodes(cls):
|
||||||
cls.default_node()
|
cls.default_node()
|
||||||
cls.empty_node()
|
cls.empty_node()
|
||||||
cls.ungrouped_node()
|
cls.ungrouped_node()
|
||||||
|
cls.favorite_node()
|
||||||
|
|
||||||
|
|
||||||
class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
|
class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin, NodeAssetsMixin):
|
||||||
|
@ -412,11 +424,11 @@ class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.value
|
return self.value
|
||||||
|
|
||||||
def __eq__(self, other):
|
# def __eq__(self, other):
|
||||||
if not other:
|
# if not other:
|
||||||
return False
|
# return False
|
||||||
return self.id == other.id
|
# return self.id == other.id
|
||||||
|
#
|
||||||
def __gt__(self, other):
|
def __gt__(self, other):
|
||||||
self_key = [int(k) for k in self.key.split(':')]
|
self_key = [int(k) for k in self.key.split(':')]
|
||||||
other_key = [int(k) for k in other.key.split(':')]
|
other_key = [int(k) for k in other.key.split(':')]
|
||||||
|
@ -470,8 +482,13 @@ class Node(OrgModelMixin, SomeNodesMixin, TreeMixin, FamilyMixin, FullValueMixin
|
||||||
tree_node = TreeNode(**data)
|
tree_node = TreeNode(**data)
|
||||||
return tree_node
|
return tree_node
|
||||||
|
|
||||||
def delete(self, using=None, keep_parents=False):
|
def has_children_or_contains_assets(self):
|
||||||
if self.children or self.get_assets():
|
if self.children or self.get_assets():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def delete(self, using=None, keep_parents=False):
|
||||||
|
if self.has_children_or_contains_assets():
|
||||||
return
|
return
|
||||||
return super().delete(using=using, keep_parents=keep_parents)
|
return super().delete(using=using, keep_parents=keep_parents)
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,10 @@ class SystemUser(AssetUser):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{0.name}({0.username})'.format(self)
|
return '{0.name}({0.username})'.format(self)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nodes_amount(self):
|
||||||
|
return self.nodes.all().count()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def login_mode_display(self):
|
def login_mode_display(self):
|
||||||
return self.get_login_mode_display()
|
return self.get_login_mode_display()
|
||||||
|
|
|
@ -10,3 +10,4 @@ from .domain import *
|
||||||
from .cmd_filter import *
|
from .cmd_filter import *
|
||||||
from .asset_user import *
|
from .asset_user import *
|
||||||
from .gathered_user import *
|
from .gathered_user import *
|
||||||
|
from .favorite_asset import *
|
||||||
|
|
|
@ -45,7 +45,7 @@ class ReplaceNodeAdminUserSerializer(serializers.ModelSerializer):
|
||||||
管理用户更新关联到的集群
|
管理用户更新关联到的集群
|
||||||
"""
|
"""
|
||||||
nodes = serializers.PrimaryKeyRelatedField(
|
nodes = serializers.PrimaryKeyRelatedField(
|
||||||
many=True, queryset=Node.objects.all()
|
many=True, queryset=Node.objects
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -8,6 +8,10 @@ from django.utils.translation import ugettext_lazy as _
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from common.serializers import AdaptedBulkListSerializer
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
from ..models import Asset, Node, Label
|
from ..models import Asset, Node, Label
|
||||||
|
from ..const import (
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN,
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
|
)
|
||||||
from .base import ConnectivitySerializer
|
from .base import ConnectivitySerializer
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -94,10 +98,10 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def validate_hostname(hostname):
|
def validate_hostname(hostname):
|
||||||
pattern = r"^[\._@\w-]+$"
|
pattern = GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN
|
||||||
res = re.match(pattern, hostname)
|
res = re.match(pattern, hostname)
|
||||||
if res is None:
|
if res is None:
|
||||||
msg = _("* The hostname contains characters that are not allowed")
|
msg = GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
raise serializers.ValidationError(msg)
|
raise serializers.ValidationError(msg)
|
||||||
return hostname
|
return hostname
|
||||||
|
|
||||||
|
@ -136,6 +140,7 @@ class AssetSerializer(BulkOrgResourceModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class AssetSimpleSerializer(serializers.ModelSerializer):
|
class AssetSimpleSerializer(serializers.ModelSerializer):
|
||||||
|
connectivity = ConnectivitySerializer(read_only=True, label=_("Connectivity"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
|
|
|
@ -79,7 +79,7 @@ class AssetUserAuthInfoSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class AssetUserPushSerializer(serializers.Serializer):
|
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)
|
username = serializers.CharField(max_length=1024)
|
||||||
|
|
||||||
def create(self, validated_data):
|
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
|
return data
|
||||||
|
|
||||||
|
|
||||||
class NodeAssetsSerializer(serializers.ModelSerializer):
|
class NodeAssetsSerializer(BulkOrgResourceModelSerializer):
|
||||||
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
|
assets = serializers.PrimaryKeyRelatedField(
|
||||||
|
many=True, queryset=Asset.objects
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Node
|
model = Node
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import re
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -6,6 +7,10 @@ from common.serializers import AdaptedBulkListSerializer
|
||||||
from common.utils import ssh_pubkey_gen
|
from common.utils import ssh_pubkey_gen
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from ..models import SystemUser
|
from ..models import SystemUser
|
||||||
|
from ..const import (
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN,
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
|
)
|
||||||
from .base import AuthSerializer, AuthSerializerMixin
|
from .base import AuthSerializer, AuthSerializerMixin
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,18 +26,28 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'username', 'password', 'public_key', 'private_key',
|
'id', 'name', 'username', 'password', 'public_key', 'private_key',
|
||||||
'login_mode', 'login_mode_display', 'priority', 'protocol',
|
'login_mode', 'login_mode_display', 'priority', 'protocol',
|
||||||
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'nodes',
|
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment',
|
||||||
'assets_amount', 'auto_generate_key'
|
'assets_amount', 'nodes_amount', 'auto_generate_key'
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'password': {"write_only": True},
|
'password': {"write_only": True},
|
||||||
'public_key': {"write_only": True},
|
'public_key': {"write_only": True},
|
||||||
'private_key': {"write_only": True},
|
'private_key': {"write_only": True},
|
||||||
|
'nodes_amount': {'label': _('Node')},
|
||||||
'assets_amount': {'label': _('Asset')},
|
'assets_amount': {'label': _('Asset')},
|
||||||
'login_mode_display': {'label': _('Login mode display')},
|
'login_mode_display': {'label': _('Login mode display')},
|
||||||
'created_by': {'read_only': True},
|
'created_by': {'read_only': True},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_name(name):
|
||||||
|
pattern = GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN
|
||||||
|
res = re.match(pattern, name)
|
||||||
|
if res is None:
|
||||||
|
msg = GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
|
raise serializers.ValidationError(msg)
|
||||||
|
return name
|
||||||
|
|
||||||
def validate_auto_push(self, value):
|
def validate_auto_push(self, value):
|
||||||
login_mode = self.initial_data.get("login_mode")
|
login_mode = self.initial_data.get("login_mode")
|
||||||
protocol = self.initial_data.get("protocol")
|
protocol = self.initial_data.get("protocol")
|
||||||
|
|
|
@ -25,12 +25,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{% include 'assets/_asset_list_modal.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2();
|
$('.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() {
|
}).on('click', '.field-tag', function() {
|
||||||
changeField(this);
|
changeField(this);
|
||||||
}).on('click', '#change_all', function () {
|
}).on('click', '#change_all', function () {
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'assets:asset-user-list' pk=asset.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset user list' %} </a>
|
<a href="{% url 'assets:asset-user-list' pk=asset.id %}" class="text-center"><i class="fa fa-bar-chart-o"></i> {% trans 'Asset user list' %} </a>
|
||||||
</li>
|
</li>
|
||||||
{% if user.is_superuser %}
|
|
||||||
<li class="pull-right">
|
<li class="pull-right">
|
||||||
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -31,7 +30,6 @@
|
||||||
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
|
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
|
|
@ -292,7 +292,8 @@ $(document).ready(function(){
|
||||||
format: 'csv',
|
format: 'csv',
|
||||||
params: {
|
params: {
|
||||||
search: search,
|
search: search,
|
||||||
node_id: current_node_id || ''
|
node_id: current_node_id || '',
|
||||||
|
show_current_asset: getCookie('show_current_asset')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
APIExportData(props);
|
APIExportData(props);
|
||||||
|
|
|
@ -21,19 +21,46 @@
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<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 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 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 systemUsersUrl = "{% url 'api-perms:my-asset-system-users' asset_id=DEFAULT_PK %}?cache_policy=1";
|
||||||
var showAssetHref = false; // Need input default true
|
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 = {
|
var actions = {
|
||||||
targets: 4, createdCell: function (td, cellData) {
|
targets: 4, createdCell: function (td, cellData) {
|
||||||
var conn_btn = '<a href="{% url "luna-view" %}?login_to=' + cellData +
|
var connBtn = '<a href="{% url "luna-view" %}?login_to=' + cellData +
|
||||||
'" class="btn btn-xs btn-primary" target="_blank">{% trans "Connect" %}</a>';
|
'" class="btn btn-xs btn-primary" target="_blank"><i class="fa fa-terminal"></i></a> ';
|
||||||
$(td).html(conn_btn)
|
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 () {
|
$(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 () {
|
}).on('click', '.labels li', function () {
|
||||||
var val = $(this).text();
|
var val = $(this).text();
|
||||||
$("#user_assets_table_filter input").val(val);
|
$("#user_assets_table_filter input").val(val);
|
||||||
|
@ -67,22 +94,38 @@ $(document).ready(function () {
|
||||||
};
|
};
|
||||||
$('#asset_detail_tbody').html(trs)
|
$('#asset_detail_tbody').html(trs)
|
||||||
$('#user_asset_detail_modal').modal();
|
$('#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) {
|
||||||
|
favoriteAssets.push(assetId);
|
||||||
|
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 index = favoriteAssets.indexOf(assetId);
|
||||||
|
if (index !== '-1'){
|
||||||
|
favoriteAssets.splice(index, 1);
|
||||||
|
}
|
||||||
|
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>
|
</script>
|
||||||
{% endblock %}
|
{% 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', api.AssetUserViewSet, 'asset-user')
|
||||||
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
|
router.register(r'asset-users-info', api.AssetUserExportViewSet, 'asset-user-info')
|
||||||
router.register(r'gathered-users', api.GatheredUserViewSet, 'gathered-user')
|
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 = routers.NestedDefaultRouter(router, r'cmd-filters', lookup='filter')
|
||||||
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
cmd_filter_router.register(r'rules', api.CommandFilterRuleViewSet, 'cmd-filter-rule')
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework import viewsets
|
|
||||||
|
|
||||||
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
|
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
|
||||||
|
from orgs.mixins.api import OrgModelViewSet
|
||||||
from .models import FTPLog
|
from .models import FTPLog
|
||||||
from .serializers import FTPLogSerializer
|
from .serializers import FTPLogSerializer
|
||||||
|
|
||||||
|
|
||||||
class FTPLogViewSet(viewsets.ModelViewSet):
|
class FTPLogViewSet(OrgModelViewSet):
|
||||||
queryset = FTPLog.objects.all()
|
model = FTPLog
|
||||||
serializer_class = FTPLogSerializer
|
serializer_class = FTPLogSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,13 @@ class LDAPAuthorizationBackend(LDAPBackend):
|
||||||
if not username:
|
if not username:
|
||||||
logger.info('Authenticate failed: username is None')
|
logger.info('Authenticate failed: username is None')
|
||||||
return None
|
return None
|
||||||
|
if settings.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS:
|
||||||
|
user_model = self.get_user_model()
|
||||||
|
exist = user_model.objects.filter(username=username).exists()
|
||||||
|
if not exist:
|
||||||
|
msg = 'Authentication failed: user ({}) is not in the user list'
|
||||||
|
logger.info(msg.format(username))
|
||||||
|
return None
|
||||||
ldap_user = LDAPUser(self, username=username.strip(), request=request)
|
ldap_user = LDAPUser(self, username=username.strip(), request=request)
|
||||||
user = self.authenticate_ldap_user(ldap_user, password)
|
user = self.authenticate_ldap_user(ldap_user, password)
|
||||||
logger.info('Authenticate user: {}'.format(user))
|
logger.info('Authenticate user: {}'.format(user))
|
||||||
|
|
|
@ -83,8 +83,6 @@ class LogTailApi(generics.RetrieveAPIView):
|
||||||
return Response({"data": data, 'end': end, 'mark': new_mark})
|
return Response({"data": data, 'end': end, 'mark': new_mark})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ResourcesIDCacheApi(APIView):
|
class ResourcesIDCacheApi(APIView):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
spm = str(uuid.uuid4())
|
spm = str(uuid.uuid4())
|
||||||
|
|
|
@ -63,7 +63,6 @@ class IDSpmFilter(filters.BaseFilterBackend):
|
||||||
cache_key = const.KEY_CACHE_RESOURCES_ID.format(spm)
|
cache_key = const.KEY_CACHE_RESOURCES_ID.format(spm)
|
||||||
resources_id = cache.get(cache_key)
|
resources_id = cache.get(cache_key)
|
||||||
if not resources_id or not isinstance(resources_id, list):
|
if not resources_id or not isinstance(resources_id, list):
|
||||||
queryset = queryset.none()
|
|
||||||
return queryset
|
return queryset
|
||||||
queryset = queryset.filter(id__in=resources_id)
|
queryset = queryset.filter(id__in=resources_id)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
import uuid
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["NoDeleteManager", "NoDeleteModelMixin", "NoDeleteQuerySet"]
|
__all__ = [
|
||||||
|
"NoDeleteManager", "NoDeleteModelMixin", "NoDeleteQuerySet",
|
||||||
|
"CommonModelMixin"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class NoDeleteQuerySet(models.query.QuerySet):
|
class NoDeleteQuerySet(models.query.QuerySet):
|
||||||
|
@ -40,3 +43,13 @@ class NoDeleteModelMixin(models.Model):
|
||||||
self.is_discard = True
|
self.is_discard = True
|
||||||
self.discard_time = timezone.now()
|
self.discard_time = timezone.now()
|
||||||
return self.save()
|
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
|
queries = connection.queries
|
||||||
counters = defaultdict(Counter)
|
counters = defaultdict(Counter)
|
||||||
for query in queries:
|
for query in queries:
|
||||||
if not query['sql'].startswith('SELECT'):
|
if not query['sql'] or not query['sql'].startswith('SELECT'):
|
||||||
continue
|
continue
|
||||||
tables = pattern.findall(query['sql'])
|
tables = pattern.findall(query['sql'])
|
||||||
table_name = ''.join(tables)
|
table_name = ''.join(tables)
|
||||||
|
|
|
@ -51,6 +51,8 @@ class TreeNode:
|
||||||
result = True
|
result = True
|
||||||
elif self.pId != other.pId:
|
elif self.pId != other.pId:
|
||||||
result = self.pId > other.pId
|
result = self.pId > other.pId
|
||||||
|
elif str(self.id).startswith('-') and not str(other.id).startswith('-'):
|
||||||
|
result = False
|
||||||
else:
|
else:
|
||||||
result = self.name > other.name
|
result = self.name > other.name
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -378,6 +378,7 @@ defaults = {
|
||||||
'AUTH_LDAP_SYNC_IS_PERIODIC': False,
|
'AUTH_LDAP_SYNC_IS_PERIODIC': False,
|
||||||
'AUTH_LDAP_SYNC_INTERVAL': None,
|
'AUTH_LDAP_SYNC_INTERVAL': None,
|
||||||
'AUTH_LDAP_SYNC_CRONTAB': None,
|
'AUTH_LDAP_SYNC_CRONTAB': None,
|
||||||
|
'AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS': False,
|
||||||
'HTTP_BIND_HOST': '0.0.0.0',
|
'HTTP_BIND_HOST': '0.0.0.0',
|
||||||
'HTTP_LISTEN_PORT': 8080,
|
'HTTP_LISTEN_PORT': 8080,
|
||||||
'WS_LISTEN_PORT': 8070,
|
'WS_LISTEN_PORT': 8070,
|
||||||
|
|
|
@ -429,6 +429,7 @@ AUTH_LDAP_SEARCH_PAGED_SIZE = CONFIG.AUTH_LDAP_SEARCH_PAGED_SIZE
|
||||||
AUTH_LDAP_SYNC_IS_PERIODIC = CONFIG.AUTH_LDAP_SYNC_IS_PERIODIC
|
AUTH_LDAP_SYNC_IS_PERIODIC = CONFIG.AUTH_LDAP_SYNC_IS_PERIODIC
|
||||||
AUTH_LDAP_SYNC_INTERVAL = CONFIG.AUTH_LDAP_SYNC_INTERVAL
|
AUTH_LDAP_SYNC_INTERVAL = CONFIG.AUTH_LDAP_SYNC_INTERVAL
|
||||||
AUTH_LDAP_SYNC_CRONTAB = CONFIG.AUTH_LDAP_SYNC_CRONTAB
|
AUTH_LDAP_SYNC_CRONTAB = CONFIG.AUTH_LDAP_SYNC_CRONTAB
|
||||||
|
AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS = CONFIG.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS
|
||||||
|
|
||||||
AUTH_LDAP_SERVER_URI = 'ldap://localhost:389'
|
AUTH_LDAP_SERVER_URI = 'ldap://localhost:389'
|
||||||
AUTH_LDAP_BIND_DN = 'cn=admin,dc=jumpserver,dc=org'
|
AUTH_LDAP_BIND_DN = 'cn=admin,dc=jumpserver,dc=org'
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -22,7 +22,8 @@
|
||||||
<script>
|
<script>
|
||||||
var scheme = document.location.protocol === "https:" ? "wss" : "ws";
|
var scheme = document.location.protocol === "https:" ? "wss" : "ws";
|
||||||
var port = document.location.port ? ":" + document.location.port : "";
|
var port = document.location.port ? ":" + document.location.port : "";
|
||||||
var url = "/ws/ops/tasks/" + "{{ task_id }}" + "/log/";
|
var taskId = "{{ task_id }}";
|
||||||
|
var url = "/ws/ops/tasks/log/";
|
||||||
var wsURL = scheme + "://" + document.location.hostname + port + url;
|
var wsURL = scheme + "://" + document.location.hostname + port + url;
|
||||||
var failOverPort = "{{ ws_port }}";
|
var failOverPort = "{{ ws_port }}";
|
||||||
var failOverWsURL = scheme + "://" + document.location.hostname + ':' + failOverPort + url;
|
var failOverWsURL = scheme + "://" + document.location.hostname + ':' + failOverPort + url;
|
||||||
|
@ -46,6 +47,10 @@
|
||||||
var data = JSON.parse(e.data);
|
var data = JSON.parse(e.data);
|
||||||
term.write(data.message);
|
term.write(data.message);
|
||||||
};
|
};
|
||||||
|
ws.onopen = function() {
|
||||||
|
var msg = {"task": taskId};
|
||||||
|
ws.send(JSON.stringify(msg))
|
||||||
|
};
|
||||||
ws.onerror = function (e) {
|
ws.onerror = function (e) {
|
||||||
ws = new WebSocket(failOverWsURL);
|
ws = new WebSocket(failOverWsURL);
|
||||||
ws.onmessage = function(e) {
|
ws.onmessage = function(e) {
|
||||||
|
|
|
@ -140,6 +140,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function initTree() {
|
function initTree() {
|
||||||
|
$('#assetTree').html("{% trans 'Loading' %}" + '..');
|
||||||
if (systemUserId) {
|
if (systemUserId) {
|
||||||
url = treeUrl + '&system_user=' + systemUserId
|
url = treeUrl + '&system_user=' + systemUserId
|
||||||
} else {
|
} else {
|
||||||
|
@ -236,6 +237,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
var term = null;
|
var term = null;
|
||||||
|
var ws = null;
|
||||||
|
|
||||||
function initResultTerminal() {
|
function initResultTerminal() {
|
||||||
term = new Terminal({
|
term = new Terminal({
|
||||||
|
@ -253,7 +255,29 @@
|
||||||
term.open(document.getElementById('term'));
|
term.open(document.getElementById('term'));
|
||||||
var msg = "{% trans 'Select the left asset, select the running system user, execute command in batch' %}" + "\r\n";
|
var msg = "{% trans 'Select the left asset, select the running system user, execute command in batch' %}" + "\r\n";
|
||||||
fit(term);
|
fit(term);
|
||||||
term.write(msg)
|
term.write(msg);
|
||||||
|
|
||||||
|
var scheme = document.location.protocol === "https:" ? "wss" : "ws";
|
||||||
|
var port = document.location.port ? ":" + document.location.port : "";
|
||||||
|
var url = "/ws/ops/tasks/log/";
|
||||||
|
var wsURL = scheme + "://" + document.location.hostname + port + url;
|
||||||
|
var failOverPort = "{{ ws_port }}";
|
||||||
|
var failOverWsURL = scheme + "://" + document.location.hostname + ':' + failOverPort + url;
|
||||||
|
ws = new WebSocket(wsURL);
|
||||||
|
ws.onerror = function (e) {
|
||||||
|
ws = new WebSocket(failOverWsURL);
|
||||||
|
ws.onmessage = function(e) {
|
||||||
|
var data = JSON.parse(e.data);
|
||||||
|
term.write(data.message);
|
||||||
|
};
|
||||||
|
ws.onerror = function (e) {
|
||||||
|
term.write("Connect websocket server error")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.onmessage = function(e) {
|
||||||
|
var data = JSON.parse(e.data);
|
||||||
|
term.write(data.message);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapperError(msg) {
|
function wrapperError(msg) {
|
||||||
|
@ -288,35 +312,12 @@
|
||||||
run_as: run_as,
|
run_as: run_as,
|
||||||
command: command
|
command: command
|
||||||
};
|
};
|
||||||
var mark = '';
|
|
||||||
var log_url = null;
|
|
||||||
var end = false;
|
|
||||||
var error = false;
|
|
||||||
var int = null;
|
|
||||||
var interval = 200;
|
|
||||||
|
|
||||||
function writeExecutionOutput() {
|
function writeExecutionOutput(taskId) {
|
||||||
if (!end) {
|
var msg = "{% trans 'Pending' %} ";
|
||||||
$.ajax({
|
term.write(msg);
|
||||||
url: log_url + '?mark=' + mark,
|
msg = JSON.stringify({task: taskId});
|
||||||
method: "GET",
|
ws.send(msg);
|
||||||
contentType: "application/json; charset=utf-8"
|
|
||||||
}).done(function (data, textStatue, jqXHR) {
|
|
||||||
if (jqXHR.status === 203) {
|
|
||||||
error = true;
|
|
||||||
term.write('.');
|
|
||||||
interval = 500;
|
|
||||||
}
|
|
||||||
if (jqXHR.status === 200) {
|
|
||||||
term.write(data.data);
|
|
||||||
mark = data.mark;
|
|
||||||
if (data.end) {
|
|
||||||
end = true;
|
|
||||||
window.clearInterval(int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestApi({
|
requestApi({
|
||||||
|
@ -325,12 +326,8 @@
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
flash_message: false,
|
flash_message: false,
|
||||||
success: function (resp) {
|
success: function (resp) {
|
||||||
var msg = "{% trans 'Pending' %}";
|
{#log_url = resp.log_url;#}
|
||||||
term.write(msg + "...\r\n");
|
writeExecutionOutput(resp.id)
|
||||||
log_url = resp.log_url;
|
|
||||||
int = setInterval(function () {
|
|
||||||
writeExecutionOutput()
|
|
||||||
}, interval);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
|
@ -339,6 +336,8 @@
|
||||||
var editor;
|
var editor;
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
systemUserId = $('#system-users-select').val();
|
systemUserId = $('#system-users-select').val();
|
||||||
|
|
||||||
|
|
||||||
$(".select2").select2({
|
$(".select2").select2({
|
||||||
dropdownAutoWidth: true,
|
dropdownAutoWidth: true,
|
||||||
}).on('select2:select', function (evt) {
|
}).on('select2:select', function (evt) {
|
||||||
|
|
|
@ -5,5 +5,5 @@ from .. import ws
|
||||||
app_name = 'ops'
|
app_name = 'ops'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('ws/ops/tasks/<uuid:task_id>/log/', ws.CeleryLogWebsocket, name='task-log-ws'),
|
path('ws/ops/tasks/log/', ws.CeleryLogWebsocket, name='task-log-ws'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -79,7 +79,8 @@ class CommandExecutionStartView(PermissionsMixin, TemplateView):
|
||||||
'app': _('Ops'),
|
'app': _('Ops'),
|
||||||
'action': _('Command execution'),
|
'action': _('Command execution'),
|
||||||
'form': self.get_form(),
|
'form': self.get_form(),
|
||||||
'system_users': system_users
|
'system_users': system_users,
|
||||||
|
'ws_port': settings.CONFIG.WS_LISTEN_PORT
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
|
@ -1,41 +1,58 @@
|
||||||
import time
|
import time
|
||||||
|
import os
|
||||||
import threading
|
import threading
|
||||||
|
import json
|
||||||
|
|
||||||
|
from celery.result import AsyncResult
|
||||||
|
|
||||||
from .celery.utils import get_celery_task_log_path
|
from .celery.utils import get_celery_task_log_path
|
||||||
from channels.generic.websocket import JsonWebsocketConsumer
|
from channels.generic.websocket import JsonWebsocketConsumer
|
||||||
|
|
||||||
|
|
||||||
class CeleryLogWebsocket(JsonWebsocketConsumer):
|
class CeleryLogWebsocket(JsonWebsocketConsumer):
|
||||||
task = ''
|
|
||||||
task_log_f = None
|
|
||||||
disconnected = False
|
disconnected = False
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
task_id = self.scope['url_route']['kwargs']['task_id']
|
|
||||||
log_path = get_celery_task_log_path(task_id)
|
|
||||||
try:
|
|
||||||
self.task_log_f = open(log_path)
|
|
||||||
except OSError:
|
|
||||||
self.send({'message': "Task {} log not found".format(task_id)})
|
|
||||||
self.disconnect(None)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.accept()
|
self.accept()
|
||||||
self.send_log_to_client()
|
|
||||||
|
def receive(self, text_data=None, bytes_data=None, **kwargs):
|
||||||
|
data = json.loads(text_data)
|
||||||
|
task_id = data.get("task")
|
||||||
|
if task_id:
|
||||||
|
self.handle_task(task_id)
|
||||||
|
|
||||||
|
def handle_task(self, task_id):
|
||||||
|
log_path = get_celery_task_log_path(task_id)
|
||||||
|
|
||||||
|
def func():
|
||||||
|
task_log_f = None
|
||||||
|
|
||||||
|
while not self.disconnected:
|
||||||
|
if not os.path.exists(log_path):
|
||||||
|
self.send_json({'message': '.', 'task': task_id})
|
||||||
|
time.sleep(0.5)
|
||||||
|
continue
|
||||||
|
self.send_json({'message': '\r\n'})
|
||||||
|
try:
|
||||||
|
task_log_f = open(log_path)
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
return
|
||||||
|
|
||||||
|
while not self.disconnected:
|
||||||
|
data = task_log_f.readline()
|
||||||
|
if data:
|
||||||
|
data = data.replace('\n', '\r\n')
|
||||||
|
self.send_json({'message': data, 'task': task_id})
|
||||||
|
if data.startswith('Task') and data.find('succeeded'):
|
||||||
|
break
|
||||||
|
time.sleep(0.2)
|
||||||
|
task_log_f.close()
|
||||||
|
thread = threading.Thread(target=func)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
def disconnect(self, close_code):
|
def disconnect(self, close_code):
|
||||||
self.disconnected = True
|
self.disconnected = True
|
||||||
if self.task_log_f and not self.task_log_f.closed:
|
|
||||||
self.task_log_f.close()
|
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def send_log_to_client(self):
|
|
||||||
def func():
|
|
||||||
while not self.disconnected:
|
|
||||||
data = self.task_log_f.read(4096)
|
|
||||||
if data:
|
|
||||||
data = data.replace('\n', '\r\n')
|
|
||||||
self.send_json({'message': data})
|
|
||||||
time.sleep(0.2)
|
|
||||||
thread = threading.Thread(target=func)
|
|
||||||
thread.start()
|
|
||||||
|
|
|
@ -5,12 +5,12 @@ from rest_framework.viewsets import ModelViewSet
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
from common.mixins import CommonApiMixin
|
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
|
from ..models import Organization
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'RootOrgViewMixin', 'OrgMembershipModelViewSetMixin', 'OrgModelViewSet',
|
'RootOrgViewMixin', 'OrgMembershipModelViewSetMixin', 'OrgModelViewSet',
|
||||||
'OrgBulkModelViewSet',
|
'OrgBulkModelViewSet', 'OrgQuerySetMixin',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,15 @@ class RootOrgViewMixin:
|
||||||
|
|
||||||
class OrgQuerySetMixin:
|
class OrgQuerySetMixin:
|
||||||
def get_queryset(self):
|
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'):
|
if hasattr(self, 'swagger_fake_view'):
|
||||||
return queryset[:1]
|
return queryset[:1]
|
||||||
if hasattr(self, 'action') and self.action == 'list' and \
|
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 common.utils import get_logger
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
set_current_org, get_current_org, current_org,
|
set_current_org, get_current_org, current_org,
|
||||||
|
get_org_filters
|
||||||
)
|
)
|
||||||
from ..models import Organization
|
from ..models import Organization
|
||||||
|
|
||||||
|
@ -19,42 +20,38 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OrgQuerySet(models.QuerySet):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OrgManager(models.Manager):
|
class OrgManager(models.Manager):
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super(OrgManager, self).get_queryset()
|
queryset = super().get_queryset()
|
||||||
kwargs = {}
|
kwargs = get_org_filters()
|
||||||
|
if kwargs:
|
||||||
_current_org = get_current_org()
|
return queryset.filter(**kwargs)
|
||||||
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)
|
|
||||||
return queryset
|
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):
|
def set_current_org(self, org):
|
||||||
if isinstance(org, str):
|
if isinstance(org, str):
|
||||||
org = Organization.get_instance(org)
|
org = Organization.get_instance(org)
|
||||||
set_current_org(org)
|
set_current_org(org)
|
||||||
return self
|
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):
|
class OrgModelMixin(models.Model):
|
||||||
org_id = models.CharField(max_length=36, blank=True, default='',
|
org_id = models.CharField(max_length=36, blank=True, default='',
|
||||||
|
@ -65,9 +62,12 @@ class OrgModelMixin(models.Model):
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
org = get_current_org()
|
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
|
self.org_id = org.id
|
||||||
elif org is not None and org.is_default():
|
elif org.is_default():
|
||||||
self.org_id = ''
|
self.org_id = ''
|
||||||
return super().save(*args, **kwargs)
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
|
|
||||||
|
import re
|
||||||
from rest_framework.serializers import ModelSerializer
|
from rest_framework.serializers import ModelSerializer
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
from assets.models import Asset, Domain, AdminUser, SystemUser, Label
|
||||||
|
from assets.const import (
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN,
|
||||||
|
GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
|
)
|
||||||
from perms.models import AssetPermission
|
from perms.models import AssetPermission
|
||||||
from common.serializers import AdaptedBulkListSerializer
|
from common.serializers import AdaptedBulkListSerializer
|
||||||
from .utils import set_current_org, get_current_org
|
from .utils import set_current_org, get_current_org
|
||||||
|
@ -18,6 +22,15 @@ class OrgSerializer(ModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
read_only_fields = ['created_by', 'date_created']
|
read_only_fields = ['created_by', 'date_created']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def validate_name(name):
|
||||||
|
pattern = GENERAL_LIMIT_SPECIAL_CHARACTERS_PATTERN
|
||||||
|
res = re.match(pattern, name)
|
||||||
|
if res is None:
|
||||||
|
msg = GENERAL_LIMIT_SPECIAL_CHARACTERS_ERROR_MSG
|
||||||
|
raise serializers.ValidationError(msg)
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
class OrgReadSerializer(ModelSerializer):
|
class OrgReadSerializer(ModelSerializer):
|
||||||
admins = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
|
admins = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
import traceback
|
||||||
from werkzeug.local import LocalProxy
|
from werkzeug.local import LocalProxy
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
@ -82,4 +83,31 @@ def tmp_to_org(org):
|
||||||
set_current_org(ori_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)
|
current_org = LocalProxy(get_current_org)
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from rest_framework.views import Response
|
from rest_framework.views import Response
|
||||||
from django.shortcuts import get_object_or_404
|
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 common.permissions import IsOrgAdmin
|
||||||
|
from orgs.mixins.api import OrgModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
from ..models import AssetPermission
|
from ..models import AssetPermission
|
||||||
from ..hands import (
|
from ..hands import (
|
||||||
|
@ -24,15 +23,21 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionViewSet(viewsets.ModelViewSet):
|
class AssetPermissionViewSet(OrgModelViewSet):
|
||||||
"""
|
"""
|
||||||
资产授权列表的增删改查api
|
资产授权列表的增删改查api
|
||||||
"""
|
"""
|
||||||
queryset = AssetPermission.objects.all()
|
model = AssetPermission
|
||||||
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
||||||
filter_fields = ['name']
|
filter_fields = ['name']
|
||||||
permission_classes = (IsOrgAdmin,)
|
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):
|
def get_serializer_class(self):
|
||||||
if self.action in ("list", 'retrieve') and \
|
if self.action in ("list", 'retrieve') and \
|
||||||
self.request.query_params.get("display"):
|
self.request.query_params.get("display"):
|
||||||
|
@ -68,14 +73,17 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
node_id = self.request.query_params.get('node_id')
|
node_id = self.request.query_params.get('node_id')
|
||||||
node_name = self.request.query_params.get('node')
|
node_name = self.request.query_params.get('node')
|
||||||
if node_id:
|
if node_id:
|
||||||
node = get_object_or_none(Node, pk=node_id)
|
_nodes = Node.objects.filter(pk=node_id)
|
||||||
elif node_name:
|
elif node_name:
|
||||||
node = get_object_or_none(Node, name=node_name)
|
_nodes = Node.objects.filter(value=node_name)
|
||||||
else:
|
else:
|
||||||
return queryset
|
return queryset
|
||||||
if not node:
|
if not _nodes:
|
||||||
return queryset.none()
|
return queryset.none()
|
||||||
nodes = node.get_ancestors(with_self=True)
|
|
||||||
|
nodes = set()
|
||||||
|
for node in _nodes:
|
||||||
|
nodes |= set(node.get_ancestors(with_self=True))
|
||||||
queryset = queryset.filter(nodes__in=nodes)
|
queryset = queryset.filter(nodes__in=nodes)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
@ -160,19 +168,14 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
queryset = queryset.distinct()
|
queryset = queryset.distinct()
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return self.queryset.all().prefetch_related(
|
|
||||||
"nodes", "assets", "users", "user_groups", "system_users"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
class AssetPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
||||||
class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
|
|
||||||
"""
|
"""
|
||||||
将用户从授权中移除,Detail页面会调用
|
将用户从授权中移除,Detail页面会调用
|
||||||
"""
|
"""
|
||||||
|
model = AssetPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -187,10 +190,10 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
|
||||||
return Response({"error": serializer.errors})
|
return Response({"error": serializer.errors})
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
|
class AssetPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
||||||
|
model = AssetPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -205,13 +208,13 @@ class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
|
||||||
return Response({"error": serializer.errors})
|
return Response({"error": serializer.errors})
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
|
class AssetPermissionRemoveAssetApi(generics.RetrieveUpdateAPIView):
|
||||||
"""
|
"""
|
||||||
将用户从授权中移除,Detail页面会调用
|
将用户从授权中移除,Detail页面会调用
|
||||||
"""
|
"""
|
||||||
|
model = AssetPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -226,10 +229,10 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
|
||||||
return Response({"error": serializer.errors})
|
return Response({"error": serializer.errors})
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
|
class AssetPermissionAddAssetApi(generics.RetrieveUpdateAPIView):
|
||||||
|
model = AssetPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -244,7 +247,7 @@ class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
|
||||||
return Response({"error": serializer.errors})
|
return Response({"error": serializer.errors})
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionAssetsApi(ListAPIView):
|
class AssetPermissionAssetsApi(generics.ListAPIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionAssetsSerializer
|
serializer_class = serializers.AssetPermissionAssetsSerializer
|
||||||
filter_fields = ("hostname", "ip")
|
filter_fields = ("hostname", "ip")
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
from rest_framework.generics import get_object_or_404
|
from rest_framework.generics import get_object_or_404
|
||||||
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
from common.permissions import IsValidUser, IsOrgAdminOrAppUser
|
||||||
from common.utils import get_logger
|
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
|
from ..hands import User, UserGroup
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,15 +17,24 @@ __all__ = [
|
||||||
|
|
||||||
class UserPermissionMixin:
|
class UserPermissionMixin:
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
current_org = None
|
||||||
obj = None
|
obj = None
|
||||||
|
|
||||||
def initial(self, *args, **kwargs):
|
def initial(self, *args, **kwargs):
|
||||||
super().initial(*args, *kwargs)
|
super().initial(*args, *kwargs)
|
||||||
|
self.current_org = get_current_org()
|
||||||
|
set_to_root_org()
|
||||||
self.obj = self.get_obj()
|
self.obj = self.get_obj()
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
# def dispatch(self, request, *args, **kwargs):
|
||||||
set_to_root_org()
|
# """不能这么做,校验权限时拿不到组织了"""
|
||||||
return super().get(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):
|
def get_obj(self):
|
||||||
user_id = self.kwargs.get('pk', '')
|
user_id = self.kwargs.get('pk', '')
|
||||||
|
@ -40,6 +49,13 @@ class UserPermissionMixin:
|
||||||
self.permission_classes = (IsValidUser,)
|
self.permission_classes = (IsValidUser,)
|
||||||
return super().get_permissions()
|
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:
|
class UserGroupPermissionMixin:
|
||||||
obj = None
|
obj = None
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework import viewsets, generics
|
|
||||||
from rest_framework.views import Response
|
from rest_framework.views import Response
|
||||||
|
|
||||||
from common.permissions import IsOrgAdmin
|
from common.permissions import IsOrgAdmin
|
||||||
|
from orgs.mixins.api import OrgModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from ..models import RemoteAppPermission
|
from ..models import RemoteAppPermission
|
||||||
from ..serializers import (
|
from ..serializers import (
|
||||||
RemoteAppPermissionSerializer,
|
RemoteAppPermissionSerializer,
|
||||||
|
@ -20,18 +21,18 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppPermissionViewSet(viewsets.ModelViewSet):
|
class RemoteAppPermissionViewSet(OrgModelViewSet):
|
||||||
|
model = RemoteAppPermission
|
||||||
filter_fields = ('name', )
|
filter_fields = ('name', )
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = RemoteAppPermission.objects.all()
|
|
||||||
serializer_class = RemoteAppPermissionSerializer
|
serializer_class = RemoteAppPermissionSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
||||||
|
model = RemoteAppPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = RemoteAppPermissionUpdateUserSerializer
|
serializer_class = RemoteAppPermissionUpdateUserSerializer
|
||||||
queryset = RemoteAppPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -46,9 +47,9 @@ class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
||||||
|
model = RemoteAppPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = RemoteAppPermissionUpdateUserSerializer
|
serializer_class = RemoteAppPermissionUpdateUserSerializer
|
||||||
queryset = RemoteAppPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -63,9 +64,9 @@ class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
|
class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
|
||||||
|
model = RemoteAppPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
|
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
|
||||||
queryset = RemoteAppPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
@ -80,9 +81,9 @@ class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView):
|
class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView):
|
||||||
|
model = RemoteAppPermission
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
|
serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer
|
||||||
queryset = RemoteAppPermission.objects.all()
|
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
perm = self.get_object()
|
perm = self.get_object()
|
||||||
|
|
|
@ -34,7 +34,7 @@ class UserNodeTreeMixin:
|
||||||
|
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
assets_amount = self.tree.valid_assets_amount(node.key)
|
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
|
continue
|
||||||
node.assets_amount = assets_amount
|
node.assets_amount = assets_amount
|
||||||
data = ParserNode.parse_node_to_tree_node(node)
|
data = ParserNode.parse_node_to_tree_node(node)
|
||||||
|
|
|
@ -3,12 +3,10 @@
|
||||||
import uuid
|
import uuid
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from rest_framework.views import APIView, Response
|
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.permissions import IsValidUser, IsOrgAdminOrAppUser
|
||||||
from common.tree import TreeNodeSerializer
|
from common.tree import TreeNodeSerializer
|
||||||
|
from orgs.mixins import generics
|
||||||
from ..utils import (
|
from ..utils import (
|
||||||
RemoteAppPermissionUtil, construct_remote_apps_tree_root,
|
RemoteAppPermissionUtil, construct_remote_apps_tree_root,
|
||||||
parse_remote_app_to_tree_node,
|
parse_remote_app_to_tree_node,
|
||||||
|
@ -25,7 +23,7 @@ __all__ = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedRemoteAppsApi(ListAPIView):
|
class UserGrantedRemoteAppsApi(generics.ListAPIView):
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = RemoteAppSerializer
|
serializer_class = RemoteAppSerializer
|
||||||
filter_fields = ['name', 'id']
|
filter_fields = ['name', 'id']
|
||||||
|
@ -68,7 +66,7 @@ class UserGrantedRemoteAppsAsTreeApi(UserGrantedRemoteAppsApi):
|
||||||
return super().get_serializer(data, many=True)
|
return super().get_serializer(data, many=True)
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, ListAPIView):
|
class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView):
|
||||||
permission_classes = (IsOrgAdminOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.RemoteAppSystemUserSerializer
|
serializer_class = serializers.RemoteAppSystemUserSerializer
|
||||||
only_fields = serializers.RemoteAppSystemUserSerializer.Meta.only_fields
|
only_fields = serializers.RemoteAppSystemUserSerializer.Meta.only_fields
|
||||||
|
@ -110,7 +108,7 @@ class ValidateUserRemoteAppPermissionApi(APIView):
|
||||||
|
|
||||||
# RemoteApp permission
|
# RemoteApp permission
|
||||||
|
|
||||||
class UserGroupGrantedRemoteAppsApi(ListAPIView):
|
class UserGroupGrantedRemoteAppsApi(generics.ListAPIView):
|
||||||
permission_classes = (IsOrgAdminOrAppUser, )
|
permission_classes = (IsOrgAdminOrAppUser, )
|
||||||
serializer_class = RemoteAppSerializer
|
serializer_class = RemoteAppSerializer
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,15 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from users.models import User, UserGroup
|
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 assets.serializers import NodeSerializer
|
||||||
from applications.serializers import RemoteAppSerializer
|
from applications.serializers import RemoteAppSerializer
|
||||||
from applications.models import RemoteApp
|
from applications.models import RemoteApp
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'User', 'UserGroup',
|
||||||
|
'Asset', 'SystemUser', 'Node', 'Label', 'FavoriteAsset',
|
||||||
|
'NodeSerializer', 'RemoteAppSerializer',
|
||||||
|
'RemoteApp'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ class BasePermissionQuerySet(models.QuerySet):
|
||||||
return self.filter(is_active=False)
|
return self.filter(is_active=False)
|
||||||
|
|
||||||
def invalid(self):
|
def invalid(self):
|
||||||
now = timezone.now
|
now = timezone.now()
|
||||||
q = (
|
q = (
|
||||||
Q(is_active=False) |
|
Q(is_active=False) |
|
||||||
Q(date_start__gt=now) |
|
Q(date_start__gt=now) |
|
||||||
|
|
|
@ -13,7 +13,7 @@ from common.utils import get_logger, timeit, lazyproperty
|
||||||
from common.tree import TreeNode
|
from common.tree import TreeNode
|
||||||
from assets.utils import TreeService
|
from assets.utils import TreeService
|
||||||
from ..models import AssetPermission
|
from ..models import AssetPermission
|
||||||
from ..hands import Node, Asset, SystemUser
|
from ..hands import Node, Asset, SystemUser, User, FavoriteAsset
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
|
|
||||||
|
@ -293,6 +293,20 @@ class AssetPermissionUtilV2(AssetPermissionUtilCacheMixin):
|
||||||
parent=user_tree.root,
|
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):
|
def set_user_tree_to_local(self, user_tree):
|
||||||
self._user_tree = user_tree
|
self._user_tree = user_tree
|
||||||
self._user_tree_filter_id = self._filter_id
|
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.add_single_assets_node_to_user_tree(user_tree)
|
||||||
self.parse_user_tree_to_full_tree(user_tree)
|
self.parse_user_tree_to_full_tree(user_tree)
|
||||||
self.add_empty_node_if_need(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_cache_if_need(user_tree)
|
||||||
self.set_user_tree_to_local(user_tree)
|
self.set_user_tree_to_local(user_tree)
|
||||||
return user_tree
|
return user_tree
|
||||||
|
|
|
@ -101,9 +101,11 @@ class LDAPUserListApi(generics.ListAPIView):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if hasattr(self, 'swagger_fake_view'):
|
if hasattr(self, 'swagger_fake_view'):
|
||||||
return []
|
return []
|
||||||
util = LDAPUtil()
|
q = self.request.query_params.get('search')
|
||||||
try:
|
try:
|
||||||
users = util.search_user_items()
|
util = LDAPUtil()
|
||||||
|
extra_filter = util.construct_extra_filter(util.SEARCH_FIELD_ALL, q)
|
||||||
|
users = util.search_user_items(extra_filter)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
users = []
|
users = []
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
@ -112,20 +114,6 @@ class LDAPUserListApi(generics.ListAPIView):
|
||||||
user['id'] = user['username']
|
user['id'] = user['username']
|
||||||
return users
|
return users
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
search = self.request.query_params.get('search')
|
|
||||||
if not search:
|
|
||||||
return queryset
|
|
||||||
search = search.lower()
|
|
||||||
queryset = [
|
|
||||||
q for q in queryset
|
|
||||||
if
|
|
||||||
search in q['username'].lower()
|
|
||||||
or search in q['name'].lower()
|
|
||||||
or search in q['email'].lower()
|
|
||||||
]
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def sort_queryset(self, queryset):
|
def sort_queryset(self, queryset):
|
||||||
order_by = self.request.query_params.get('order')
|
order_by = self.request.query_params.get('order')
|
||||||
if not order_by:
|
if not order_by:
|
||||||
|
@ -139,7 +127,7 @@ class LDAPUserListApi(generics.ListAPIView):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
queryset = self.filter_queryset(self.get_queryset())
|
queryset = self.get_queryset()
|
||||||
queryset = self.sort_queryset(queryset)
|
queryset = self.sort_queryset(queryset)
|
||||||
page = self.paginate_queryset(queryset)
|
page = self.paginate_queryset(queryset)
|
||||||
if page is not None:
|
if page is not None:
|
||||||
|
|
|
@ -22,6 +22,9 @@ class LDAPOUGroupException(Exception):
|
||||||
class LDAPUtil:
|
class LDAPUtil:
|
||||||
_conn = None
|
_conn = None
|
||||||
|
|
||||||
|
SEARCH_FIELD_ALL = 'all'
|
||||||
|
SEARCH_FIELD_USERNAME = 'username'
|
||||||
|
|
||||||
def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None,
|
def __init__(self, use_settings_config=True, server_uri=None, bind_dn=None,
|
||||||
password=None, use_ssl=None, search_ougroup=None,
|
password=None, use_ssl=None, search_ougroup=None,
|
||||||
search_filter=None, attr_map=None, auth_ldap=None):
|
search_filter=None, attr_map=None, auth_ldap=None):
|
||||||
|
@ -81,9 +84,13 @@ class LDAPUtil:
|
||||||
user_item[attr] = value
|
user_item[attr] = value
|
||||||
return user_item
|
return user_item
|
||||||
|
|
||||||
def _search_user_items_ou(self, search_ou, cookie=None):
|
def _search_user_items_ou(self, search_ou, extra_filter=None, cookie=None):
|
||||||
|
search_filter = self.search_filter % {"user": "*"}
|
||||||
|
if extra_filter:
|
||||||
|
search_filter = '(&{}{})'.format(search_filter, extra_filter)
|
||||||
|
|
||||||
ok = self.connection.search(
|
ok = self.connection.search(
|
||||||
search_ou, self.search_filter % ({"user": "*"}),
|
search_ou, search_filter,
|
||||||
attributes=list(self.attr_map.values()),
|
attributes=list(self.attr_map.values()),
|
||||||
paged_size=self.paged_size, paged_cookie=cookie
|
paged_size=self.paged_size, paged_cookie=cookie
|
||||||
)
|
)
|
||||||
|
@ -108,24 +115,43 @@ class LDAPUtil:
|
||||||
cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
|
cookie = self.connection.result['controls']['1.2.840.113556.1.4.319']['value']['cookie']
|
||||||
return cookie
|
return cookie
|
||||||
|
|
||||||
def search_user_items(self):
|
def search_user_items(self, extra_filter=None):
|
||||||
user_items = []
|
user_items = []
|
||||||
logger.info("Search user items")
|
logger.info("Search user items")
|
||||||
|
|
||||||
for search_ou in str(self.search_ougroup).split("|"):
|
for search_ou in str(self.search_ougroup).split("|"):
|
||||||
logger.info("Search user search ou: {}".format(search_ou))
|
logger.info("Search user search ou: {}".format(search_ou))
|
||||||
_user_items = self._search_user_items_ou(search_ou)
|
_user_items = self._search_user_items_ou(search_ou, extra_filter=extra_filter)
|
||||||
user_items.extend(_user_items)
|
user_items.extend(_user_items)
|
||||||
while self._cookie():
|
while self._cookie():
|
||||||
logger.info("Page Search user search ou: {}".format(search_ou))
|
logger.info("Page Search user search ou: {}".format(search_ou))
|
||||||
_user_items = self._search_user_items_ou(search_ou, self._cookie())
|
_user_items = self._search_user_items_ou(search_ou, extra_filter, self._cookie())
|
||||||
user_items.extend(_user_items)
|
user_items.extend(_user_items)
|
||||||
logger.info("Search user items end")
|
logger.info("Search user items end")
|
||||||
return user_items
|
return user_items
|
||||||
|
|
||||||
|
def construct_extra_filter(self, field, q):
|
||||||
|
if not q:
|
||||||
|
return None
|
||||||
|
extra_filter = ''
|
||||||
|
if field == self.SEARCH_FIELD_ALL:
|
||||||
|
for attr in self.attr_map.values():
|
||||||
|
extra_filter += '({}={})'.format(attr, q)
|
||||||
|
extra_filter = '(|{})'.format(extra_filter)
|
||||||
|
return extra_filter
|
||||||
|
|
||||||
|
if field == self.SEARCH_FIELD_USERNAME and isinstance(q, list):
|
||||||
|
attr = self.attr_map.get('username')
|
||||||
|
for username in q:
|
||||||
|
extra_filter += '({}={})'.format(attr, username)
|
||||||
|
extra_filter = '(|{})'.format(extra_filter)
|
||||||
|
return extra_filter
|
||||||
|
|
||||||
def search_filter_user_items(self, username_list):
|
def search_filter_user_items(self, username_list):
|
||||||
user_items = self.search_user_items()
|
extra_filter = self.construct_extra_filter(
|
||||||
if username_list:
|
self.SEARCH_FIELD_USERNAME, username_list
|
||||||
user_items = [u for u in user_items if u['username'] in username_list]
|
)
|
||||||
|
user_items = self.search_user_items(extra_filter)
|
||||||
return user_items
|
return user_items
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -267,7 +267,7 @@ function requestApi(props) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: props.url,
|
url: props.url,
|
||||||
type: props.method || "PATCH",
|
type: props.method || "PATCH",
|
||||||
data: props.body,
|
data: props.body || props.data,
|
||||||
contentType: props.content_type || "application/json; charset=utf-8",
|
contentType: props.content_type || "application/json; charset=utf-8",
|
||||||
dataType: props.data_type || "json"
|
dataType: props.data_type || "json"
|
||||||
}).done(function (data, textStatue, jqXHR) {
|
}).done(function (data, textStatue, jqXHR) {
|
||||||
|
@ -579,6 +579,9 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
ajax: {
|
ajax: {
|
||||||
url: options.ajax_url,
|
url: options.ajax_url,
|
||||||
error: function (jqXHR, textStatus, errorThrown) {
|
error: function (jqXHR, textStatus, errorThrown) {
|
||||||
|
if (jqXHR.responseText && jqXHR.responseText.indexOf("%(value)s") !== -1 ) {
|
||||||
|
return
|
||||||
|
}
|
||||||
var msg = gettext("Unknown error occur");
|
var msg = gettext("Unknown error occur");
|
||||||
if (jqXHR.responseJSON) {
|
if (jqXHR.responseJSON) {
|
||||||
if (jqXHR.responseJSON.error) {
|
if (jqXHR.responseJSON.error) {
|
||||||
|
@ -953,8 +956,13 @@ function initPopover($container, $progress, $idPassword, $el, password_check_rul
|
||||||
function rootNodeAddDom(ztree, callback) {
|
function rootNodeAddDom(ztree, callback) {
|
||||||
var refreshIcon = "<a id='tree-refresh'><i class='fa fa-refresh'></i></a>";
|
var refreshIcon = "<a id='tree-refresh'><i class='fa fa-refresh'></i></a>";
|
||||||
var rootNode = ztree.getNodes()[0];
|
var rootNode = ztree.getNodes()[0];
|
||||||
var $rootNodeRef = $("#" + rootNode.tId + "_a");
|
if (rootNode) {
|
||||||
$rootNodeRef.after(refreshIcon);
|
var $rootNodeRef = $("#" + rootNode.tId + "_a");
|
||||||
|
$rootNodeRef.after(refreshIcon);
|
||||||
|
} else {
|
||||||
|
$rootNodeRef = $('#' + ztree.setting.treeId);
|
||||||
|
$rootNodeRef.html(refreshIcon);
|
||||||
|
}
|
||||||
var refreshIconRef = $('#tree-refresh');
|
var refreshIconRef = $('#tree-refresh');
|
||||||
refreshIconRef.bind('click', function () {
|
refreshIconRef.bind('click', function () {
|
||||||
ztree.destroy();
|
ztree.destroy();
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -24,10 +24,10 @@ logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class SessionViewSet(OrgBulkModelViewSet):
|
class SessionViewSet(OrgBulkModelViewSet):
|
||||||
queryset = Session.objects.all()
|
model = Session
|
||||||
serializer_class = serializers.SessionSerializer
|
serializer_class = serializers.SessionSerializer
|
||||||
permission_classes = (IsOrgAdminOrAppUser, )
|
permission_classes = (IsOrgAdminOrAppUser, )
|
||||||
filter_fields = [
|
filterset_fields = [
|
||||||
"user", "asset", "system_user", "remote_addr",
|
"user", "asset", "system_user", "remote_addr",
|
||||||
"protocol", "terminal", "is_finished",
|
"protocol", "terminal", "is_finished",
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,6 +6,7 @@ from common import utils
|
||||||
|
|
||||||
TYPE_ENGINE_MAPPING = {
|
TYPE_ENGINE_MAPPING = {
|
||||||
'elasticsearch': 'terminal.backends.command.es',
|
'elasticsearch': 'terminal.backends.command.es',
|
||||||
|
'es': 'terminal.backends.command.es',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from jms_storage.es import ESStorage
|
from jms_storage.es import ESStorage
|
||||||
from .base import CommandBase
|
from .base import CommandBase
|
||||||
from .models import AbstractSessionCommand
|
from .models import AbstractSessionCommand
|
||||||
|
@ -14,6 +15,13 @@ class CommandStore(ESStorage, CommandBase):
|
||||||
user=None, asset=None, system_user=None,
|
user=None, asset=None, system_user=None,
|
||||||
input=None, session=None):
|
input=None, session=None):
|
||||||
|
|
||||||
|
if date_from is not None:
|
||||||
|
if isinstance(date_from, float):
|
||||||
|
date_from = datetime.fromtimestamp(date_from)
|
||||||
|
if date_to is not None:
|
||||||
|
if isinstance(date_to, float):
|
||||||
|
date_to = datetime.fromtimestamp(date_to)
|
||||||
|
|
||||||
data = super().filter(date_from=date_from, date_to=date_to,
|
data = super().filter(date_from=date_from, date_to=date_to,
|
||||||
user=user, asset=asset, system_user=system_user,
|
user=user, asset=asset, system_user=system_user,
|
||||||
input=input, session=session)
|
input=input, session=session)
|
||||||
|
|
|
@ -164,11 +164,14 @@ $(document).ready(function () {
|
||||||
detailRows.push(tr.attr('id'));
|
detailRows.push(tr.attr('id'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
function format(d) {
|
function format(d) {
|
||||||
var output = $("<pre style='border: none; background: none'></pre>");
|
var output = $("<pre style='border: none; background: none'></pre>");
|
||||||
|
|
||||||
|
output.append('$ ', d.input);
|
||||||
|
output.append('\r\n\r\n');
|
||||||
output.append(d.output);
|
output.append(d.output);
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
@ -187,6 +190,17 @@ function initTable() {
|
||||||
$(td).addClass("toggle");
|
$(td).addClass("toggle");
|
||||||
$(td).html("<i class='fa fa-angle-right'></i>");
|
$(td).html("<i class='fa fa-angle-right'></i>");
|
||||||
}},
|
}},
|
||||||
|
{targets: 1, createdCell: function (td, cellData) {
|
||||||
|
var data = htmlEscape(cellData);
|
||||||
|
var interHtml = $("<span></span>");
|
||||||
|
if (data.length > 40) {
|
||||||
|
interHtml.attr('title', data);
|
||||||
|
data = data.slice(0, 40);
|
||||||
|
data += ' ...';
|
||||||
|
}
|
||||||
|
interHtml.html(data);
|
||||||
|
$(td).html(interHtml);
|
||||||
|
}},
|
||||||
{targets: 5, createdCell: function (td, cellData) {
|
{targets: 5, createdCell: function (td, cellData) {
|
||||||
var data = '<a href="{% url "terminal:session-detail" pk=DEFAULT_PK %}">{% trans "Goto" %}</a>'
|
var data = '<a href="{% url "terminal:session-detail" pk=DEFAULT_PK %}">{% trans "Goto" %}</a>'
|
||||||
.replace('{{ DEFAULT_PK }}', cellData);
|
.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
@ -200,7 +214,7 @@ function initTable() {
|
||||||
toggle: true,
|
toggle: true,
|
||||||
ajax_url: commandListUrl,
|
ajax_url: commandListUrl,
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id"}, {data: "input", orderable: false}, {data: "user", orderable: false},
|
{data: "id"}, {data: "input", orderable: false, width: "40%"}, {data: "user", orderable: false},
|
||||||
{data: "asset", orderable: false}, {data: "system_user", orderable: false},
|
{data: "asset", orderable: false}, {data: "system_user", orderable: false},
|
||||||
{data: "session", orderable: false}, {data: "timestamp", width: "160px", orderable: false},
|
{data: "session", orderable: false}, {data: "timestamp", width: "160px", orderable: false},
|
||||||
],
|
],
|
||||||
|
|
|
@ -54,8 +54,12 @@
|
||||||
{% for command in object_list %}
|
{% for command in object_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ forloop.counter }}</td>
|
<td>{{ forloop.counter }}</td>
|
||||||
<td>{{ command.input }}</td>
|
<td>{{ command.input | truncatechars:40 }}</td>
|
||||||
<td><pre style="border: none;background: none">{{ command.output }}</pre></td>
|
<td><pre style="border: none;background: none">
|
||||||
|
$ {{ command.input }}
|
||||||
|
|
||||||
|
{{ command.output }}
|
||||||
|
</pre></td>
|
||||||
<td>{{ command.timestamp|ts_to_date}}</td>
|
<td>{{ command.timestamp|ts_to_date}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
<li><a class="search-item" data-value="asset">{% trans 'Asset' %}</a></li>
|
<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="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="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>#}
|
{# <li><a class="search-item" data-value="protocol">{% trans 'Protocol' %}</a></li>#}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework import generics
|
|
||||||
|
|
||||||
from ..serializers import (
|
from ..serializers import (
|
||||||
UserGroupSerializer,
|
UserGroupSerializer,
|
||||||
UserGroupListSerializer,
|
UserGroupListSerializer,
|
||||||
|
@ -10,6 +8,7 @@ from ..serializers import (
|
||||||
)
|
)
|
||||||
from ..models import UserGroup
|
from ..models import UserGroup
|
||||||
from orgs.mixins.api import OrgBulkModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
|
from orgs.mixins import generics
|
||||||
from common.permissions import IsOrgAdmin
|
from common.permissions import IsOrgAdmin
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,9 +16,9 @@ __all__ = ['UserGroupViewSet', 'UserGroupUpdateUserApi']
|
||||||
|
|
||||||
|
|
||||||
class UserGroupViewSet(OrgBulkModelViewSet):
|
class UserGroupViewSet(OrgBulkModelViewSet):
|
||||||
|
model = UserGroup
|
||||||
filter_fields = ("name",)
|
filter_fields = ("name",)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = UserGroup.objects.all()
|
|
||||||
serializer_class = UserGroupSerializer
|
serializer_class = UserGroupSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
@ -31,6 +30,6 @@ class UserGroupViewSet(OrgBulkModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
class UserGroupUpdateUserApi(generics.RetrieveUpdateAPIView):
|
||||||
queryset = UserGroup.objects.all()
|
model = UserGroup
|
||||||
serializer_class = UserGroupUpdateMemberSerializer
|
serializer_class = UserGroupUpdateMemberSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
|
@ -17,7 +17,7 @@ from common.permissions import (
|
||||||
from common.mixins import CommonApiMixin
|
from common.mixins import CommonApiMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from orgs.utils import current_org
|
from orgs.utils import current_org
|
||||||
from .. import serializers
|
from .. import serializers, utils
|
||||||
from ..models import User
|
from ..models import User
|
||||||
from ..signals import post_user_create
|
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')
|
filter_fields = ('username', 'email', 'name', 'id')
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = User.objects.exclude(role=User.ROLE_APP)
|
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
|
permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return super().get_queryset().prefetch_related('groups')
|
||||||
|
|
||||||
def send_created_signal(self, users):
|
def send_created_signal(self, users):
|
||||||
if not isinstance(users, list):
|
if not isinstance(users, list):
|
||||||
users = [users]
|
users = [users]
|
||||||
|
@ -51,11 +59,6 @@ class UserViewSet(CommonApiMixin, BulkModelViewSet):
|
||||||
current_org.users.add(*users)
|
current_org.users.add(*users)
|
||||||
self.send_created_signal(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):
|
def get_permissions(self):
|
||||||
if self.action in ["retrieve", "list"]:
|
if self.action in ["retrieve", "list"]:
|
||||||
self.permission_classes = (IsOrgAdminOrAppUser,)
|
self.permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
@ -79,9 +82,8 @@ class UserViewSet(CommonApiMixin, BulkModelViewSet):
|
||||||
return super().perform_bulk_update(serializer)
|
return super().perform_bulk_update(serializer)
|
||||||
|
|
||||||
|
|
||||||
class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
|
class UserChangePasswordApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
queryset = User.objects.all()
|
|
||||||
serializer_class = serializers.ChangeUserPasswordSerializer
|
serializer_class = serializers.ChangeUserPasswordSerializer
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
@ -90,13 +92,12 @@ class UserChangePasswordApi(generics.RetrieveUpdateAPIView):
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateGroupApi(generics.RetrieveUpdateAPIView):
|
class UserUpdateGroupApi(UserQuerysetMixin, generics.RetrieveUpdateAPIView):
|
||||||
queryset = User.objects.all()
|
|
||||||
serializer_class = serializers.UserUpdateGroupSerializer
|
serializer_class = serializers.UserUpdateGroupSerializer
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class UserResetPasswordApi(generics.UpdateAPIView):
|
class UserResetPasswordApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
@ -111,8 +112,7 @@ class UserResetPasswordApi(generics.UpdateAPIView):
|
||||||
send_reset_password_mail(user)
|
send_reset_password_mail(user)
|
||||||
|
|
||||||
|
|
||||||
class UserResetPKApi(generics.UpdateAPIView):
|
class UserResetPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
@ -125,8 +125,7 @@ class UserResetPKApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
# 废弃
|
# 废弃
|
||||||
class UserUpdatePKApi(generics.UpdateAPIView):
|
class UserUpdatePKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
|
||||||
serializer_class = serializers.UserPKUpdateSerializer
|
serializer_class = serializers.UserPKUpdateSerializer
|
||||||
permission_classes = (IsCurrentUserOrReadOnly,)
|
permission_classes = (IsCurrentUserOrReadOnly,)
|
||||||
|
|
||||||
|
@ -136,8 +135,7 @@ class UserUpdatePKApi(generics.UpdateAPIView):
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
|
|
||||||
class UserUnblockPKApi(generics.UpdateAPIView):
|
class UserUnblockPKApi(UserQuerysetMixin, generics.UpdateAPIView):
|
||||||
queryset = User.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.UserSerializer
|
serializer_class = serializers.UserSerializer
|
||||||
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
key_prefix_limit = "_LOGIN_LIMIT_{}_{}"
|
||||||
|
@ -165,8 +163,7 @@ class UserProfileApi(generics.RetrieveAPIView):
|
||||||
return super().retrieve(request, *args, **kwargs)
|
return super().retrieve(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class UserResetOTPApi(generics.RetrieveAPIView):
|
class UserResetOTPApi(UserQuerysetMixin, generics.RetrieveAPIView):
|
||||||
queryset = User.objects.all()
|
|
||||||
permission_classes = (IsOrgAdmin,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.ResetOTPSerializer
|
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 common.utils import validate_ssh_public_key
|
||||||
from orgs.mixins.forms import OrgModelForm
|
from orgs.mixins.forms import OrgModelForm
|
||||||
from orgs.utils import current_org
|
|
||||||
from .models import User, UserGroup
|
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):
|
class UserCheckPasswordForm(forms.Form):
|
||||||
|
@ -267,15 +266,23 @@ class UserBulkUpdateForm(OrgModelForm):
|
||||||
users = forms.ModelMultipleChoiceField(
|
users = forms.ModelMultipleChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
label=_('Select users'),
|
label=_('Select users'),
|
||||||
queryset=User.objects.all(),
|
queryset=User.objects.none(),
|
||||||
widget=forms.SelectMultiple(
|
widget=forms.SelectMultiple(
|
||||||
attrs={
|
attrs={
|
||||||
'class': 'select2',
|
'class': 'users-select2',
|
||||||
'data-placeholder': _('Select users')
|
'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:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ['users', 'groups', 'date_expired']
|
fields = ['users', 'groups', 'date_expired']
|
||||||
|
@ -320,25 +327,19 @@ class UserGroupForm(OrgModelForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
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)
|
super().__init__(**kwargs)
|
||||||
if 'initial' not in kwargs:
|
self.set_fields_queryset()
|
||||||
return
|
|
||||||
|
def set_fields_queryset(self):
|
||||||
users_field = self.fields.get('users')
|
users_field = self.fields.get('users')
|
||||||
if instance:
|
if self.instance:
|
||||||
users_field.queryset = instance.users.all()
|
users_field.initial = self.instance.users.all()
|
||||||
|
users_field.queryset = self.instance.users.all()
|
||||||
else:
|
else:
|
||||||
users_field.queryset = User.objects.none()
|
users_field.queryset = User.objects.none()
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
group = super().save(commit=commit)
|
raise Exception("Save by restful api")
|
||||||
users = self.cleaned_data['users']
|
|
||||||
group.users.set(users)
|
|
||||||
return group
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserGroup
|
model = UserGroup
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
import copy
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
@ -12,6 +11,7 @@ from common.serializers import AdaptedBulkListSerializer
|
||||||
from common.permissions import CanUpdateDeleteUser
|
from common.permissions import CanUpdateDeleteUser
|
||||||
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
||||||
from ..models import User, UserGroup
|
from ..models import User, UserGroup
|
||||||
|
from .. import utils
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -118,7 +118,9 @@ class UserPKUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class UserUpdateGroupSerializer(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:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
@ -127,7 +129,7 @@ class UserUpdateGroupSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class UserGroupSerializer(BulkOrgResourceModelSerializer):
|
class UserGroupSerializer(BulkOrgResourceModelSerializer):
|
||||||
users = serializers.PrimaryKeyRelatedField(
|
users = serializers.PrimaryKeyRelatedField(
|
||||||
required=False, many=True, queryset=User.objects.all(), label=_('User')
|
required=False, many=True, queryset=User.objects, label=_('User')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
@ -141,6 +143,14 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer):
|
||||||
'created_by': {'label': _('Created by'), 'read_only': True}
|
'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):
|
def validate_users(self, users):
|
||||||
for user in users:
|
for user in users:
|
||||||
if user.is_super_auditor:
|
if user.is_super_auditor:
|
||||||
|
@ -154,12 +164,20 @@ class UserGroupListSerializer(UserGroupSerializer):
|
||||||
|
|
||||||
|
|
||||||
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
|
class UserGroupUpdateMemberSerializer(serializers.ModelSerializer):
|
||||||
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects.all())
|
users = serializers.PrimaryKeyRelatedField(many=True, queryset=User.objects)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserGroup
|
model = UserGroup
|
||||||
fields = ['id', 'users']
|
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):
|
class ChangeUserPasswordSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% load i18n %}
|
{% 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 float-e-margins">
|
||||||
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
<div class="ibox-content mailbox-content" style="padding-top: 0">
|
||||||
<div class="file-manager ">
|
<div class="file-manager ">
|
||||||
|
@ -11,7 +11,12 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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="mail-box-header">
|
||||||
{# <div class="btn-group" style="float: right">#}
|
{# <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>#}
|
{# <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 () {
|
$(document).ready(function () {
|
||||||
{#loadLabels()#}
|
{#loadLabels()#}
|
||||||
}).on('click', '.labels-menu li', function () {
|
}).on('click', '.labels-menu li', function () {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
$('.select2').select2();
|
$('.select2').select2();
|
||||||
|
usersSelect2Init('.users-select2')
|
||||||
}).on('click', '.field-tag', function() {
|
}).on('click', '.field-tag', function() {
|
||||||
changeField(this);
|
changeField(this);
|
||||||
}).on('click', '#change_all', function () {
|
}).on('click', '#change_all', function () {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
{% bootstrap_field form.comment layout="horizontal" %}
|
{% bootstrap_field form.comment layout="horizontal" %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-4 col-sm-offset-2">
|
<div class="col-sm-4 col-sm-offset-2">
|
||||||
<button class="btn btn-white" type="reset">{% trans 'Cancel' %}</button>
|
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
|
||||||
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Confirm' %}</button>
|
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Confirm' %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var assetTableUrl = "{% url 'api-perms:user-group-assets' pk=object.id %}?cache_policy=1";
|
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 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 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
|
var showAssetHref = true; // Need input default true
|
||||||
|
|
|
@ -325,3 +325,7 @@ def construct_user_email(username, email):
|
||||||
email = '{}@{}'.format(username, settings.EMAIL_SUFFIX)
|
email = '{}@{}'.format(username, settings.EMAIL_SUFFIX)
|
||||||
return email
|
return email
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_org_members(exclude=()):
|
||||||
|
from orgs.utils import current_org
|
||||||
|
return current_org.get_org_members(exclude=exclude)
|
||||||
|
|
|
@ -72,13 +72,18 @@ REDIS_PORT: 6379
|
||||||
# RADIUS_PORT: 1812
|
# RADIUS_PORT: 1812
|
||||||
# RADIUS_SECRET:
|
# RADIUS_SECRET:
|
||||||
|
|
||||||
# LDAP/AD 设置定时同步参数
|
# LDAP/AD settings
|
||||||
|
# 定时同步用户
|
||||||
# 启用/禁用
|
# 启用/禁用
|
||||||
# AUTH_LDAP_SYNC_IS_PERIODIC: True
|
# AUTH_LDAP_SYNC_IS_PERIODIC: True
|
||||||
# 单位: 时
|
# 单位: 时
|
||||||
# AUTH_LDAP_SYNC_INTERVAL: 12
|
# AUTH_LDAP_SYNC_INTERVAL: 12
|
||||||
# Crontab 表达式
|
# Crontab 表达式
|
||||||
# AUTH_LDAP_SYNC_CRONTAB: * 6 * * *
|
# AUTH_LDAP_SYNC_CRONTAB: * 6 * * *
|
||||||
|
#
|
||||||
|
# LDAP 用户登录时仅允许在用户列表中的用户执行 LDAP Server 认证
|
||||||
|
# AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS: False
|
||||||
|
|
||||||
|
|
||||||
# OTP settings
|
# OTP settings
|
||||||
# OTP/MFA 配置
|
# OTP/MFA 配置
|
||||||
|
|
3
jms
3
jms
|
@ -445,8 +445,7 @@ def stop_service(srv, sig=15):
|
||||||
|
|
||||||
def stop_daemon_service():
|
def stop_daemon_service():
|
||||||
pid = get_pid('jms')
|
pid = get_pid('jms')
|
||||||
logging.info("Daemon pid is: {}".format(pid))
|
if pid and check_pid(pid):
|
||||||
if pid:
|
|
||||||
os.kill(pid, 15)
|
os.kill(pid, 15)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import django
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists('../apps'):
|
||||||
|
sys.path.insert(0, '../apps')
|
||||||
|
elif os.path.exists('./apps'):
|
||||||
|
sys.path.insert(0, './apps')
|
||||||
|
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "jumpserver.settings")
|
||||||
|
django.setup()
|
||||||
|
|
||||||
|
from assets.models import *
|
||||||
|
from orgs.utils import Organization
|
||||||
|
|
||||||
|
Organization.root().change_to()
|
||||||
|
|
||||||
|
ns = Node.objects.all()
|
||||||
|
|
||||||
|
for i in ns:
|
||||||
|
try:
|
||||||
|
pkey = i.parent.key
|
||||||
|
except:
|
||||||
|
pkey = ''
|
||||||
|
if i.parent_key != pkey and not i.key.isdigit():
|
||||||
|
print("Node parent not found: {} -> {}".format(i.key, i.parent_key))
|
||||||
|
|
Loading…
Reference in New Issue