mirror of https://github.com/jumpserver/jumpserver
Dev (#1646)
* [Update] 添加org * [Update] 修改url * [Update] 完成基本框架 * [Update] 修改一些逻辑 * [Update] 修改用户view * [Update] 修改资产 * [Update] 修改asset api * [Update] 修改协议小问题 * [Update] stash it * [Update] 修改约束 * [Update] 修改外键为org_id * [Update] 删掉Premiddleware * [Update] 修改Node * [Update] 修改get_current_org 为 proxy对象 current_org * [Bugfix] 解决Node.root() 死循环,移动AdminRequired到permission中 (#1571) * [Update] 修改permission (#1574) * Tmp org (#1579) * [Update] 添加org api, 升级到django 2.0 * [Update] fix some bug * [Update] 修改一些bug * [Update] 添加授权规则org (#1580) * [Update] 修复创建授权规则,显示org_name不是有效UUID的bug * [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug; * Tmp org (#1583) * [Update] 修改一些内容 * [Update] 修改datatable 支持process * [Bugfix] 修复asset queryset 没有valid方法的bug * [Update] 在线/历史/命令model添加org;修复命令记录保存org失败bug (#1584) * [Update] 修复创建授权规则,显示org_name不是有效UUID的bug * [Update] 更新org之间隔离授权规则,解决QuerySet与Manager问题;修复创建用户,显示org_name不是有效UUID之bug; * [Update] 在线/历史/命令model添加org * [Bugfix] 修复命令记录,保存org不成功bug * [Update] Org功能修改 * [Bugfix] 修复merge带来的问题 * [Update] org admin显示资产详情右侧选项卡;修复资产授权添加用户,会显示其他org用户的bug (#1594) * [Bugfix] 修复资产授权添加用户,显示其他org的用户bug * [Update] org admin 显示资产详情右侧选项卡 * Tmp org (#1596) * [Update] 修改index view * [Update] 修改nav * [Update] 修改profile * [Bugfix] 修复org下普通用户打开web终端看不到已被授权的资产和节点bug * [Update] 修改get_all_assets * [Bugfix] 修复节点前面有个空目录 * [Bugfix] 修复merge引起的bug * [Update] Add init * [Update] Node get_all_assets 过滤游离资产,条件nodes_key=None -> nodes=None * [Update] 恢复原来的api地址 * [Update] 修改api * [Bugfix] 修复org下用户查看我的资产不显示已授权节点/资产的bug * [Bugfix] Fix perm name unique * [Bugfix] 修复校验失败api * [Update] Merge with org * [Merge] 修改一下bug * [Update] 暂时修改一些url * [Update] 修改url 为django 2.0 path * [Update] 优化datatable 和显示组织优化 * [Update] 升级url * [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode(… (#1613) * [Bugfix] 修复coco启动失败(load_config_from_server)、硬件刷新,测试连接,str 没有 decode() method的bug * [Bugfix] (task任务系统)修复资产连接性测试、硬件刷新和系统用户连接性测试失败等bug * [Bugfix] 修复一些bug * [Bugfix] 修复一些bug * [Update] 更新org下普通用户的资产详情 (#1619) * [Update] 更新org下普通用户查看资产详情,只显示数据 * [Update] 优化org下普通用户查看资产详情前端代码 * [Update] 创建/更新用户的role选项;密码强度提示信息中英文; (#1623) * [Update] 修改 超级管理员/组织管理员 在 创建/更新 用户时role的选项 问题 * [Update] 用户密码强度提示信息支持中英文 * [Update] 修改token返回 * [Update] Asset返回org name * [Update] 修改支持xpack * [Update] 修改url * [Bugfix] 修复不登录就能查看资产的bug * [Update] 用户修改 * [Bugfix] ... * [Bugfix] 修复跳转错误的问题 * [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; (#1644) * [Update] 更新xpack下orgs的翻译信息 * [Update] 更新model Label,继承OrgModelMixin; * [Update] xpack/orgs组织添加删除功能-js; 修复Label继承Org后bug; * [Bugfix] 修复小bug * [Update] 优化一些api * [Update] 优化用户资产页面 * [Update] 更新 xpack/orgs 删除功能:限制在当前org下删除当前org (#1645) * [Update] 修改版本号pull/1651/head^2 1.4.0
parent
dded4e10fb
commit
d5451a482a
|
@ -32,3 +32,4 @@ django.db
|
||||||
celerybeat-schedule.db
|
celerybeat-schedule.db
|
||||||
data/static
|
data/static
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
xpack
|
||||||
|
|
|
@ -2,4 +2,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = "1.3.3"
|
__version__ = "1.4.0"
|
||||||
|
|
|
@ -20,7 +20,7 @@ from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
from common.mixins import IDInFilterMixin
|
from common.mixins import IDInFilterMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsSuperUser
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import AdminUser, Asset
|
from ..models import AdminUser, Asset
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import test_admin_user_connectability_manual
|
from ..tasks import test_admin_user_connectability_manual
|
||||||
|
@ -39,19 +39,19 @@ class AdminUserViewSet(IDInFilterMixin, BulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
queryset = AdminUser.objects.all()
|
queryset = AdminUser.objects.all()
|
||||||
serializer_class = serializers.AdminUserSerializer
|
serializer_class = serializers.AdminUserSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class AdminUserAuthApi(generics.UpdateAPIView):
|
class AdminUserAuthApi(generics.UpdateAPIView):
|
||||||
queryset = AdminUser.objects.all()
|
queryset = AdminUser.objects.all()
|
||||||
serializer_class = serializers.AdminUserAuthSerializer
|
serializer_class = serializers.AdminUserAuthSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
class ReplaceNodesAdminUserApi(generics.UpdateAPIView):
|
||||||
queryset = AdminUser.objects.all()
|
queryset = AdminUser.objects.all()
|
||||||
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
serializer_class = serializers.ReplaceNodeAdminUserSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
admin_user = self.get_object()
|
admin_user = self.get_object()
|
||||||
|
@ -75,7 +75,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
|
||||||
Test asset admin user connectivity
|
Test asset admin user connectivity
|
||||||
"""
|
"""
|
||||||
queryset = AdminUser.objects.all()
|
queryset = AdminUser.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
admin_user = self.get_object()
|
admin_user = self.get_object()
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics, permissions
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
|
from rest_framework_bulk import ListBulkCreateUpdateDestroyAPIView
|
||||||
|
@ -13,7 +14,7 @@ from django.db.models import Q
|
||||||
|
|
||||||
from common.mixins import IDInFilterMixin
|
from common.mixins import IDInFilterMixin
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser
|
from common.permissions import IsOrgAdmin, IsAppUser, IsOrgAdminOrAppUser
|
||||||
from ..models import Asset, SystemUser, AdminUser, Node
|
from ..models import Asset, SystemUser, AdminUser, Node
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import update_asset_hardware_info_manual, \
|
from ..tasks import update_asset_hardware_info_manual, \
|
||||||
|
@ -39,38 +40,42 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
|
||||||
queryset = Asset.objects.all()
|
queryset = Asset.objects.all()
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
pagination_class = LimitOffsetPagination
|
pagination_class = LimitOffsetPagination
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
|
||||||
def get_queryset(self):
|
def filter_node(self):
|
||||||
queryset = super().get_queryset()\
|
|
||||||
.prefetch_related('labels', 'nodes')\
|
|
||||||
.select_related('admin_user')
|
|
||||||
admin_user_id = self.request.query_params.get('admin_user_id')
|
|
||||||
node_id = self.request.query_params.get("node_id")
|
node_id = self.request.query_params.get("node_id")
|
||||||
|
if not node_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
node = get_object_or_404(Node, id=node_id)
|
||||||
show_current_asset = self.request.query_params.get("show_current_asset")
|
show_current_asset = self.request.query_params.get("show_current_asset")
|
||||||
|
|
||||||
if admin_user_id:
|
if node.is_root():
|
||||||
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
|
if show_current_asset:
|
||||||
queryset = queryset.filter(admin_user=admin_user)
|
self.queryset = self.queryset.filter(
|
||||||
|
|
||||||
if node_id and show_current_asset:
|
|
||||||
node = get_object_or_404(Node, id=node_id)
|
|
||||||
if node.is_root():
|
|
||||||
queryset = queryset.filter(
|
|
||||||
Q(nodes=node_id) | Q(nodes__isnull=True)
|
Q(nodes=node_id) | Q(nodes__isnull=True)
|
||||||
).distinct()
|
).distinct()
|
||||||
else:
|
return
|
||||||
queryset = queryset.filter(nodes=node).distinct()
|
if show_current_asset:
|
||||||
|
self.queryset = self.queryset.filter(nodes=node).distinct()
|
||||||
|
else:
|
||||||
|
self.queryset = self.queryset.filter(
|
||||||
|
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
|
||||||
|
).distinct()
|
||||||
|
|
||||||
if node_id and not show_current_asset:
|
def filter_admin_user_id(self):
|
||||||
node = get_object_or_404(Node, id=node_id)
|
admin_user_id = self.request.query_params.get('admin_user_id')
|
||||||
if node.is_root():
|
if admin_user_id:
|
||||||
queryset = Asset.objects.all()
|
admin_user = get_object_or_404(AdminUser, id=admin_user_id)
|
||||||
else:
|
self.queryset = self.queryset.filter(admin_user=admin_user)
|
||||||
queryset = queryset.filter(
|
|
||||||
nodes__key__regex='^{}(:[0-9]+)*$'.format(node.key),
|
def get_queryset(self):
|
||||||
).distinct()
|
self.queryset = super().get_queryset()\
|
||||||
return queryset
|
.prefetch_related('labels', 'nodes')\
|
||||||
|
.select_related('admin_user')
|
||||||
|
self.filter_admin_user_id()
|
||||||
|
self.filter_node()
|
||||||
|
return self.queryset
|
||||||
|
|
||||||
|
|
||||||
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
||||||
|
@ -79,7 +84,7 @@ class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
queryset = Asset.objects.all()
|
queryset = Asset.objects.all()
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
|
|
||||||
class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
||||||
|
@ -88,7 +93,7 @@ class AssetRefreshHardwareApi(generics.RetrieveAPIView):
|
||||||
"""
|
"""
|
||||||
queryset = Asset.objects.all()
|
queryset = Asset.objects.all()
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
asset_id = kwargs.get('pk')
|
asset_id = kwargs.get('pk')
|
||||||
|
@ -102,7 +107,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
||||||
Test asset admin user connectivity
|
Test asset admin user connectivity
|
||||||
"""
|
"""
|
||||||
queryset = Asset.objects.all()
|
queryset = Asset.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
asset_id = kwargs.get('pk')
|
asset_id = kwargs.get('pk')
|
||||||
|
@ -113,7 +118,7 @@ class AssetAdminUserTestApi(generics.RetrieveAPIView):
|
||||||
|
|
||||||
class AssetGatewayApi(generics.RetrieveAPIView):
|
class AssetGatewayApi(generics.RetrieveAPIView):
|
||||||
queryset = Asset.objects.all()
|
queryset = Asset.objects.all()
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
asset_id = kwargs.get('pk')
|
asset_id = kwargs.get('pk')
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
|
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
from rest_framework.views import APIView, Response
|
from rest_framework.views import APIView, Response
|
||||||
from rest_framework.generics import RetrieveAPIView
|
|
||||||
|
|
||||||
from django.views.generic.detail import SingleObjectMixin
|
from django.views.generic.detail import SingleObjectMixin
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsSuperUser, IsSuperUserOrAppUser
|
from common.permissions import IsOrgAdmin, IsAppUser, IsOrgAdminOrAppUser
|
||||||
from ..models import Domain, Gateway
|
from ..models import Domain, Gateway
|
||||||
from ..utils import test_gateway_connectability
|
from ..utils import test_gateway_connectability
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
@ -19,7 +18,7 @@ __all__ = ['DomainViewSet', 'GatewayViewSet', "GatewayTestConnectionApi"]
|
||||||
|
|
||||||
class DomainViewSet(BulkModelViewSet):
|
class DomainViewSet(BulkModelViewSet):
|
||||||
queryset = Domain.objects.all()
|
queryset = Domain.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.DomainSerializer
|
serializer_class = serializers.DomainSerializer
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
|
@ -29,7 +28,7 @@ class DomainViewSet(BulkModelViewSet):
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
if self.request.query_params.get('gateway'):
|
if self.request.query_params.get('gateway'):
|
||||||
self.permission_classes = (IsSuperUserOrAppUser,)
|
self.permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,12 +36,12 @@ class GatewayViewSet(BulkModelViewSet):
|
||||||
filter_fields = ("domain",)
|
filter_fields = ("domain",)
|
||||||
search_fields = filter_fields
|
search_fields = filter_fields
|
||||||
queryset = Gateway.objects.all()
|
queryset = Gateway.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.GatewaySerializer
|
serializer_class = serializers.GatewaySerializer
|
||||||
|
|
||||||
|
|
||||||
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
class GatewayTestConnectionApi(SingleObjectMixin, APIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
model = Gateway
|
model = Gateway
|
||||||
object = None
|
object = None
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ from rest_framework_bulk import BulkModelViewSet
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsSuperUser
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import Label
|
from ..models import Label
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
|
||||||
|
@ -27,8 +27,7 @@ __all__ = ['LabelViewSet']
|
||||||
|
|
||||||
|
|
||||||
class LabelViewSet(BulkModelViewSet):
|
class LabelViewSet(BulkModelViewSet):
|
||||||
queryset = Label.objects.annotate(asset_count=Count("assets"))
|
permission_classes = (IsOrgAdmin,)
|
||||||
permission_classes = (IsSuperUser,)
|
|
||||||
serializer_class = serializers.LabelSerializer
|
serializer_class = serializers.LabelSerializer
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
|
@ -36,3 +35,7 @@ class LabelViewSet(BulkModelViewSet):
|
||||||
self.serializer_class = serializers.LabelDistinctSerializer
|
self.serializer_class = serializers.LabelDistinctSerializer
|
||||||
self.queryset = self.queryset.values("name").distinct()
|
self.queryset = self.queryset.values("name").distinct()
|
||||||
return super().list(request, *args, **kwargs)
|
return super().list(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
self.queryset = Label.objects.annotate(asset_count=Count("assets"))
|
||||||
|
return self.queryset
|
||||||
|
|
|
@ -13,16 +13,17 @@
|
||||||
# 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, mixins
|
from rest_framework import generics, mixins, viewsets
|
||||||
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
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.db.models import Count
|
||||||
|
|
||||||
from common.utils import get_logger, get_object_or_none
|
from common.utils import get_logger, get_object_or_none
|
||||||
from ..hands import IsSuperUser
|
from ..hands import IsOrgAdmin
|
||||||
from ..models import Node
|
from ..models import Node
|
||||||
from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util
|
from ..tasks import update_assets_hardware_info_util, test_asset_connectability_util
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
|
@ -30,57 +31,31 @@ from .. import serializers
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'NodeViewSet', 'NodeChildrenApi',
|
'NodeViewSet', 'NodeChildrenApi', 'NodeAssetsApi',
|
||||||
'NodeAssetsApi',
|
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeReplaceAssetsApi',
|
||||||
'NodeAddAssetsApi', 'NodeRemoveAssetsApi',
|
|
||||||
'NodeReplaceAssetsApi',
|
|
||||||
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
|
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
|
||||||
'TestNodeConnectiveApi'
|
'TestNodeConnectiveApi'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NodeViewSet(BulkModelViewSet):
|
class NodeViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Node.objects.all()
|
queryset = Node.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeSerializer
|
serializer_class = serializers.NodeSerializer
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super().get_queryset().annotate(Count('assets'))
|
||||||
|
return queryset
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
child_key = Node.root().get_next_child_key()
|
child_key = Node.root().get_next_child_key()
|
||||||
serializer.validated_data["key"] = child_key
|
serializer.validated_data["key"] = child_key
|
||||||
serializer.save()
|
serializer.save()
|
||||||
|
|
||||||
|
|
||||||
# class NodeWithAssetsApi(generics.ListAPIView):
|
|
||||||
# permission_classes = (IsSuperUser,)
|
|
||||||
# serializers = serializers.NodeSerializer
|
|
||||||
#
|
|
||||||
# def get_node(self):
|
|
||||||
# pk = self.kwargs.get('pk') or self.request.query_params.get('node')
|
|
||||||
# if not pk:
|
|
||||||
# node = Node.root()
|
|
||||||
# else:
|
|
||||||
# node = get_object_or_404(Node, pk)
|
|
||||||
# return node
|
|
||||||
#
|
|
||||||
# def get_queryset(self):
|
|
||||||
# queryset = []
|
|
||||||
# node = self.get_node()
|
|
||||||
# children = node.get_children()
|
|
||||||
# assets = node.get_assets()
|
|
||||||
# queryset.extend(list(children))
|
|
||||||
#
|
|
||||||
# for asset in assets:
|
|
||||||
# node = Node()
|
|
||||||
# node.id = asset.id
|
|
||||||
# node.parent = node.id
|
|
||||||
# node.value = asset.hostname
|
|
||||||
# queryset.append(node)
|
|
||||||
# return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
|
class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
|
||||||
queryset = Node.objects.all()
|
queryset = Node.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeSerializer
|
serializer_class = serializers.NodeSerializer
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
|
@ -126,22 +101,26 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
|
||||||
query_all = self.request.query_params.get("all")
|
query_all = self.request.query_params.get("all")
|
||||||
query_assets = self.request.query_params.get('assets')
|
query_assets = self.request.query_params.get('assets')
|
||||||
node = self.get_object()
|
node = self.get_object()
|
||||||
|
|
||||||
if node is None:
|
if node is None:
|
||||||
node = Node.root()
|
node = Node.root()
|
||||||
|
node.assets__count = node.get_all_assets().count()
|
||||||
queryset.append(node)
|
queryset.append(node)
|
||||||
if query_all:
|
|
||||||
children = node.get_all_children()
|
|
||||||
else:
|
|
||||||
children = node.get_children()
|
|
||||||
|
|
||||||
|
if query_all:
|
||||||
|
children = node.get_all_children().annotate(Count("assets"))
|
||||||
|
else:
|
||||||
|
children = node.get_children().annotate(Count("assets"))
|
||||||
queryset.extend(list(children))
|
queryset.extend(list(children))
|
||||||
|
|
||||||
if query_assets:
|
if query_assets:
|
||||||
assets = node.get_assets()
|
assets = node.get_assets()
|
||||||
for asset in assets:
|
for asset in assets:
|
||||||
node_fake = Node()
|
node_fake = Node()
|
||||||
|
node_fake.assets__count = 0
|
||||||
node_fake.id = asset.id
|
node_fake.id = asset.id
|
||||||
node_fake.is_node = False
|
node_fake.is_node = False
|
||||||
node_fake.parent_id = node.id
|
node_fake.key = node.key + ':0'
|
||||||
node_fake.value = asset.hostname
|
node_fake.value = asset.hostname
|
||||||
queryset.append(node_fake)
|
queryset.append(node_fake)
|
||||||
queryset = sorted(queryset, key=lambda x: x.is_node, reverse=True)
|
queryset = sorted(queryset, key=lambda x: x.is_node, reverse=True)
|
||||||
|
@ -152,7 +131,7 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class NodeAssetsApi(generics.ListAPIView):
|
class NodeAssetsApi(generics.ListAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetSerializer
|
serializer_class = serializers.AssetSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -167,7 +146,7 @@ class NodeAssetsApi(generics.ListAPIView):
|
||||||
|
|
||||||
class NodeAddChildrenApi(generics.UpdateAPIView):
|
class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||||
queryset = Node.objects.all()
|
queryset = Node.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.NodeAddChildrenSerializer
|
serializer_class = serializers.NodeAddChildrenSerializer
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
|
@ -185,7 +164,7 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
|
||||||
class NodeAddAssetsApi(generics.UpdateAPIView):
|
class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
queryset = Node.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
@ -197,7 +176,7 @@ class NodeAddAssetsApi(generics.UpdateAPIView):
|
||||||
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
queryset = Node.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
@ -213,7 +192,7 @@ class NodeRemoveAssetsApi(generics.UpdateAPIView):
|
||||||
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||||
serializer_class = serializers.NodeAssetsSerializer
|
serializer_class = serializers.NodeAssetsSerializer
|
||||||
queryset = Node.objects.all()
|
queryset = Node.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
instance = None
|
instance = None
|
||||||
|
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
@ -224,7 +203,7 @@ class NodeReplaceAssetsApi(generics.UpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RefreshNodeHardwareInfoApi(APIView):
|
class RefreshNodeHardwareInfoApi(APIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
model = Node
|
model = Node
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
@ -237,7 +216,7 @@ class RefreshNodeHardwareInfoApi(APIView):
|
||||||
|
|
||||||
|
|
||||||
class TestNodeConnectiveApi(APIView):
|
class TestNodeConnectiveApi(APIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
model = Node
|
model = Node
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
|
@ -16,8 +16,9 @@
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework_bulk import BulkModelViewSet
|
from rest_framework_bulk import BulkModelViewSet
|
||||||
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
from ..hands import IsSuperUser, IsSuperUserOrAppUser
|
from common.permissions import IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
from ..models import SystemUser
|
from ..models import SystemUser
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..tasks import push_system_user_to_assets_manual, \
|
from ..tasks import push_system_user_to_assets_manual, \
|
||||||
|
@ -37,7 +38,7 @@ class SystemUserViewSet(BulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
queryset = SystemUser.objects.all()
|
||||||
serializer_class = serializers.SystemUserSerializer
|
serializer_class = serializers.SystemUserSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
|
|
||||||
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
@ -45,7 +46,7 @@ class SystemUserAuthInfoApi(generics.RetrieveUpdateDestroyAPIView):
|
||||||
Get system user auth info
|
Get system user auth info
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
queryset = SystemUser.objects.all()
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = serializers.SystemUserAuthSerializer
|
serializer_class = serializers.SystemUserAuthSerializer
|
||||||
|
|
||||||
def destroy(self, request, *args, **kwargs):
|
def destroy(self, request, *args, **kwargs):
|
||||||
|
@ -59,7 +60,7 @@ class SystemUserPushApi(generics.RetrieveAPIView):
|
||||||
Push system user to cluster assets api
|
Push system user to cluster assets api
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
queryset = SystemUser.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
system_user = self.get_object()
|
system_user = self.get_object()
|
||||||
|
@ -75,7 +76,7 @@ class SystemUserTestConnectiveApi(generics.RetrieveAPIView):
|
||||||
Push system user to cluster assets api
|
Push system user to cluster assets api
|
||||||
"""
|
"""
|
||||||
queryset = SystemUser.objects.all()
|
queryset = SystemUser.objects.all()
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
system_user = self.get_object()
|
system_user = self.get_object()
|
||||||
|
|
|
@ -3,14 +3,17 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from ..models import Asset, AdminUser
|
|
||||||
from common.utils import get_logger
|
from common.utils import get_logger
|
||||||
|
from orgs.mixins import OrgModelForm
|
||||||
|
|
||||||
|
from ..models import Asset, AdminUser
|
||||||
|
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm']
|
__all__ = ['AssetCreateForm', 'AssetUpdateForm', 'AssetBulkUpdateForm']
|
||||||
|
|
||||||
|
|
||||||
class AssetCreateForm(forms.ModelForm):
|
class AssetCreateForm(OrgModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
fields = [
|
fields = [
|
||||||
|
@ -50,7 +53,7 @@ class AssetCreateForm(forms.ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AssetUpdateForm(forms.ModelForm):
|
class AssetUpdateForm(OrgModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Asset
|
model = Asset
|
||||||
fields = [
|
fields = [
|
||||||
|
@ -90,7 +93,7 @@ class AssetUpdateForm(forms.ModelForm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class AssetBulkUpdateForm(forms.ModelForm):
|
class AssetBulkUpdateForm(OrgModelForm):
|
||||||
assets = forms.ModelMultipleChoiceField(
|
assets = forms.ModelMultipleChoiceField(
|
||||||
required=True, help_text='* required',
|
required=True, help_text='* required',
|
||||||
label=_('Select assets'), queryset=Asset.objects.all(),
|
label=_('Select assets'), queryset=Asset.objects.all(),
|
||||||
|
@ -105,7 +108,7 @@ class AssetBulkUpdateForm(forms.ModelForm):
|
||||||
label=_('Port'), required=False, min_value=1, max_value=65535,
|
label=_('Port'), required=False, min_value=1, max_value=65535,
|
||||||
)
|
)
|
||||||
admin_user = forms.ModelChoiceField(
|
admin_user = forms.ModelChoiceField(
|
||||||
required=False, queryset=AdminUser.objects.all(),
|
required=False, queryset=AdminUser.objects,
|
||||||
label=_("Admin user"),
|
label=_("Admin user"),
|
||||||
widget=forms.Select(
|
widget=forms.Select(
|
||||||
attrs={
|
attrs={
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelForm
|
||||||
from ..models import Domain, Asset, Gateway
|
from ..models import Domain, Asset, Gateway
|
||||||
from .user import PasswordAndKeyAuthForm
|
from .user import PasswordAndKeyAuthForm
|
||||||
|
|
||||||
|
@ -34,7 +35,7 @@ class DomainForm(forms.ModelForm):
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class GatewayForm(PasswordAndKeyAuthForm):
|
class GatewayForm(PasswordAndKeyAuthForm, OrgModelForm):
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
# Because we define custom field, so we need rewrite :method: `save`
|
# Because we define custom field, so we need rewrite :method: `save`
|
||||||
|
|
|
@ -11,6 +11,6 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from common.mixins import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
from common.permissions import IsAppUser, IsSuperUser, IsValidUser, IsSuperUserOrAppUser
|
from common.permissions import IsAppUser, IsOrgAdmin, IsValidUser, IsOrgAdminOrAppUser
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
|
|
|
@ -13,6 +13,7 @@ from django.core.cache import cache
|
||||||
|
|
||||||
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
|
from ..const import ASSET_ADMIN_CONN_CACHE_KEY
|
||||||
from .user import AdminUser, SystemUser
|
from .user import AdminUser, SystemUser
|
||||||
|
from orgs.mixins import OrgModelMixin,OrgManager
|
||||||
|
|
||||||
__all__ = ['Asset']
|
__all__ = ['Asset']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -44,12 +45,7 @@ class AssetQuerySet(models.QuerySet):
|
||||||
return self.active()
|
return self.active()
|
||||||
|
|
||||||
|
|
||||||
class AssetManager(models.Manager):
|
class Asset(OrgModelMixin):
|
||||||
def get_queryset(self):
|
|
||||||
return AssetQuerySet(self.model, using=self._db)
|
|
||||||
|
|
||||||
|
|
||||||
class Asset(models.Model):
|
|
||||||
# Important
|
# Important
|
||||||
PLATFORM_CHOICES = (
|
PLATFORM_CHOICES = (
|
||||||
('Linux', 'Linux'),
|
('Linux', 'Linux'),
|
||||||
|
@ -71,16 +67,11 @@ class Asset(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'),
|
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
|
||||||
db_index=True)
|
hostname = models.CharField(max_length=128, verbose_name=_('Hostname'))
|
||||||
hostname = models.CharField(max_length=128, unique=True,
|
protocol = models.CharField(max_length=128, default=SSH_PROTOCOL, choices=PROTOCOL_CHOICES, verbose_name=_('Protocol'))
|
||||||
verbose_name=_('Hostname'))
|
|
||||||
protocol = models.CharField(max_length=128, default=SSH_PROTOCOL,
|
|
||||||
choices=PROTOCOL_CHOICES,
|
|
||||||
verbose_name=_('Protocol'))
|
|
||||||
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
||||||
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES,
|
platform = models.CharField(max_length=128, choices=PLATFORM_CHOICES, default='Linux', verbose_name=_('Platform'))
|
||||||
default='Linux', verbose_name=_('Platform'))
|
|
||||||
domain = models.ForeignKey("assets.Domain", null=True, blank=True,
|
domain = models.ForeignKey("assets.Domain", null=True, blank=True,
|
||||||
related_name='assets', verbose_name=_("Domain"),
|
related_name='assets', verbose_name=_("Domain"),
|
||||||
on_delete=models.SET_NULL)
|
on_delete=models.SET_NULL)
|
||||||
|
@ -94,11 +85,8 @@ class Asset(models.Model):
|
||||||
null=True, verbose_name=_("Admin user"))
|
null=True, verbose_name=_("Admin user"))
|
||||||
|
|
||||||
# Some information
|
# Some information
|
||||||
public_ip = models.GenericIPAddressField(max_length=32, blank=True,
|
public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP'))
|
||||||
null=True,
|
number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number'))
|
||||||
verbose_name=_('Public IP'))
|
|
||||||
number = models.CharField(max_length=32, null=True, blank=True,
|
|
||||||
verbose_name=_('Asset number'))
|
|
||||||
|
|
||||||
# Collect
|
# Collect
|
||||||
vendor = models.CharField(max_length=64, null=True, blank=True,
|
vendor = models.CharField(max_length=64, null=True, blank=True,
|
||||||
|
@ -139,7 +127,7 @@ class Asset(models.Model):
|
||||||
comment = models.TextField(max_length=128, default='', blank=True,
|
comment = models.TextField(max_length=128, default='', blank=True,
|
||||||
verbose_name=_('Comment'))
|
verbose_name=_('Comment'))
|
||||||
|
|
||||||
objects = AssetManager()
|
objects = OrgManager.from_queryset(AssetQuerySet)()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{0.hostname}({0.ip})'.format(self)
|
return '{0.hostname}({0.ip})'.format(self)
|
||||||
|
@ -173,6 +161,12 @@ class Asset(models.Model):
|
||||||
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
|
nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def org_name(self):
|
||||||
|
from orgs.models import Organization
|
||||||
|
org = Organization.get_instance(self.org_id)
|
||||||
|
return org.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hardware_info(self):
|
def hardware_info(self):
|
||||||
if self.cpu_count:
|
if self.cpu_count:
|
||||||
|
@ -233,7 +227,7 @@ class Asset(models.Model):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('ip', 'port')
|
unique_together = [('org_id', 'hostname')]
|
||||||
verbose_name = _("Asset")
|
verbose_name = _("Asset")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -11,14 +11,15 @@ from django.conf import settings
|
||||||
|
|
||||||
from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen
|
from common.utils import get_signer, ssh_key_string_to_obj, ssh_key_gen
|
||||||
from common.validators import alphanumeric
|
from common.validators import alphanumeric
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
from .utils import private_key_validator
|
from .utils import private_key_validator
|
||||||
|
|
||||||
signer = get_signer()
|
signer = get_signer()
|
||||||
|
|
||||||
|
|
||||||
class AssetUser(models.Model):
|
class AssetUser(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric])
|
username = models.CharField(max_length=32, blank=True, verbose_name=_('Username'), validators=[alphanumeric])
|
||||||
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
_password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password'))
|
||||||
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
|
_private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator, ])
|
||||||
|
|
|
@ -7,12 +7,13 @@ import random
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
from .base import AssetUser
|
from .base import AssetUser
|
||||||
|
|
||||||
__all__ = ['Domain', 'Gateway']
|
__all__ = ['Domain', 'Gateway']
|
||||||
|
|
||||||
|
|
||||||
class Domain(models.Model):
|
class Domain(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
||||||
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
comment = models.TextField(blank=True, verbose_name=_('Comment'))
|
||||||
|
@ -43,10 +44,12 @@ class Gateway(AssetUser):
|
||||||
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
|
ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True)
|
||||||
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
port = models.IntegerField(default=22, verbose_name=_('Port'))
|
||||||
protocol = models.CharField(choices=PROTOCOL_CHOICES, max_length=16, default=SSH_PROTOCOL, verbose_name=_("Protocol"))
|
protocol = models.CharField(choices=PROTOCOL_CHOICES, max_length=16, default=SSH_PROTOCOL, verbose_name=_("Protocol"))
|
||||||
domain = models.ForeignKey(Domain, verbose_name=_("Domain"), on_delete=models.CASCADE)
|
domain = models.ForeignKey(Domain, on_delete=models.CASCADE, verbose_name=_("Domain"))
|
||||||
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Comment"))
|
comment = models.CharField(max_length=128, blank=True, null=True, verbose_name=_("Comment"))
|
||||||
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
is_active = models.BooleanField(default=True, verbose_name=_("Is active"))
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = [('name', 'org_id')]
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
import uuid
|
import uuid
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
|
|
||||||
|
|
||||||
class Label(models.Model):
|
class Label(OrgModelMixin):
|
||||||
SYSTEM_CATEGORY = "S"
|
SYSTEM_CATEGORY = "S"
|
||||||
USER_CATEGORY = "U"
|
USER_CATEGORY = "U"
|
||||||
CATEGORY_CHOICES = (
|
CATEGORY_CHOICES = (
|
||||||
|
@ -34,4 +35,4 @@ class Label(models.Model):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "assets_label"
|
db_table = "assets_label"
|
||||||
unique_together = ('name', 'value')
|
unique_together = [('name', 'value')]
|
||||||
|
|
|
@ -5,12 +5,15 @@ import uuid
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from common.utils import with_cache
|
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
|
from orgs.utils import current_org, set_current_org, get_current_org
|
||||||
|
from orgs.models import Organization
|
||||||
|
|
||||||
__all__ = ['Node']
|
__all__ = ['Node']
|
||||||
|
|
||||||
|
|
||||||
class Node(models.Model):
|
class Node(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
|
key = models.CharField(unique=True, max_length=64, verbose_name=_("Key")) # '1:1:1:1'
|
||||||
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
value = models.CharField(max_length=128, verbose_name=_("Value"))
|
||||||
|
@ -20,7 +23,8 @@ class Node(models.Model):
|
||||||
is_node = True
|
is_node = True
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.full_value
|
return self.value
|
||||||
|
# return self.full_value
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.key == other.key
|
return self.key == other.key
|
||||||
|
@ -93,12 +97,10 @@ class Node(models.Model):
|
||||||
|
|
||||||
def get_assets(self):
|
def get_assets(self):
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
if self.is_root():
|
if self.is_default_node():
|
||||||
assets = Asset.objects.filter(
|
assets = Asset.objects.filter(nodes__isnull=True)
|
||||||
Q(nodes__id=self.id) | Q(nodes__isnull=True)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
assets = self.assets.all()
|
assets = Asset.objects.filter(nodes__id=self.id)
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
def get_valid_assets(self):
|
def get_valid_assets(self):
|
||||||
|
@ -106,49 +108,61 @@ class Node(models.Model):
|
||||||
|
|
||||||
def get_all_assets(self):
|
def get_all_assets(self):
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
if self.is_root():
|
pattern = r'^{0}$|^{0}:'.format(self.key)
|
||||||
assets = Asset.objects.all()
|
args = []
|
||||||
|
kwargs = {}
|
||||||
|
if self.is_default_node():
|
||||||
|
args.append(Q(nodes__key__regex=pattern) | Q(nodes=None))
|
||||||
else:
|
else:
|
||||||
pattern = r'^{0}$|^{0}:'.format(self.key)
|
kwargs['nodes__key__regex'] = pattern
|
||||||
assets = Asset.objects.filter(nodes__key__regex=pattern)
|
assets = Asset.objects.filter(*args, **kwargs)
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
def get_all_valid_assets(self):
|
def get_all_valid_assets(self):
|
||||||
return self.get_all_assets().valid()
|
return self.get_all_assets().valid()
|
||||||
|
|
||||||
|
def is_default_node(self):
|
||||||
|
return self.is_root() and self.key == '0'
|
||||||
|
|
||||||
def is_root(self):
|
def is_root(self):
|
||||||
return self.key == '0'
|
if self.key.isdigit():
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent_key(self):
|
||||||
|
parent_key = ":".join(self.key.split(":")[:-1])
|
||||||
|
return parent_key
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parent(self):
|
def parent(self):
|
||||||
if self.key == "0" or not self.key.startswith("0"):
|
if self.is_root():
|
||||||
return self.__class__.root()
|
return self
|
||||||
parent_key = ":".join(self.key.split(":")[:-1])
|
|
||||||
try:
|
try:
|
||||||
parent = self.__class__.objects.get(key=parent_key)
|
parent = self.__class__.objects.get(key=self.parent_key)
|
||||||
return parent
|
return parent
|
||||||
except Node.DoesNotExist:
|
except Node.DoesNotExist:
|
||||||
return self.__class__.root()
|
return self.__class__.root()
|
||||||
|
|
||||||
@parent.setter
|
@parent.setter
|
||||||
def parent(self, parent):
|
def parent(self, parent):
|
||||||
if self.is_node:
|
if not self.is_node:
|
||||||
children = self.get_all_children()
|
self.key = parent.key + ':fake'
|
||||||
old_key = self.key
|
return
|
||||||
with transaction.atomic():
|
children = self.get_all_children()
|
||||||
self.key = parent.get_next_child_key()
|
old_key = self.key
|
||||||
for child in children:
|
with transaction.atomic():
|
||||||
child.key = child.key.replace(old_key, self.key, 1)
|
self.key = parent.get_next_child_key()
|
||||||
child.save()
|
for child in children:
|
||||||
self.save()
|
child.key = child.key.replace(old_key, self.key, 1)
|
||||||
else:
|
child.save()
|
||||||
self.key = parent.key+':fake'
|
self.save()
|
||||||
|
|
||||||
def get_ancestor(self, with_self=False):
|
def get_ancestor(self, with_self=False):
|
||||||
if self.is_root():
|
if self.is_root():
|
||||||
ancestor = self.__class__.objects.filter(key='0')
|
root = self.__class__.root()
|
||||||
return ancestor
|
return [root]
|
||||||
|
|
||||||
_key = self.key.split(':')
|
_key = self.key.split(':')
|
||||||
if not with_self:
|
if not with_self:
|
||||||
_key.pop()
|
_key.pop()
|
||||||
|
@ -162,10 +176,35 @@ class Node(models.Model):
|
||||||
return ancestor
|
return ancestor
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def root(cls):
|
def create_root_node(cls):
|
||||||
obj, created = cls.objects.get_or_create(
|
# 如果使用current_org 在set_current_org时会死循环
|
||||||
key='0', defaults={"key": '0', 'value': "ROOT"}
|
_current_org = get_current_org()
|
||||||
)
|
with transaction.atomic():
|
||||||
print(obj)
|
if _current_org.is_default():
|
||||||
return obj
|
key = '0'
|
||||||
|
else:
|
||||||
|
set_current_org(Organization.root())
|
||||||
|
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
|
||||||
|
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True)
|
||||||
|
key = max([int(k) for k in org_nodes_roots_keys]) + 1
|
||||||
|
set_current_org(_current_org)
|
||||||
|
root = cls.objects.create(key=key, value=_current_org.name)
|
||||||
|
return root
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def root(cls):
|
||||||
|
root = cls.objects.filter(key__regex=r'^[0-9]+$')
|
||||||
|
if root:
|
||||||
|
return root[0]
|
||||||
|
else:
|
||||||
|
return cls.create_root_node()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_fake(cls, count=100):
|
||||||
|
import random
|
||||||
|
for i in range(count):
|
||||||
|
node = random.choice(cls.objects.all())
|
||||||
|
node.create_child('Node {}'.format(i))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ class AdminUser(AssetUser):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
unique_together = [('name', 'org_id')]
|
||||||
verbose_name = _("Admin user")
|
verbose_name = _("Admin user")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -176,6 +177,7 @@ class SystemUser(AssetUser):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['name']
|
ordering = ['name']
|
||||||
|
unique_together = [('name', 'org_id')]
|
||||||
verbose_name = _("System user")
|
verbose_name = _("System user")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -58,7 +58,7 @@ class ReplaceNodeAdminUserSerializer(serializers.ModelSerializer):
|
||||||
管理用户更新关联到的集群
|
管理用户更新关联到的集群
|
||||||
"""
|
"""
|
||||||
nodes = serializers.PrimaryKeyRelatedField(
|
nodes = serializers.PrimaryKeyRelatedField(
|
||||||
many=True, queryset=Node.objects.all()
|
many=True, queryset = Node.objects.all()
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -20,12 +20,12 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
model = Asset
|
model = Asset
|
||||||
list_serializer_class = BulkListSerializer
|
list_serializer_class = BulkListSerializer
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
validators = [] # If not set to [], partial bulk update will be error
|
# validators = [] # If not set to [], partial bulk update will be error
|
||||||
|
|
||||||
def get_field_names(self, declared_fields, info):
|
def get_field_names(self, declared_fields, info):
|
||||||
fields = super().get_field_names(declared_fields, info)
|
fields = super().get_field_names(declared_fields, info)
|
||||||
fields.extend([
|
fields.extend([
|
||||||
'hardware_info', 'is_connective',
|
'hardware_info', 'is_connective', 'org_name'
|
||||||
])
|
])
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
|
||||||
fields = (
|
fields = (
|
||||||
"id", "hostname", "ip", "port", "system_users_granted",
|
"id", "hostname", "ip", "port", "system_users_granted",
|
||||||
"is_active", "system_users_join", "os", 'domain',
|
"is_active", "system_users_join", "os", 'domain',
|
||||||
"platform", "comment", "protocol",
|
"platform", "comment", "protocol", "org_id", "org_name",
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -61,6 +61,6 @@ class MyAssetGrantedSerializer(AssetGrantedSerializer):
|
||||||
model = Asset
|
model = Asset
|
||||||
fields = (
|
fields = (
|
||||||
"id", "hostname", "system_users_granted",
|
"id", "hostname", "system_users_granted",
|
||||||
"is_active", "system_users_join",
|
"is_active", "system_users_join", "org_name",
|
||||||
"os", "platform", "comment",
|
"os", "platform", "comment", "org_id", "protocol"
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,7 +26,7 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
model = Node
|
model = Node
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'key', 'name', 'value', 'parent',
|
'id', 'key', 'name', 'value', 'parent',
|
||||||
'assets_granted', 'assets_amount',
|
'assets_granted', 'assets_amount', 'org_id',
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -43,12 +43,16 @@ class NodeGrantedSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class NodeSerializer(serializers.ModelSerializer):
|
class NodeSerializer(serializers.ModelSerializer):
|
||||||
parent = serializers.SerializerMethodField()
|
|
||||||
assets_amount = serializers.SerializerMethodField()
|
assets_amount = serializers.SerializerMethodField()
|
||||||
|
tree_id = serializers.SerializerMethodField()
|
||||||
|
tree_parent = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Node
|
model = Node
|
||||||
fields = ['id', 'key', 'value', 'parent', 'assets_amount', 'is_node']
|
fields = [
|
||||||
|
'id', 'key', 'value', 'assets_amount',
|
||||||
|
'is_node', 'org_id', 'tree_id', 'tree_parent',
|
||||||
|
]
|
||||||
list_serializer_class = BulkListSerializer
|
list_serializer_class = BulkListSerializer
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
@ -63,12 +67,16 @@ class NodeSerializer(serializers.ModelSerializer):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_parent(obj):
|
def get_assets_amount(obj):
|
||||||
return obj.parent.id if obj.is_node else obj.parent_id
|
return obj.assets__count if hasattr(obj, 'assets__count') else 0
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_assets_amount(obj):
|
def get_tree_id(obj):
|
||||||
return obj.get_all_assets().count() if obj.is_node else 0
|
return obj.key
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_tree_parent(obj):
|
||||||
|
return obj.parent_key
|
||||||
|
|
||||||
def get_fields(self):
|
def get_fields(self):
|
||||||
fields = super().get_fields()
|
fields = super().get_fields()
|
||||||
|
@ -78,7 +86,7 @@ class NodeSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
|
||||||
class NodeAssetsSerializer(serializers.ModelSerializer):
|
class NodeAssetsSerializer(serializers.ModelSerializer):
|
||||||
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
|
assets = serializers.PrimaryKeyRelatedField(many=True, queryset = Asset.objects.all())
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Node
|
model = Node
|
||||||
|
|
|
@ -71,7 +71,7 @@ function initTable2() {
|
||||||
|
|
||||||
function onSelected2(event, treeNode) {
|
function onSelected2(event, treeNode) {
|
||||||
var url = asset_table2.ajax.url();
|
var url = asset_table2.ajax.url();
|
||||||
url = setUrlParam(url, "node_id", treeNode.id);
|
url = setUrlParam(url, "node_id", treeNode.node_id);
|
||||||
setCookie('node_selected', treeNode.id);
|
setCookie('node_selected', treeNode.id);
|
||||||
asset_table2.ajax.url(url);
|
asset_table2.ajax.url(url);
|
||||||
asset_table2.ajax.reload();
|
asset_table2.ajax.reload();
|
||||||
|
@ -97,17 +97,20 @@ function initTree2() {
|
||||||
var zNodes = [];
|
var zNodes = [];
|
||||||
$.get("{% url 'api-assets:node-list' %}", function(data, status){
|
$.get("{% url 'api-assets:node-list' %}", function(data, status){
|
||||||
$.each(data, function (index, value) {
|
$.each(data, function (index, value) {
|
||||||
value["pId"] = value["parent"];
|
value["node_id"] = value["id"];
|
||||||
|
value["id"] = value["tree_id"];
|
||||||
|
value["pId"] = value["tree_parent"];
|
||||||
{#value["open"] = true;#}
|
{#value["open"] = true;#}
|
||||||
if (value["key"] === "0") {
|
if (value["key"] === "0") {
|
||||||
value["open"] = true;
|
value["open"] = true;
|
||||||
}
|
}
|
||||||
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
|
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
|
||||||
value['value'] = value['value'];
|
|
||||||
});
|
});
|
||||||
zNodes = data;
|
zNodes = data;
|
||||||
$.fn.zTree.init($("#assetTree2"), setting, zNodes);
|
$.fn.zTree.init($("#assetTree2"), setting, zNodes);
|
||||||
zTree2 = $.fn.zTree.getZTreeObj("assetTree2");
|
zTree2 = $.fn.zTree.getZTreeObj("assetTree2");
|
||||||
|
var root = zTree2.getNodes()[0];
|
||||||
|
zTree2.expandNode(root);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends '_modal.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
<style>
|
||||||
|
.modal-body {
|
||||||
|
background-color: white !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% block modal_id %}user_asset_detail_modal{% endblock %}
|
||||||
|
|
||||||
|
{% block modal_title %}{% trans "Asset detail" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block modal_body %}
|
||||||
|
<div class="ibox-content" style="background-color: inherit">
|
||||||
|
<table class="table">
|
||||||
|
<tbody id="asset_detail_tbody">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block modal_button %}
|
||||||
|
<button data-dismiss="modal" class="btn btn-white" type="button">{% trans "Close" %}</button>
|
||||||
|
{% endblock %}
|
|
@ -130,7 +130,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if user.is_superuser %}
|
{% if user.is_superuser or user.is_org_admin %}
|
||||||
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||||
<div class="panel panel-primary">
|
<div class="panel panel-primary">
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
{% block custom_head_css_js %}
|
{% block custom_head_css_js %}
|
||||||
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
|
<link href="{% static 'css/plugins/ztree/awesomeStyle/awesome.css' %}" rel="stylesheet">
|
||||||
|
{# <link href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet">#}
|
||||||
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
|
<script type="text/javascript" src="{% static 'js/plugins/ztree/jquery.ztree.all.min.js' %}"></script>
|
||||||
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
<script src="{% static 'js/jquery.form.min.js' %}"></script>
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
|
@ -27,6 +28,10 @@
|
||||||
list-style: none;
|
list-style: none;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
.dataTables_wrapper .dataTables_processing {
|
||||||
|
opacity: .9;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
div#rMenu li{
|
div#rMenu li{
|
||||||
margin: 1px 0;
|
margin: 1px 0;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -161,16 +166,6 @@ function initTable() {
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
|
|
||||||
{#{targets: 5, createdCell: function (td, cellData) {#}
|
|
||||||
{# if (cellData === 'Unknown'){#}
|
|
||||||
{# $(td).html('<i class="fa fa-circle text-warning"></i>')#}
|
|
||||||
{# } else if (!cellData) {#}
|
|
||||||
{# $(td).html('<i class="fa fa-circle text-danger"></i>')#}
|
|
||||||
{# } else {#}
|
|
||||||
{# $(td).html('<i class="fa fa-circle text-navy"></i>')#}
|
|
||||||
{# }#}
|
|
||||||
{# }},#}
|
|
||||||
|
|
||||||
{targets: 5, createdCell: function (td, cellData, rowData) {
|
{targets: 5, createdCell: function (td, cellData, rowData) {
|
||||||
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
|
var update_btn = '<a href="{% url "assets:asset-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-info">{% trans "Update" %}</a>'.replace("{{ DEFAULT_PK }}", cellData);
|
||||||
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_asset_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
|
||||||
|
@ -178,13 +173,6 @@ function initTable() {
|
||||||
}}
|
}}
|
||||||
],
|
],
|
||||||
ajax_url: '{% url "api-assets:asset-list" %}',
|
ajax_url: '{% url "api-assets:asset-list" %}',
|
||||||
|
|
||||||
{#columns: [#}
|
|
||||||
{# {data: "id"}, {data: "hostname" }, {data: "ip" },#}
|
|
||||||
{# {data: "cpu_cores"}, {data: "is_active", orderable: false },#}
|
|
||||||
{# {data: "is_connective", orderable: false}, {data: "id", orderable: false }#}
|
|
||||||
{#],#}
|
|
||||||
|
|
||||||
columns: [
|
columns: [
|
||||||
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
{data: "id"}, {data: "hostname" }, {data: "ip" },
|
||||||
{data: "cpu_cores"}, {data: "is_active", orderable: false },
|
{data: "cpu_cores"}, {data: "is_active", orderable: false },
|
||||||
|
@ -202,17 +190,17 @@ function addTreeNode() {
|
||||||
if (!parentNode){
|
if (!parentNode){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.id );
|
var url = "{% url 'api-assets:node-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", parentNode.node_id );
|
||||||
$.post(url, {}, function (data, status){
|
$.post(url, {}, function (data, status){
|
||||||
if (status === "success") {
|
if (status === "success") {
|
||||||
var newNode = {
|
var newNode = {
|
||||||
name: data["value"],
|
name: data["value"],
|
||||||
id: data["id"],
|
id: data["id"],
|
||||||
pId: parentNode.id
|
pId: parentNode.node_id
|
||||||
};
|
};
|
||||||
newNode.checked = zTree.getSelectedNodes()[0].checked;
|
newNode.checked = zTree.getSelectedNodes()[0].checked;
|
||||||
zTree.addNodes(parentNode, 0, newNode);
|
zTree.addNodes(parentNode, 0, newNode);
|
||||||
var node = zTree.getNodeByParam('id', newNode.id, parentNode)
|
var node = zTree.getNodeByParam('id', newNode.node_id, parentNode);
|
||||||
zTree.editName(node);
|
zTree.editName(node);
|
||||||
} else {
|
} else {
|
||||||
alert("{% trans 'Create node failed' %}")
|
alert("{% trans 'Create node failed' %}")
|
||||||
|
@ -231,7 +219,7 @@ function removeTreeNode() {
|
||||||
} else if (current_node.assets_amount !== 0) {
|
} else if (current_node.assets_amount !== 0) {
|
||||||
toastr.error("{% trans 'Have assets, cancel' %}");
|
toastr.error("{% trans 'Have assets, cancel' %}");
|
||||||
} else {
|
} else {
|
||||||
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id );
|
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.node_id );
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
@ -291,7 +279,7 @@ function onBodyMouseDown(event){
|
||||||
|
|
||||||
|
|
||||||
function onRename(event, treeId, treeNode, isCancel){
|
function onRename(event, treeId, treeNode, isCancel){
|
||||||
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.id);
|
var url = "{% url 'api-assets:node-detail' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", treeNode.node_id);
|
||||||
var data = {"value": treeNode.name};
|
var data = {"value": treeNode.name};
|
||||||
if (isCancel){
|
if (isCancel){
|
||||||
return
|
return
|
||||||
|
@ -305,9 +293,9 @@ function onRename(event, treeId, treeNode, isCancel){
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
function onSelected(event, treeNode) {
|
||||||
var url = asset_table.ajax.url();
|
var url = asset_table.ajax.url();
|
||||||
url = setUrlParam(url, "node_id", treeNode.id);
|
url = setUrlParam(url, "node_id", treeNode.node_id);
|
||||||
url = setUrlParam(url, "show_current_asset", getCookie('show_current_asset'));
|
url = setUrlParam(url, "show_current_asset", getCookie('show_current_asset'));
|
||||||
setCookie('node_selected', treeNode.id);
|
setCookie('node_selected', treeNode.node_id);
|
||||||
asset_table.ajax.url(url);
|
asset_table.ajax.url(url);
|
||||||
asset_table.ajax.reload();
|
asset_table.ajax.reload();
|
||||||
}
|
}
|
||||||
|
@ -324,7 +312,7 @@ function selectQueryNode() {
|
||||||
node_id = cookie_node_id;
|
node_id = cookie_node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = zTree.getNodesByParam("id", node_id, null);
|
node = zTree.getNodesByParam("node_id", node_id, null);
|
||||||
if (node){
|
if (node){
|
||||||
zTree.selectNode(node[0]);
|
zTree.selectNode(node[0]);
|
||||||
}
|
}
|
||||||
|
@ -341,11 +329,7 @@ function beforeDrop(treeId, treeNodes, targetNode, moveType) {
|
||||||
});
|
});
|
||||||
|
|
||||||
var msg = "你想移动节点: `" + treeNodesNames.join(",") + "` 到 `" + targetNode.value + "` 下吗?";
|
var msg = "你想移动节点: `" + treeNodesNames.join(",") + "` 到 `" + targetNode.value + "` 下吗?";
|
||||||
if (confirm(msg)){
|
return confirm(msg);
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDrag(event, treeId, treeNodes) {
|
function onDrag(event, treeId, treeNodes) {
|
||||||
|
@ -354,10 +338,10 @@ function onDrag(event, treeId, treeNodes) {
|
||||||
function onDrop(event, treeId, treeNodes, targetNode, moveType) {
|
function onDrop(event, treeId, treeNodes, targetNode, moveType) {
|
||||||
var treeNodesIds = [];
|
var treeNodesIds = [];
|
||||||
$.each(treeNodes, function (index, value) {
|
$.each(treeNodes, function (index, value) {
|
||||||
treeNodesIds.push(value.id);
|
treeNodesIds.push(value.node_id);
|
||||||
});
|
});
|
||||||
|
|
||||||
var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.id);
|
var the_url = "{% url 'api-assets:node-add-children' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", targetNode.node_id);
|
||||||
var body = {nodes: treeNodesIds};
|
var body = {nodes: treeNodesIds};
|
||||||
APIUpdateAttr({
|
APIUpdateAttr({
|
||||||
url: the_url,
|
url: the_url,
|
||||||
|
@ -401,16 +385,21 @@ function initTree() {
|
||||||
var zNodes = [];
|
var zNodes = [];
|
||||||
$.get("{% url 'api-assets:node-list' %}", function(data, status){
|
$.get("{% url 'api-assets:node-list' %}", function(data, status){
|
||||||
$.each(data, function (index, value) {
|
$.each(data, function (index, value) {
|
||||||
value["pId"] = value["parent"];
|
value["node_id"] = value["id"];
|
||||||
if (value["key"] === "0") {
|
value["id"] = value["tree_id"];
|
||||||
value["open"] = true;
|
if (value["tree_id"] !== value["tree_parent"]){
|
||||||
}
|
value["pId"] = value["tree_parent"];
|
||||||
|
} else {
|
||||||
|
value["isParent"] = true;
|
||||||
|
}
|
||||||
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
|
value["name"] = value["value"] + ' (' + value['assets_amount'] + ')';
|
||||||
value['value'] = value['value'];
|
value['value'] = value['value'];
|
||||||
});
|
});
|
||||||
zNodes = data;
|
zNodes = data;
|
||||||
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
||||||
|
var root = zTree.getNodes()[0];
|
||||||
|
zTree.expandNode(root);
|
||||||
rMenu = $("#rMenu");
|
rMenu = $("#rMenu");
|
||||||
selectQueryNode();
|
selectQueryNode();
|
||||||
});
|
});
|
||||||
|
@ -462,7 +451,7 @@ $(document).ready(function(){
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "{% url "assets:asset-export" %}",
|
url: "{% url "assets:asset-export" %}",
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: JSON.stringify({assets_id: assets, node_id: current_node.id}),
|
data: JSON.stringify({assets_id: assets, node_id: current_node.node_id}),
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: function (data, textStatus) {
|
success: function (data, textStatus) {
|
||||||
window.open(data.redirect)
|
window.open(data.redirect)
|
||||||
|
@ -479,8 +468,8 @@ $(document).ready(function(){
|
||||||
var current_node;
|
var current_node;
|
||||||
if (nodes && nodes.length ===1 ){
|
if (nodes && nodes.length ===1 ){
|
||||||
current_node = nodes[0];
|
current_node = nodes[0];
|
||||||
action = setUrlParam(action, 'node_id', current_node.id);
|
action = setUrlParam(action, 'node_id', current_node.node_id);
|
||||||
{#action += "?node_id=" + current_node.id;#}
|
{#action += "?node_id=" + current_node.node_id;#}
|
||||||
$form.attr("action", action)
|
$form.attr("action", action)
|
||||||
}
|
}
|
||||||
$form.find('.help-block').remove();
|
$form.find('.help-block').remove();
|
||||||
|
@ -506,7 +495,7 @@ $(document).ready(function(){
|
||||||
var current_node;
|
var current_node;
|
||||||
if (nodes && nodes.length ===1 ){
|
if (nodes && nodes.length ===1 ){
|
||||||
current_node = nodes[0];
|
current_node = nodes[0];
|
||||||
url += "?node_id=" + current_node.id;
|
url += "?node_id=" + current_node.node_id;
|
||||||
}
|
}
|
||||||
window.open(url, '_self');
|
window.open(url, '_self');
|
||||||
})
|
})
|
||||||
|
@ -520,7 +509,7 @@ $(document).ready(function(){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id);
|
var the_url = url.replace("{{ DEFAULT_PK }}", current_node.node_id);
|
||||||
function success(data) {
|
function success(data) {
|
||||||
rMenu.css({"visibility" : "hidden"});
|
rMenu.css({"visibility" : "hidden"});
|
||||||
var task_id = data.task;
|
var task_id = data.task;
|
||||||
|
@ -545,7 +534,7 @@ $(document).ready(function(){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var the_url = url.replace("{{ DEFAULT_PK }}", current_node.id);
|
var the_url = url.replace("{{ DEFAULT_PK }}", current_node.node_id);
|
||||||
function success(data) {
|
function success(data) {
|
||||||
rMenu.css({"visibility" : "hidden"});
|
rMenu.css({"visibility" : "hidden"});
|
||||||
var task_id = data.task;
|
var task_id = data.task;
|
||||||
|
@ -682,7 +671,7 @@ $(document).ready(function(){
|
||||||
};
|
};
|
||||||
|
|
||||||
APIUpdateAttr({
|
APIUpdateAttr({
|
||||||
'url': '/api/assets/v1/nodes/' + current_node.id + '/assets/remove/',
|
'url': '/api/assets/v1/nodes/' + current_node.node_id + '/assets/remove/',
|
||||||
'method': 'PUT',
|
'method': 'PUT',
|
||||||
'body': JSON.stringify(data),
|
'body': JSON.stringify(data),
|
||||||
'success': success
|
'success': success
|
||||||
|
@ -719,9 +708,7 @@ $(document).ready(function(){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {'assets': assets_selected};
|
||||||
'assets': assets_selected
|
|
||||||
};
|
|
||||||
var success = function () {
|
var success = function () {
|
||||||
asset_table2.selected = [];
|
asset_table2.selected = [];
|
||||||
asset_table2.ajax.reload()
|
asset_table2.ajax.reload()
|
||||||
|
@ -729,9 +716,9 @@ $(document).ready(function(){
|
||||||
|
|
||||||
var url = '';
|
var url = '';
|
||||||
if (update_node_action === "move") {
|
if (update_node_action === "move") {
|
||||||
url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id);
|
url = "{% url 'api-assets:node-replace-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.node_id);
|
||||||
} else {
|
} else {
|
||||||
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.id);
|
url = "{% url 'api-assets:node-add-assets' pk=DEFAULT_PK %}".replace("{{ DEFAULT_PK }}", current_node.node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
APIUpdateAttr({
|
APIUpdateAttr({
|
||||||
|
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
{% block help_message %}
|
{% block help_message %}
|
||||||
<div class="alert alert-info help-message">
|
<div class="alert alert-info help-message">
|
||||||
网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过网关服务器进行跳转登
|
网域功能是为了解决部分环境(如:混合云)无法直接连接而新增的功能,原理是通过网关服务器进行跳转登录。<br>
|
||||||
录。
|
JMS => 网域网关 => 目标资产
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -55,11 +55,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% include 'assets/_user_asset_detail_modal.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block custom_foot_js %}
|
{% block custom_foot_js %}
|
||||||
<script>
|
<script>
|
||||||
var zTree, rMenu, asset_table;
|
var zTree, asset_table;
|
||||||
var inited = false;
|
var inited = false;
|
||||||
var url;
|
var url;
|
||||||
function initTable() {
|
function initTable() {
|
||||||
|
@ -68,14 +71,15 @@ function initTable() {
|
||||||
} else {
|
} else {
|
||||||
inited = true;
|
inited = true;
|
||||||
}
|
}
|
||||||
|
console.log("init table")
|
||||||
|
url = "{% url 'api-perms:my-assets' %}";
|
||||||
var options = {
|
var options = {
|
||||||
ele: $('#user_assets_table'),
|
ele: $('#user_assets_table'),
|
||||||
columnDefs: [
|
columnDefs: [
|
||||||
{targets: 1, createdCell: function (td, cellData, rowData) {
|
{targets: 1, createdCell: function (td, cellData, rowData) {
|
||||||
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
|
var detail_btn = '<a class="asset_detail" asset-id="rowData_id" data-toggle="modal" data-target="#user_asset_detail_modal" tabindex="0">'+ cellData +'</a>'
|
||||||
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
|
$(td).html(detail_btn.replace("rowData_id", rowData.id));
|
||||||
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
|
}},
|
||||||
}},
|
|
||||||
{targets: 3, createdCell: function (td, cellData) {
|
{targets: 3, createdCell: function (td, cellData) {
|
||||||
if (!cellData) {
|
if (!cellData) {
|
||||||
$(td).html('<i class="fa fa-times text-danger"></i>')
|
$(td).html('<i class="fa fa-times text-danger"></i>')
|
||||||
|
@ -103,33 +107,13 @@ function initTable() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelected(event, treeNode) {
|
function onSelected(event, treeNode) {
|
||||||
console.log("select");
|
|
||||||
url = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}';
|
url = '{% url "api-perms:my-node-assets" node_id=DEFAULT_PK %}';
|
||||||
url = url.replace("{{ DEFAULT_PK }}", treeNode.id);
|
url = url.replace("{{ DEFAULT_PK }}", treeNode.node_id);
|
||||||
initTable();
|
|
||||||
setCookie('node_selected', treeNode.id);
|
setCookie('node_selected', treeNode.id);
|
||||||
asset_table.ajax.url(url);
|
asset_table.ajax.url(url);
|
||||||
asset_table.ajax.reload();
|
asset_table.ajax.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectQueryNode() {
|
|
||||||
var query_node_id = $.getUrlParam("node");
|
|
||||||
var cookie_node_id = getCookie('node_selected');
|
|
||||||
var node;
|
|
||||||
var node_id;
|
|
||||||
|
|
||||||
if (query_node_id !== null) {
|
|
||||||
node_id = query_node_id
|
|
||||||
} else if (cookie_node_id !== null) {
|
|
||||||
node_id = cookie_node_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
node = zTree.getNodesByParam("id", node_id, null);
|
|
||||||
if (node){
|
|
||||||
zTree.selectNode(node[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initTree() {
|
function initTree() {
|
||||||
var setting = {
|
var setting = {
|
||||||
view: {
|
view: {
|
||||||
|
@ -149,23 +133,56 @@ function initTree() {
|
||||||
var zNodes = [];
|
var zNodes = [];
|
||||||
$.get("{% url 'api-perms:my-nodes' %}", function(data, status){
|
$.get("{% url 'api-perms:my-nodes' %}", function(data, status){
|
||||||
$.each(data, function (index, value) {
|
$.each(data, function (index, value) {
|
||||||
value["pId"] = value["parent"];
|
value["node_id"] = value["id"];
|
||||||
if (value["key"] === "0") {
|
value["id"] = value["tree_id"];
|
||||||
value["open"] = true;
|
if (value["tree_id"] !== value["tree_parent"]) {
|
||||||
|
value["pId"] = value["tree_parent"];
|
||||||
}
|
}
|
||||||
value["name"] = value["value"]
|
value["isParent"] = value["is_node"];
|
||||||
|
value['name'] = value['value'];
|
||||||
});
|
});
|
||||||
zNodes = data;
|
zNodes = data;
|
||||||
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
||||||
rMenu = $("#rMenu");
|
var root = zTree.getNodes()[0];
|
||||||
selectQueryNode();
|
zTree.expandNode(root);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
initTree();
|
initTree();
|
||||||
|
initTable();
|
||||||
|
})
|
||||||
|
.on('click', '.asset_detail', function() {
|
||||||
|
var data = asset_table.ajax.json();
|
||||||
|
var asset_id = this.getAttribute("asset-id");
|
||||||
|
var trs = '';
|
||||||
|
var desc = {
|
||||||
|
'hostname': "{% trans 'Hostname' %}",
|
||||||
|
'ip': "{% trans 'IP' %}",
|
||||||
|
'port': "{% trans 'Port' %}",
|
||||||
|
'protocol': "{% trans 'Protocol' %}",
|
||||||
|
'platform': "{% trans 'Platform' %}",
|
||||||
|
'os': "{% trans 'OS' %}",
|
||||||
|
'system_users_join': "{% trans 'System user' %}",
|
||||||
|
'domain': "{% trans 'Domain' %}",
|
||||||
|
'is_active': "{% trans 'Is active' %}",
|
||||||
|
'comment': "{% trans 'Comment' %}"
|
||||||
|
{#'date_joined': "{% trans 'Date joined' %}",#}
|
||||||
|
};
|
||||||
|
$.each(data, function(index, value){
|
||||||
|
if(value.id === asset_id){
|
||||||
|
for(var i in desc){
|
||||||
|
trs += "<tr class='no-borders-tr'>\n" +
|
||||||
|
"<td>"+ desc[i] + ":</td>"+
|
||||||
|
"<td><b>"+ (value[i] === null?'':value[i]) + "</b></td>\n" +
|
||||||
|
"</tr>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$('#asset_detail_tbody').html(trs)
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -1,5 +1,5 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from .. import api
|
from .. import api
|
||||||
from rest_framework_bulk.routes import BulkRouter
|
from rest_framework_bulk.routes import BulkRouter
|
||||||
|
|
||||||
|
@ -7,54 +7,54 @@ app_name = 'assets'
|
||||||
|
|
||||||
|
|
||||||
router = BulkRouter()
|
router = BulkRouter()
|
||||||
router.register(r'v1/assets', api.AssetViewSet, 'asset')
|
router.register(r'assets', api.AssetViewSet, 'asset')
|
||||||
router.register(r'v1/admin-user', api.AdminUserViewSet, 'admin-user')
|
router.register(r'admin-user', api.AdminUserViewSet, 'admin-user')
|
||||||
router.register(r'v1/system-user', api.SystemUserViewSet, 'system-user')
|
router.register(r'system-user', api.SystemUserViewSet, 'system-user')
|
||||||
router.register(r'v1/labels', api.LabelViewSet, 'label')
|
router.register(r'labels', api.LabelViewSet, 'label')
|
||||||
router.register(r'v1/nodes', api.NodeViewSet, 'node')
|
router.register(r'nodes', api.NodeViewSet, 'node')
|
||||||
router.register(r'v1/domain', api.DomainViewSet, 'domain')
|
router.register(r'domain', api.DomainViewSet, 'domain')
|
||||||
router.register(r'v1/gateway', api.GatewayViewSet, 'gateway')
|
router.register(r'gateway', api.GatewayViewSet, 'gateway')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^v1/assets-bulk/$', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
path('assets-bulk/', api.AssetListUpdateApi.as_view(), name='asset-bulk-update'),
|
||||||
url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/auth-info/', api.SystemUserAuthInfoApi.as_view(),
|
path('system-user/<uuid:pk>/auth-info/',
|
||||||
name='system-user-auth-info'),
|
api.SystemUserAuthInfoApi.as_view(), name='system-user-auth-info'),
|
||||||
url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/refresh/$',
|
path('assets/<uuid:pk>/refresh/',
|
||||||
api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'),
|
api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'),
|
||||||
url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/alive/$',
|
path('assets/<uuid:pk>/alive/',
|
||||||
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
|
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
|
||||||
url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/gateway/$',
|
path('assets/<uuid:pk>/gateway/',
|
||||||
api.AssetGatewayApi.as_view(), name='asset-gateway'),
|
api.AssetGatewayApi.as_view(), name='asset-gateway'),
|
||||||
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$',
|
path('admin-user/<uuid:pk>/nodes/',
|
||||||
api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'),
|
api.ReplaceNodesAdminUserApi.as_view(), name='replace-nodes-admin-user'),
|
||||||
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/auth/$',
|
path('admin-user/<uuid:pk>/auth/',
|
||||||
api.AdminUserAuthApi.as_view(), name='admin-user-auth'),
|
api.AdminUserAuthApi.as_view(), name='admin-user-auth'),
|
||||||
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$',
|
path('admin-user/<uuid:pk>/connective/',
|
||||||
api.AdminUserTestConnectiveApi.as_view(), name='admin-user-connective'),
|
api.AdminUserTestConnectiveApi.as_view(), name='admin-user-connective'),
|
||||||
url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/push/$',
|
path('system-user/<uuid:pk>/push/',
|
||||||
api.SystemUserPushApi.as_view(), name='system-user-push'),
|
api.SystemUserPushApi.as_view(), name='system-user-push'),
|
||||||
url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$',
|
path('system-user/<uuid:pk>/connective/',
|
||||||
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
|
api.SystemUserTestConnectiveApi.as_view(), name='system-user-connective'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/$',
|
path('nodes/<uuid:pk>/children/',
|
||||||
api.NodeChildrenApi.as_view(), name='node-children'),
|
api.NodeChildrenApi.as_view(), name='node-children'),
|
||||||
url(r'^v1/nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$',
|
path('nodes/<uuid:pk>/children/add/',
|
||||||
api.NodeAddChildrenApi.as_view(), name='node-add-children'),
|
api.NodeAddChildrenApi.as_view(), name='node-add-children'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
|
path('nodes/<uuid:pk>/assets/',
|
||||||
api.NodeAssetsApi.as_view(), name='node-assets'),
|
api.NodeAssetsApi.as_view(), name='node-assets'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$',
|
path('nodes/<uuid:pk>/assets/add/',
|
||||||
api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
|
api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/replace/$',
|
path('nodes/<uuid:pk>/assets/replace/',
|
||||||
api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'),
|
api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$',
|
path('nodes/<uuid:pk>/assets/remove/',
|
||||||
api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
|
api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$',
|
path('nodes/<uuid:pk>/refresh-hardware-info/',
|
||||||
api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'),
|
api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'),
|
||||||
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$',
|
path('nodes/<uuid:pk>/test-connective/',
|
||||||
api.TestNodeConnectiveApi.as_view(), name='node-test-connective'),
|
api.TestNodeConnectiveApi.as_view(), name='node-test-connective'),
|
||||||
|
|
||||||
url(r'^v1/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$',
|
path('gateway/<uuid:pk>/test-connective/',
|
||||||
api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
app_name = 'assets'
|
app_name = 'assets'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Resource asset url
|
# Resource asset url
|
||||||
url(r'^$', views.AssetListView.as_view(), name='asset-index'),
|
path('', views.AssetListView.as_view(), name='asset-index'),
|
||||||
url(r'^asset/$', views.AssetListView.as_view(), name='asset-list'),
|
path('asset/', views.AssetListView.as_view(), name='asset-list'),
|
||||||
url(r'^asset/create/$', views.AssetCreateView.as_view(), name='asset-create'),
|
path('asset/create/', views.AssetCreateView.as_view(), name='asset-create'),
|
||||||
url(r'^asset/export/$', views.AssetExportView.as_view(), name='asset-export'),
|
path('asset/export/', views.AssetExportView.as_view(), name='asset-export'),
|
||||||
url(r'^asset/import/$', views.BulkImportAssetView.as_view(), name='asset-import'),
|
path('asset/import/', views.BulkImportAssetView.as_view(), name='asset-import'),
|
||||||
url(r'^asset/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AssetDetailView.as_view(), name='asset-detail'),
|
path('asset/<uuid:pk>/', views.AssetDetailView.as_view(), name='asset-detail'),
|
||||||
url(r'^asset/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.AssetUpdateView.as_view(), name='asset-update'),
|
path('asset/<uuid:pk>/update/', views.AssetUpdateView.as_view(), name='asset-update'),
|
||||||
url(r'^asset/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.AssetDeleteView.as_view(), name='asset-delete'),
|
path('asset/<uuid:pk>/delete/', views.AssetDeleteView.as_view(), name='asset-delete'),
|
||||||
url(r'^asset/update/$', views.AssetBulkUpdateView.as_view(), name='asset-bulk-update'),
|
path('asset/update/', views.AssetBulkUpdateView.as_view(), name='asset-bulk-update'),
|
||||||
|
|
||||||
# User asset view
|
# User asset view
|
||||||
url(r'^user-asset/$', views.UserAssetListView.as_view(), name='user-asset-list'),
|
path('user-asset/', views.UserAssetListView.as_view(), name='user-asset-list'),
|
||||||
|
|
||||||
# Resource admin user url
|
# Resource admin user url
|
||||||
url(r'^admin-user/$', views.AdminUserListView.as_view(), name='admin-user-list'),
|
path('admin-user/', views.AdminUserListView.as_view(), name='admin-user-list'),
|
||||||
url(r'^admin-user/create/$', views.AdminUserCreateView.as_view(), name='admin-user-create'),
|
path('admin-user/create/', views.AdminUserCreateView.as_view(), name='admin-user-create'),
|
||||||
url(r'^admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
|
path('admin-user/<uuid:pk>/', views.AdminUserDetailView.as_view(), name='admin-user-detail'),
|
||||||
url(r'^admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
|
path('admin-user/<uuid:pk>/update/', views.AdminUserUpdateView.as_view(), name='admin-user-update'),
|
||||||
url(r'^admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.AdminUserDeleteView.as_view(), name='admin-user-delete'),
|
path('admin-user/<uuid:pk>/delete/', views.AdminUserDeleteView.as_view(), name='admin-user-delete'),
|
||||||
url(r'^admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', views.AdminUserAssetsView.as_view(), name='admin-user-assets'),
|
path('admin-user/<uuid:pk>/assets/', views.AdminUserAssetsView.as_view(), name='admin-user-assets'),
|
||||||
|
|
||||||
# Resource system user url
|
# Resource system user url
|
||||||
url(r'^system-user/$', views.SystemUserListView.as_view(), name='system-user-list'),
|
path('system-user/', views.SystemUserListView.as_view(), name='system-user-list'),
|
||||||
url(r'^system-user/create/$', views.SystemUserCreateView.as_view(), name='system-user-create'),
|
path('system-user/create/', views.SystemUserCreateView.as_view(), name='system-user-create'),
|
||||||
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.SystemUserDetailView.as_view(), name='system-user-detail'),
|
path('system-user/<uuid:pk>/', views.SystemUserDetailView.as_view(), name='system-user-detail'),
|
||||||
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.SystemUserUpdateView.as_view(), name='system-user-update'),
|
path('system-user/<uuid:pk>/update/', views.SystemUserUpdateView.as_view(), name='system-user-update'),
|
||||||
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
|
path('system-user/<uuid:pk>/delete/', views.SystemUserDeleteView.as_view(), name='system-user-delete'),
|
||||||
url(r'^system-user/(?P<pk>[0-9a-zA-Z\-]{36})/asset/$', views.SystemUserAssetView.as_view(), name='system-user-asset'),
|
path('system-user/<uuid:pk>/asset/', views.SystemUserAssetView.as_view(), name='system-user-asset'),
|
||||||
|
|
||||||
url(r'^label/$', views.LabelListView.as_view(), name='label-list'),
|
path('label/', views.LabelListView.as_view(), name='label-list'),
|
||||||
url(r'^label/create/$', views.LabelCreateView.as_view(), name='label-create'),
|
path('label/create/', views.LabelCreateView.as_view(), name='label-create'),
|
||||||
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.LabelUpdateView.as_view(), name='label-update'),
|
path('label/<uuid:pk>/update/', views.LabelUpdateView.as_view(), name='label-update'),
|
||||||
url(r'^label/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.LabelDeleteView.as_view(), name='label-delete'),
|
path('label/<uuid:pk>/delete/', views.LabelDeleteView.as_view(), name='label-delete'),
|
||||||
|
|
||||||
url(r'^domain/$', views.DomainListView.as_view(), name='domain-list'),
|
path('domain/', views.DomainListView.as_view(), name='domain-list'),
|
||||||
url(r'^domain/create/$', views.DomainCreateView.as_view(), name='domain-create'),
|
path('domain/create/', views.DomainCreateView.as_view(), name='domain-create'),
|
||||||
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.DomainDetailView.as_view(), name='domain-detail'),
|
path('domain/<uuid:pk>/', views.DomainDetailView.as_view(), name='domain-detail'),
|
||||||
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.DomainUpdateView.as_view(), name='domain-update'),
|
path('domain/<uuid:pk>/update/', views.DomainUpdateView.as_view(), name='domain-update'),
|
||||||
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.DomainDeleteView.as_view(), name='domain-delete'),
|
path('domain/<uuid:pk>/delete/', views.DomainDeleteView.as_view(), name='domain-delete'),
|
||||||
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/gateway/$', views.DomainGatewayListView.as_view(), name='domain-gateway-list'),
|
path('domain/<uuid:pk>/gateway/', views.DomainGatewayListView.as_view(), name='domain-gateway-list'),
|
||||||
|
|
||||||
url(r'^domain/(?P<pk>[0-9a-zA-Z\-]{36})/gateway/create/$', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'),
|
path('domain/<uuid:pk>/gateway/create/', views.DomainGatewayCreateView.as_view(), name='domain-gateway-create'),
|
||||||
url(r'^domain/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'),
|
path('domain/gateway/<uuid:pk>/update/', views.DomainGatewayUpdateView.as_view(), name='domain-gateway-update'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from .. import forms
|
from .. import forms
|
||||||
from ..models import AdminUser, Node
|
from ..models import AdminUser, Node
|
||||||
from ..hands import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'AdminUserCreateView', 'AdminUserDetailView',
|
'AdminUserCreateView', 'AdminUserDetailView',
|
||||||
|
|
|
@ -29,7 +29,7 @@ from common.utils import get_object_or_none, get_logger, is_uuid
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from .. import forms
|
from .. import forms
|
||||||
from ..models import Asset, AdminUser, SystemUser, Label, Node, Domain
|
from ..models import Asset, AdminUser, SystemUser, Label, Node, Domain
|
||||||
from ..hands import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -186,7 +186,7 @@ class AssetDeleteView(AdminUserRequiredMixin, DeleteView):
|
||||||
success_url = reverse_lazy('assets:asset-list')
|
success_url = reverse_lazy('assets:asset-list')
|
||||||
|
|
||||||
|
|
||||||
class AssetDetailView(DetailView):
|
class AssetDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = Asset
|
model = Asset
|
||||||
context_object_name = 'asset'
|
context_object_name = 'asset'
|
||||||
template_name = 'assets/asset_detail.html'
|
template_name = 'assets/asset_detail.html'
|
||||||
|
@ -203,7 +203,7 @@ class AssetDetailView(DetailView):
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class AssetExportView(View):
|
class AssetExportView(LoginRequiredMixin, View):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
spm = request.GET.get('spm', '')
|
spm = request.GET.get('spm', '')
|
||||||
assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else []
|
assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else []
|
||||||
|
@ -211,7 +211,7 @@ class AssetExportView(View):
|
||||||
fields = [
|
fields = [
|
||||||
field for field in Asset._meta.fields
|
field for field in Asset._meta.fields
|
||||||
if field.name not in [
|
if field.name not in [
|
||||||
'date_created'
|
'date_created', 'org_id'
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
filename = 'assets-{}.csv'.format(
|
filename = 'assets-{}.csv'.format(
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.views.generic.detail import SingleObjectMixin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.urls import reverse_lazy, reverse
|
from django.urls import reverse_lazy, reverse
|
||||||
|
|
||||||
from common.mixins import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
from ..models import Domain, Gateway
|
from ..models import Domain, Gateway
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.views.generic import TemplateView, CreateView, \
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
from common.mixins import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from ..models import Label
|
from ..models import Label
|
||||||
from ..forms import LabelForm
|
from ..forms import LabelForm
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.views.generic.detail import DetailView
|
||||||
from common.const import create_success_msg, update_success_msg
|
from common.const import create_success_msg, update_success_msg
|
||||||
from ..forms import SystemUserForm
|
from ..forms import SystemUserForm
|
||||||
from ..models import SystemUser, Node
|
from ..models import SystemUser, Node
|
||||||
from ..hands import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from common.permissions import IsSuperUserOrAppUser
|
from common.permissions import IsOrgAdminOrAppUser
|
||||||
from .models import FTPLog
|
from .models import FTPLog
|
||||||
from .serializers import FTPLogSerializer
|
from .serializers import FTPLogSerializer
|
||||||
|
|
||||||
|
@ -11,4 +11,4 @@ from .serializers import FTPLogSerializer
|
||||||
class FTPLogViewSet(viewsets.ModelViewSet):
|
class FTPLogViewSet(viewsets.ModelViewSet):
|
||||||
queryset = FTPLog.objects.all()
|
queryset = FTPLog.objects.all()
|
||||||
serializer_class = FTPLogSerializer
|
serializer_class = FTPLogSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
|
@ -3,8 +3,10 @@ import uuid
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
|
|
||||||
class FTPLog(models.Model):
|
|
||||||
|
class FTPLog(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
user = models.CharField(max_length=128, verbose_name=_('User'))
|
user = models.CharField(max_length=128, verbose_name=_('User'))
|
||||||
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
|
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
|
||||||
|
|
|
@ -9,10 +9,9 @@ from .. import api
|
||||||
app_name = "audits"
|
app_name = "audits"
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'v1/ftp-log', api.FTPLogViewSet, 'ftp-log')
|
router.register(r'ftp-log', api.FTPLogViewSet, 'ftp-log')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# url(r'^v1/celery/task/(?P<pk>[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
from django.conf.urls import url
|
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
__all__ = ["urlpatterns"]
|
__all__ = ["urlpatterns"]
|
||||||
|
@ -10,5 +9,5 @@ __all__ = ["urlpatterns"]
|
||||||
app_name = "audits"
|
app_name = "audits"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^ftp-log/$', views.FTPLogListView.as_view(), name='ftp-log-list'),
|
path('ftp-log/', views.FTPLogListView.as_view(), name='ftp-log-list'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -2,7 +2,8 @@ from django.conf import settings
|
||||||
from django.views.generic import ListView
|
from django.views.generic import ListView
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from common.mixins import AdminUserRequiredMixin, DatetimeSearchMixin
|
from common.mixins import DatetimeSearchMixin
|
||||||
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
|
||||||
from .models import FTPLog
|
from .models import FTPLog
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ from django.core.mail import get_connection, send_mail
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from .permissions import IsSuperUser
|
from .permissions import IsOrgAdmin
|
||||||
from .serializers import MailTestSerializer, LDAPTestSerializer
|
from .serializers import MailTestSerializer, LDAPTestSerializer
|
||||||
|
|
||||||
|
|
||||||
class MailTestingAPI(APIView):
|
class MailTestingAPI(APIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = MailTestSerializer
|
serializer_class = MailTestSerializer
|
||||||
success_message = _("Test mail sent to {}, please check")
|
success_message = _("Test mail sent to {}, please check")
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ class MailTestingAPI(APIView):
|
||||||
|
|
||||||
|
|
||||||
class LDAPTestingAPI(APIView):
|
class LDAPTestingAPI(APIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = LDAPTestSerializer
|
serializer_class = LDAPTestSerializer
|
||||||
success_message = _("Test ldap success")
|
success_message = _("Test ldap success")
|
||||||
|
|
||||||
|
@ -86,7 +86,18 @@ class LDAPTestingAPI(APIView):
|
||||||
|
|
||||||
class DjangoSettingsAPI(APIView):
|
class DjangoSettingsAPI(APIView):
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
return Response('Danger, Close now')
|
if not settings.DEBUG:
|
||||||
|
return Response("Not in debug mode")
|
||||||
|
|
||||||
|
data = {}
|
||||||
|
for k, v in settings.__dict__.items():
|
||||||
|
if k and k.isupper():
|
||||||
|
try:
|
||||||
|
json.dumps(v)
|
||||||
|
data[k] = v
|
||||||
|
except (json.JSONDecodeError, TypeError):
|
||||||
|
data[k] = str(v)
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ class EncryptMixin:
|
||||||
def get_prep_value(self, value):
|
def get_prep_value(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
return value
|
return value
|
||||||
return signer.sign(value).decode('utf-8')
|
return signer.sign(value)
|
||||||
|
|
||||||
|
|
||||||
class EncryptTextField(EncryptMixin, models.TextField):
|
class EncryptTextField(EncryptMixin, models.TextField):
|
||||||
|
|
|
@ -4,7 +4,6 @@ from django.db import models
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
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 _
|
||||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
|
||||||
|
|
||||||
|
|
||||||
class NoDeleteQuerySet(models.query.QuerySet):
|
class NoDeleteQuerySet(models.query.QuerySet):
|
||||||
|
@ -119,11 +118,4 @@ class DatetimeSearchMixin:
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class AdminUserRequiredMixin(UserPassesTestMixin):
|
|
||||||
def test_func(self):
|
|
||||||
if not self.request.user.is_authenticated:
|
|
||||||
return False
|
|
||||||
elif not self.request.user.is_superuser:
|
|
||||||
self.raise_exception = True
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from rest_framework import permissions
|
from rest_framework import permissions
|
||||||
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.http.response import HttpResponseForbidden
|
||||||
|
|
||||||
|
from orgs.utils import current_org
|
||||||
|
|
||||||
|
|
||||||
class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
|
class IsValidUser(permissions.IsAuthenticated, permissions.BasePermission):
|
||||||
|
@ -21,28 +26,40 @@ class IsAppUser(IsValidUser):
|
||||||
|
|
||||||
|
|
||||||
class IsSuperUser(IsValidUser):
|
class IsSuperUser(IsValidUser):
|
||||||
"""Allows access only to superuser"""
|
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
return super(IsSuperUser, self).has_permission(request, view) \
|
return super(IsSuperUser, self).has_permission(request, view) \
|
||||||
and request.user.is_superuser
|
and request.user.is_superuser
|
||||||
|
|
||||||
|
|
||||||
class IsSuperUserOrAppUser(IsValidUser):
|
class IsSuperUserOrAppUser(IsSuperUser):
|
||||||
"""Allows access between superuser and app user"""
|
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
|
return super(IsSuperUserOrAppUser, self).has_permission(request, view) \
|
||||||
and (request.user.is_superuser or request.user.is_app)
|
and (request.user.is_superuser or request.user.is_app)
|
||||||
|
|
||||||
|
|
||||||
class IsSuperUserOrAppUserOrUserReadonly(IsSuperUserOrAppUser):
|
class IsOrgAdmin(IsValidUser):
|
||||||
|
"""Allows access only to superuser"""
|
||||||
|
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
return super(IsOrgAdmin, self).has_permission(request, view) \
|
||||||
|
and current_org.can_admin_by(request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class IsOrgAdminOrAppUser(IsValidUser):
|
||||||
|
"""Allows access between superuser and app user"""
|
||||||
|
|
||||||
|
def has_permission(self, request, view):
|
||||||
|
return super(IsOrgAdminOrAppUser, self).has_permission(request, view) \
|
||||||
|
and (current_org.can_admin_by(request.user) or request.user.is_app)
|
||||||
|
|
||||||
|
|
||||||
|
class IsOrgAdminOrAppUserOrUserReadonly(IsOrgAdminOrAppUser):
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
if IsValidUser.has_permission(self, request, view) \
|
if IsValidUser.has_permission(self, request, view) \
|
||||||
and request.method in permissions.SAFE_METHODS:
|
and request.method in permissions.SAFE_METHODS:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return IsSuperUserOrAppUser.has_permission(self, request, view)
|
return IsOrgAdminOrAppUser.has_permission(self, request, view)
|
||||||
|
|
||||||
|
|
||||||
class IsCurrentUserOrReadOnly(permissions.BasePermission):
|
class IsCurrentUserOrReadOnly(permissions.BasePermission):
|
||||||
|
@ -50,3 +67,31 @@ class IsCurrentUserOrReadOnly(permissions.BasePermission):
|
||||||
if request.method in permissions.SAFE_METHODS:
|
if request.method in permissions.SAFE_METHODS:
|
||||||
return True
|
return True
|
||||||
return obj == request.user
|
return obj == request.user
|
||||||
|
|
||||||
|
|
||||||
|
class AdminUserRequiredMixin(UserPassesTestMixin):
|
||||||
|
def test_func(self):
|
||||||
|
if not self.request.user.is_authenticated:
|
||||||
|
return False
|
||||||
|
elif not current_org.can_admin_by(self.request.user):
|
||||||
|
self.raise_exception = True
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
print("Current org: {}".format(current_org))
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
if not current_org:
|
||||||
|
return redirect('orgs:switch-a-org')
|
||||||
|
|
||||||
|
if not current_org.can_admin_by(request.user):
|
||||||
|
print("{} cannot admin {}".format(request.user, current_org))
|
||||||
|
if request.user.is_org_admin:
|
||||||
|
print("Is org admin")
|
||||||
|
return redirect('orgs:switch-a-org')
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
else:
|
||||||
|
print(current_org.can_admin_by(request.user))
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
|
|
||||||
from .. import api
|
from .. import api
|
||||||
|
|
||||||
app_name = 'common'
|
app_name = 'common'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^v1/mail/testing/$', api.MailTestingAPI.as_view(), name='mail-testing'),
|
path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'),
|
||||||
url(r'^v1/ldap/testing/$', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
|
path('ldap/testing/', api.LDAPTestingAPI.as_view(), name='ldap-testing'),
|
||||||
url(r'^v1/django-settings/$', api.DjangoSettingsAPI.as_view(), name='django-settings'),
|
# path('django-settings/', api.DjangoSettingsAPI.as_view(), name='django-settings'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,6 +17,7 @@ import threading
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
import uuid
|
import uuid
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
import copy
|
||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
import sshpubkeys
|
import sshpubkeys
|
||||||
|
@ -67,10 +68,8 @@ class Signer(metaclass=Singleton):
|
||||||
self.secret_key = secret_key
|
self.secret_key = secret_key
|
||||||
|
|
||||||
def sign(self, value):
|
def sign(self, value):
|
||||||
if isinstance(value, bytes):
|
|
||||||
value = value.decode("utf-8")
|
|
||||||
s = JSONWebSignatureSerializer(self.secret_key)
|
s = JSONWebSignatureSerializer(self.secret_key)
|
||||||
return s.dumps(value)
|
return s.dumps(value).decode()
|
||||||
|
|
||||||
def unsign(self, value):
|
def unsign(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
|
@ -410,3 +409,122 @@ def with_cache(func):
|
||||||
cache[key] = res
|
cache[key] = res
|
||||||
return res
|
return res
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class LocalProxy(object):
|
||||||
|
|
||||||
|
"""
|
||||||
|
Copy from werkzeug.local.LocalProxy
|
||||||
|
"""
|
||||||
|
__slots__ = ('__local', '__dict__', '__name__', '__wrapped__')
|
||||||
|
|
||||||
|
def __init__(self, local, name=None):
|
||||||
|
object.__setattr__(self, '_LocalProxy__local', local)
|
||||||
|
object.__setattr__(self, '__name__', name)
|
||||||
|
if callable(local) and not hasattr(local, '__release_local__'):
|
||||||
|
# "local" is a callable that is not an instance of Local or
|
||||||
|
# LocalManager: mark it as a wrapped function.
|
||||||
|
object.__setattr__(self, '__wrapped__', local)
|
||||||
|
|
||||||
|
def _get_current_object(self):
|
||||||
|
"""Return the current object. This is useful if you want the real
|
||||||
|
object behind the proxy at a time for performance reasons or because
|
||||||
|
you want to pass the object into a different context.
|
||||||
|
"""
|
||||||
|
if not hasattr(self.__local, '__release_local__'):
|
||||||
|
return self.__local()
|
||||||
|
try:
|
||||||
|
return getattr(self.__local, self.__name__)
|
||||||
|
except AttributeError:
|
||||||
|
raise RuntimeError('no object bound to %s' % self.__name__)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __dict__(self):
|
||||||
|
try:
|
||||||
|
return self._get_current_object().__dict__
|
||||||
|
except RuntimeError:
|
||||||
|
raise AttributeError('__dict__')
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
try:
|
||||||
|
obj = self._get_current_object()
|
||||||
|
except RuntimeError:
|
||||||
|
return '<%s unbound>' % self.__class__.__name__
|
||||||
|
return repr(obj)
|
||||||
|
|
||||||
|
def __bool__(self):
|
||||||
|
try:
|
||||||
|
return bool(self._get_current_object())
|
||||||
|
except RuntimeError:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def __dir__(self):
|
||||||
|
try:
|
||||||
|
return dir(self._get_current_object())
|
||||||
|
except RuntimeError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name == '__members__':
|
||||||
|
return dir(self._get_current_object())
|
||||||
|
return getattr(self._get_current_object(), name)
|
||||||
|
|
||||||
|
def __setitem__(self, key, value):
|
||||||
|
self._get_current_object()[key] = value
|
||||||
|
|
||||||
|
def __delitem__(self, key):
|
||||||
|
del self._get_current_object()[key]
|
||||||
|
|
||||||
|
__setattr__ = lambda x, n, v: setattr(x._get_current_object(), n, v)
|
||||||
|
__delattr__ = lambda x, n: delattr(x._get_current_object(), n)
|
||||||
|
__str__ = lambda x: str(x._get_current_object())
|
||||||
|
__lt__ = lambda x, o: x._get_current_object() < o
|
||||||
|
__le__ = lambda x, o: x._get_current_object() <= o
|
||||||
|
__eq__ = lambda x, o: x._get_current_object() == o
|
||||||
|
__ne__ = lambda x, o: x._get_current_object() != o
|
||||||
|
__gt__ = lambda x, o: x._get_current_object() > o
|
||||||
|
__ge__ = lambda x, o: x._get_current_object() >= o
|
||||||
|
__cmp__ = lambda x, o: cmp(x._get_current_object(), o) # noqa
|
||||||
|
__hash__ = lambda x: hash(x._get_current_object())
|
||||||
|
__call__ = lambda x, *a, **kw: x._get_current_object()(*a, **kw)
|
||||||
|
__len__ = lambda x: len(x._get_current_object())
|
||||||
|
__getitem__ = lambda x, i: x._get_current_object()[i]
|
||||||
|
__iter__ = lambda x: iter(x._get_current_object())
|
||||||
|
__contains__ = lambda x, i: i in x._get_current_object()
|
||||||
|
__add__ = lambda x, o: x._get_current_object() + o
|
||||||
|
__sub__ = lambda x, o: x._get_current_object() - o
|
||||||
|
__mul__ = lambda x, o: x._get_current_object() * o
|
||||||
|
__floordiv__ = lambda x, o: x._get_current_object() // o
|
||||||
|
__mod__ = lambda x, o: x._get_current_object() % o
|
||||||
|
__divmod__ = lambda x, o: x._get_current_object().__divmod__(o)
|
||||||
|
__pow__ = lambda x, o: x._get_current_object() ** o
|
||||||
|
__lshift__ = lambda x, o: x._get_current_object() << o
|
||||||
|
__rshift__ = lambda x, o: x._get_current_object() >> o
|
||||||
|
__and__ = lambda x, o: x._get_current_object() & o
|
||||||
|
__xor__ = lambda x, o: x._get_current_object() ^ o
|
||||||
|
__or__ = lambda x, o: x._get_current_object() | o
|
||||||
|
__div__ = lambda x, o: x._get_current_object().__div__(o)
|
||||||
|
__truediv__ = lambda x, o: x._get_current_object().__truediv__(o)
|
||||||
|
__neg__ = lambda x: -(x._get_current_object())
|
||||||
|
__pos__ = lambda x: +(x._get_current_object())
|
||||||
|
__abs__ = lambda x: abs(x._get_current_object())
|
||||||
|
__invert__ = lambda x: ~(x._get_current_object())
|
||||||
|
__complex__ = lambda x: complex(x._get_current_object())
|
||||||
|
__int__ = lambda x: int(x._get_current_object())
|
||||||
|
__float__ = lambda x: float(x._get_current_object())
|
||||||
|
__oct__ = lambda x: oct(x._get_current_object())
|
||||||
|
__hex__ = lambda x: hex(x._get_current_object())
|
||||||
|
__index__ = lambda x: x._get_current_object().__index__()
|
||||||
|
__coerce__ = lambda x, o: x._get_current_object().__coerce__(x, o)
|
||||||
|
__enter__ = lambda x: x._get_current_object().__enter__()
|
||||||
|
__exit__ = lambda x, *a, **kw: x._get_current_object().__exit__(*a, **kw)
|
||||||
|
__radd__ = lambda x, o: o + x._get_current_object()
|
||||||
|
__rsub__ = lambda x, o: o - x._get_current_object()
|
||||||
|
__rmul__ = lambda x, o: o * x._get_current_object()
|
||||||
|
__rdiv__ = lambda x, o: o / x._get_current_object()
|
||||||
|
__rtruediv__ = __rdiv__
|
||||||
|
__rfloordiv__ = lambda x, o: o // x._get_current_object()
|
||||||
|
__rmod__ = lambda x, o: o % x._get_current_object()
|
||||||
|
__rdivmod__ = lambda x, o: x._get_current_object().__rdivmod__(o)
|
||||||
|
__copy__ = lambda x: copy.copy(x._get_current_object())
|
||||||
|
__deepcopy__ = lambda x, memo: copy.deepcopy(x._get_current_object(), memo)
|
|
@ -1,14 +1,12 @@
|
||||||
|
from django.views.generic import TemplateView
|
||||||
from django.core.cache import cache
|
from django.shortcuts import render, redirect
|
||||||
from django.views.generic import TemplateView, View, DetailView
|
|
||||||
from django.shortcuts import render, redirect, Http404, reverse
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
|
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
|
||||||
TerminalSettingForm, SecuritySettingForm
|
TerminalSettingForm, SecuritySettingForm
|
||||||
from .mixins import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
from .signals import ldap_auth_enable
|
from .signals import ldap_auth_enable
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -56,6 +56,7 @@ ALLOWED_HOSTS = CONFIG.ALLOWED_HOSTS or []
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
'orgs.apps.OrgsConfig',
|
||||||
'users.apps.UsersConfig',
|
'users.apps.UsersConfig',
|
||||||
'assets.apps.AssetsConfig',
|
'assets.apps.AssetsConfig',
|
||||||
'perms.apps.PermsConfig',
|
'perms.apps.PermsConfig',
|
||||||
|
@ -65,6 +66,7 @@ INSTALLED_APPS = [
|
||||||
'audits.apps.AuditsConfig',
|
'audits.apps.AuditsConfig',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
'rest_framework_swagger',
|
'rest_framework_swagger',
|
||||||
|
'drf_yasg',
|
||||||
'django_filters',
|
'django_filters',
|
||||||
'bootstrap3',
|
'bootstrap3',
|
||||||
'captcha',
|
'captcha',
|
||||||
|
@ -76,6 +78,12 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
XPACK_DIR = os.path.join(BASE_DIR, 'xpack')
|
||||||
|
XPACK_ENABLED = os.path.isdir(XPACK_DIR)
|
||||||
|
if XPACK_ENABLED:
|
||||||
|
INSTALLED_APPS.append('xpack.apps.XpackConfig')
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
@ -87,14 +95,35 @@ MIDDLEWARE = [
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
'jumpserver.middleware.TimezoneMiddleware',
|
'jumpserver.middleware.TimezoneMiddleware',
|
||||||
'jumpserver.middleware.DemoMiddleware',
|
'jumpserver.middleware.DemoMiddleware',
|
||||||
|
'orgs.middleware.OrgMiddleware',
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = 'jumpserver.urls'
|
ROOT_URLCONF = 'jumpserver.urls'
|
||||||
|
|
||||||
|
|
||||||
|
def get_xpack_context_processor():
|
||||||
|
if XPACK_ENABLED:
|
||||||
|
return ['xpack.context_processor.xpack_processor']
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def get_xpack_templates_dir():
|
||||||
|
if XPACK_ENABLED:
|
||||||
|
dirs = []
|
||||||
|
from xpack.utils import find_enabled_plugins
|
||||||
|
for i in find_enabled_plugins():
|
||||||
|
template_dir = os.path.join(BASE_DIR, 'xpack', 'plugins', i, 'templates')
|
||||||
|
if os.path.isdir(template_dir):
|
||||||
|
dirs.append(template_dir)
|
||||||
|
return dirs
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
|
'DIRS': [os.path.join(BASE_DIR, 'templates'), *get_xpack_templates_dir()],
|
||||||
'APP_DIRS': True,
|
'APP_DIRS': True,
|
||||||
'OPTIONS': {
|
'OPTIONS': {
|
||||||
'context_processors': [
|
'context_processors': [
|
||||||
|
@ -107,6 +136,8 @@ TEMPLATES = [
|
||||||
'django.template.context_processors.static',
|
'django.template.context_processors.static',
|
||||||
'django.template.context_processors.request',
|
'django.template.context_processors.request',
|
||||||
'django.template.context_processors.media',
|
'django.template.context_processors.media',
|
||||||
|
'orgs.context_processor.org_processor',
|
||||||
|
*get_xpack_context_processor(),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -227,13 +258,13 @@ LOGGING = {
|
||||||
'level': LOG_LEVEL,
|
'level': LOG_LEVEL,
|
||||||
},
|
},
|
||||||
'django_auth_ldap': {
|
'django_auth_ldap': {
|
||||||
'handlers': ['console', 'ansible_logs'],
|
'handlers': ['console', 'file'],
|
||||||
'level': "INFO",
|
'level': "INFO",
|
||||||
},
|
},
|
||||||
# 'django.db': {
|
'django.db': {
|
||||||
# 'handlers': ['console', 'file'],
|
'handlers': ['console', 'file'],
|
||||||
# 'level': 'DEBUG'
|
'level': 'DEBUG'
|
||||||
# }
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +319,10 @@ REST_FRAMEWORK = {
|
||||||
# Use Django's standard `django.contrib.auth` permissions,
|
# Use Django's standard `django.contrib.auth` permissions,
|
||||||
# or allow read-only access for unauthenticated users.
|
# or allow read-only access for unauthenticated users.
|
||||||
'DEFAULT_PERMISSION_CLASSES': (
|
'DEFAULT_PERMISSION_CLASSES': (
|
||||||
'users.permissions.IsSuperUser',
|
'common.permissions.IsOrgAdmin',
|
||||||
),
|
),
|
||||||
'DEFAULT_AUTHENTICATION_CLASSES': (
|
'DEFAULT_AUTHENTICATION_CLASSES': (
|
||||||
|
'rest_framework.authentication.BasicAuthentication',
|
||||||
'users.authentication.AccessKeyAuthentication',
|
'users.authentication.AccessKeyAuthentication',
|
||||||
'users.authentication.AccessTokenAuthentication',
|
'users.authentication.AccessTokenAuthentication',
|
||||||
'users.authentication.PrivateTokenAuthentication',
|
'users.authentication.PrivateTokenAuthentication',
|
||||||
|
@ -373,7 +405,7 @@ CACHES = {
|
||||||
'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '',
|
'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '',
|
||||||
'host': CONFIG.REDIS_HOST or '127.0.0.1',
|
'host': CONFIG.REDIS_HOST or '127.0.0.1',
|
||||||
'port': CONFIG.REDIS_PORT or 6379,
|
'port': CONFIG.REDIS_PORT or 6379,
|
||||||
'db':CONFIG.REDIS_DB_CACHE or 4,
|
'db': CONFIG.REDIS_DB_CACHE or 4,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -423,3 +455,12 @@ TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION or 3600
|
||||||
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE or 25
|
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE or 25
|
||||||
DEFAULT_EXPIRED_YEARS = 70
|
DEFAULT_EXPIRED_YEARS = 70
|
||||||
USER_GUIDE_URL = ""
|
USER_GUIDE_URL = ""
|
||||||
|
|
||||||
|
|
||||||
|
SWAGGER_SETTINGS = {
|
||||||
|
'SECURITY_DEFINITIONS': {
|
||||||
|
'basic': {
|
||||||
|
'type': 'basic'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,45 +1,102 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
from django.conf.urls import url, include
|
from django.urls import path, include, re_path
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.conf.urls.static import static
|
from django.conf.urls.static import static
|
||||||
|
from rest_framework.response import Response
|
||||||
from rest_framework.schemas import get_schema_view
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from rest_framework_swagger.renderers import SwaggerUIRenderer, OpenAPIRenderer
|
from django.http import HttpResponse
|
||||||
|
from django.utils.encoding import iri_to_uri
|
||||||
|
from rest_framework import permissions
|
||||||
|
from drf_yasg.views import get_schema_view
|
||||||
|
from drf_yasg import openapi
|
||||||
|
|
||||||
from .views import IndexView, LunaView
|
from .views import IndexView, LunaView
|
||||||
|
|
||||||
schema_view = get_schema_view(title='Users API', renderer_classes=[OpenAPIRenderer, SwaggerUIRenderer])
|
schema_view = get_schema_view(
|
||||||
urlpatterns = [
|
openapi.Info(
|
||||||
url(r'^$', IndexView.as_view(), name='index'),
|
title="Jumpserver API Docs",
|
||||||
url(r'^luna/', LunaView.as_view(), name='luna-error'),
|
default_version='v1',
|
||||||
url(r'^users/', include('users.urls.views_urls', namespace='users')),
|
description="Jumpserver Restful api docs",
|
||||||
url(r'^assets/', include('assets.urls.views_urls', namespace='assets')),
|
terms_of_service="https://www.jumpserver.org",
|
||||||
url(r'^perms/', include('perms.urls.views_urls', namespace='perms')),
|
contact=openapi.Contact(email="support@fit2cloud.com"),
|
||||||
url(r'^terminal/', include('terminal.urls.views_urls', namespace='terminal')),
|
license=openapi.License(name="GPLv2 License"),
|
||||||
url(r'^ops/', include('ops.urls.view_urls', namespace='ops')),
|
),
|
||||||
url(r'^audits/', include('audits.urls.view_urls', namespace='audits')),
|
public=True,
|
||||||
url(r'^settings/', include('common.urls.view_urls', namespace='settings')),
|
permission_classes=(permissions.AllowAny,),
|
||||||
url(r'^common/', include('common.urls.view_urls', namespace='common')),
|
)
|
||||||
|
api_url_pattern = re.compile(r'^/api/(?P<version>\w+)/(?P<app>\w+)/(?P<extra>.*)$')
|
||||||
|
|
||||||
# Api url view map
|
|
||||||
url(r'^api/users/', include('users.urls.api_urls', namespace='api-users')),
|
|
||||||
url(r'^api/assets/', include('assets.urls.api_urls', namespace='api-assets')),
|
|
||||||
url(r'^api/perms/', include('perms.urls.api_urls', namespace='api-perms')),
|
|
||||||
url(r'^api/terminal/', include('terminal.urls.api_urls', namespace='api-terminal')),
|
|
||||||
url(r'^api/ops/', include('ops.urls.api_urls', namespace='api-ops')),
|
|
||||||
url(r'^api/audits/', include('audits.urls.api_urls', namespace='api-audits')),
|
|
||||||
url(r'^api/common/', include('common.urls.api_urls', namespace='api-common')),
|
|
||||||
|
|
||||||
# External apps url
|
class HttpResponseTemporaryRedirect(HttpResponse):
|
||||||
url(r'^captcha/', include('captcha.urls')),
|
status_code = 307
|
||||||
|
|
||||||
|
def __init__(self, redirect_to):
|
||||||
|
HttpResponse.__init__(self)
|
||||||
|
self['Location'] = iri_to_uri(redirect_to)
|
||||||
|
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
def redirect_format_api(request, *args, **kwargs):
|
||||||
|
_path, query = request.path, request.GET.urlencode()
|
||||||
|
matched = api_url_pattern.match(_path)
|
||||||
|
if matched:
|
||||||
|
version, app, extra = matched.groups()
|
||||||
|
_path = '/api/{app}/{version}/{extra}?{query}'.format(**{
|
||||||
|
"app": app, "version": version, "extra": extra,
|
||||||
|
"query": query
|
||||||
|
})
|
||||||
|
return HttpResponseTemporaryRedirect(_path)
|
||||||
|
else:
|
||||||
|
return Response({"msg": "Redirect url failed: {}".format(_path)}, status=404)
|
||||||
|
|
||||||
|
|
||||||
|
v1_api_patterns = [
|
||||||
|
path('users/v1/', include('users.urls.api_urls', namespace='api-users')),
|
||||||
|
path('assets/v1/', include('assets.urls.api_urls', namespace='api-assets')),
|
||||||
|
path('perms/v1/', include('perms.urls.api_urls', namespace='api-perms')),
|
||||||
|
path('terminal/v1/', include('terminal.urls.api_urls', namespace='api-terminal')),
|
||||||
|
path('ops/v1/', include('ops.urls.api_urls', namespace='api-ops')),
|
||||||
|
path('audits/v1/', include('audits.urls.api_urls', namespace='api-audits')),
|
||||||
|
path('orgs/v1/', include('orgs.urls.api_urls', namespace='api-orgs')),
|
||||||
|
path('common/v1/', include('common.urls.api_urls', namespace='api-common')),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
app_view_patterns = [
|
||||||
|
path('users/', include('users.urls.views_urls', namespace='users')),
|
||||||
|
path('assets/', include('assets.urls.views_urls', namespace='assets')),
|
||||||
|
path('perms/', include('perms.urls.views_urls', namespace='perms')),
|
||||||
|
path('terminal/', include('terminal.urls.views_urls', namespace='terminal')),
|
||||||
|
path('ops/', include('ops.urls.view_urls', namespace='ops')),
|
||||||
|
path('audits/', include('audits.urls.view_urls', namespace='audits')),
|
||||||
|
path('orgs/', include('orgs.urls.views_urls', namespace='orgs')),
|
||||||
|
]
|
||||||
|
|
||||||
|
if settings.XPACK_ENABLED:
|
||||||
|
app_view_patterns.append(path('xpack/', include('xpack.urls', namespace='xpack')))
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', IndexView.as_view(), name='index'),
|
||||||
|
path('luna/', LunaView.as_view(), name='luna-error'),
|
||||||
|
path('settings/', include('common.urls.view_urls', namespace='settings')),
|
||||||
|
path('common/', include('common.urls.view_urls', namespace='common')),
|
||||||
|
path('api/v1/', redirect_format_api),
|
||||||
|
path('api/', include(v1_api_patterns)),
|
||||||
|
|
||||||
|
# External apps url
|
||||||
|
path('captcha/', include('captcha.urls')),
|
||||||
|
]
|
||||||
|
urlpatterns += app_view_patterns
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \
|
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \
|
||||||
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
url(r'^docs/', schema_view, name="docs"),
|
re_path('swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=None), name='schema-json'),
|
||||||
|
path('docs/', schema_view.with_ui('swagger', cache_timeout=None), name="docs"),
|
||||||
|
path('redoc/', schema_view.with_ui('redoc', cache_timeout=None), name='redoc'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,12 +4,13 @@ from django.http import HttpResponse
|
||||||
from django.views.generic import TemplateView, View
|
from django.views.generic import TemplateView, View
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from assets.models import Asset
|
from assets.models import Asset
|
||||||
from terminal.models import Session
|
from terminal.models import Session
|
||||||
|
from orgs.utils import current_org
|
||||||
|
|
||||||
|
|
||||||
class IndexView(LoginRequiredMixin, TemplateView):
|
class IndexView(LoginRequiredMixin, TemplateView):
|
||||||
|
@ -20,14 +21,16 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
||||||
session_month_dates = []
|
session_month_dates = []
|
||||||
session_month_dates_archive = []
|
session_month_dates_archive = []
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not request.user.is_superuser:
|
if not request.user.is_authenticated:
|
||||||
|
return self.handle_no_permission()
|
||||||
|
if not request.user.is_org_admin:
|
||||||
return redirect('assets:user-asset-list')
|
return redirect('assets:user-asset-list')
|
||||||
return super(IndexView, self).get(request, *args, **kwargs)
|
return super(IndexView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_user_count():
|
def get_user_count():
|
||||||
return User.objects.filter(role__in=('Admin', 'User')).count()
|
return current_org.get_org_users().count()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_asset_count():
|
def get_asset_count():
|
||||||
|
@ -49,7 +52,6 @@ class IndexView(LoginRequiredMixin, TemplateView):
|
||||||
|
|
||||||
def get_week_login_asset_count(self):
|
def get_week_login_asset_count(self):
|
||||||
return self.session_week.count()
|
return self.session_week.count()
|
||||||
# return self.session_week.values('asset').distinct().count()
|
|
||||||
|
|
||||||
def get_month_day_metrics(self):
|
def get_month_day_metrics(self):
|
||||||
month_str = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
|
month_str = [d.strftime('%m-%d') for d in self.session_month_dates] or ['0']
|
||||||
|
@ -175,4 +177,7 @@ class LunaView(View):
|
||||||
Luna是单独部署的一个程序,你需要部署luna,coco,配置nginx做url分发,
|
Luna是单独部署的一个程序,你需要部署luna,coco,配置nginx做url分发,
|
||||||
如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运
|
如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运
|
||||||
"""
|
"""
|
||||||
return HttpResponse(msg)
|
return HttpResponse(msg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.utils.translation import ugettext as _
|
||||||
from rest_framework import viewsets, generics
|
from rest_framework import viewsets, generics
|
||||||
from rest_framework.views import Response
|
from rest_framework.views import Response
|
||||||
|
|
||||||
from .hands import IsSuperUser
|
from common.permissions import IsOrgAdmin
|
||||||
from .models import Task, AdHoc, AdHocRunHistory, CeleryTask
|
from .models import Task, AdHoc, AdHocRunHistory, CeleryTask
|
||||||
from .serializers import TaskSerializer, AdHocSerializer, \
|
from .serializers import TaskSerializer, AdHocSerializer, \
|
||||||
AdHocRunHistorySerializer
|
AdHocRunHistorySerializer
|
||||||
|
@ -18,13 +18,15 @@ from .tasks import run_ansible_task
|
||||||
class TaskViewSet(viewsets.ModelViewSet):
|
class TaskViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Task.objects.all()
|
queryset = Task.objects.all()
|
||||||
serializer_class = TaskSerializer
|
serializer_class = TaskSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
label = None
|
||||||
|
help_text = ''
|
||||||
|
|
||||||
|
|
||||||
class TaskRun(generics.RetrieveAPIView):
|
class TaskRun(generics.RetrieveAPIView):
|
||||||
queryset = Task.objects.all()
|
queryset = Task.objects.all()
|
||||||
serializer_class = TaskViewSet
|
serializer_class = TaskViewSet
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
task = self.get_object()
|
task = self.get_object()
|
||||||
|
@ -35,7 +37,7 @@ class TaskRun(generics.RetrieveAPIView):
|
||||||
class AdHocViewSet(viewsets.ModelViewSet):
|
class AdHocViewSet(viewsets.ModelViewSet):
|
||||||
queryset = AdHoc.objects.all()
|
queryset = AdHoc.objects.all()
|
||||||
serializer_class = AdHocSerializer
|
serializer_class = AdHocSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
task_id = self.request.query_params.get('task')
|
task_id = self.request.query_params.get('task')
|
||||||
|
@ -48,7 +50,7 @@ class AdHocViewSet(viewsets.ModelViewSet):
|
||||||
class AdHocRunHistorySet(viewsets.ModelViewSet):
|
class AdHocRunHistorySet(viewsets.ModelViewSet):
|
||||||
queryset = AdHocRunHistory.objects.all()
|
queryset = AdHocRunHistory.objects.all()
|
||||||
serializer_class = AdHocRunHistorySerializer
|
serializer_class = AdHocRunHistorySerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
task_id = self.request.query_params.get('task')
|
task_id = self.request.query_params.get('task')
|
||||||
|
@ -65,7 +67,7 @@ class AdHocRunHistorySet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
|
|
||||||
class CeleryTaskLogApi(generics.RetrieveAPIView):
|
class CeleryTaskLogApi(generics.RetrieveAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
buff_size = 1024 * 10
|
buff_size = 1024 * 10
|
||||||
end = False
|
end = False
|
||||||
queryset = CeleryTask.objects.all()
|
queryset = CeleryTask.objects.all()
|
||||||
|
|
|
@ -7,5 +7,9 @@ class OpsConfig(AppConfig):
|
||||||
name = 'ops'
|
name = 'ops'
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
|
from orgs.models import Organization
|
||||||
|
from orgs.utils import set_current_org
|
||||||
|
set_current_org(Organization.root())
|
||||||
|
|
||||||
super().ready()
|
super().ready()
|
||||||
from .celery import signal_handler
|
from .celery import signal_handler
|
||||||
|
|
|
@ -1,4 +1,2 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
from users.permissions import IsSuperUser
|
|
||||||
from users.utils import AdminUserRequiredMixin
|
|
|
@ -263,7 +263,8 @@ class AdHoc(models.Model):
|
||||||
}
|
}
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
self._become = signer.sign(json.dumps(item)).decode('utf-8')
|
# self._become = signer.sign(json.dumps(item)).decode('utf-8')
|
||||||
|
self._become = signer.sign(json.dumps(item))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def options(self):
|
def options(self):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from .. import api
|
from .. import api
|
||||||
|
|
||||||
|
@ -9,13 +9,13 @@ from .. import api
|
||||||
app_name = "ops"
|
app_name = "ops"
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register(r'v1/tasks', api.TaskViewSet, 'task')
|
router.register(r'tasks', api.TaskViewSet, 'task')
|
||||||
router.register(r'v1/adhoc', api.AdHocViewSet, 'adhoc')
|
router.register(r'adhoc', api.AdHocViewSet, 'adhoc')
|
||||||
router.register(r'v1/history', api.AdHocRunHistorySet, 'history')
|
router.register(r'history', api.AdHocRunHistorySet, 'history')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^v1/tasks/(?P<pk>[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'),
|
path('tasks/<uuid:pk>/run/', api.TaskRun.as_view(), name='task-run'),
|
||||||
url(r'^v1/celery/task/(?P<pk>[0-9a-zA-Z\-]{36})/log/$', api.CeleryTaskLogApi.as_view(), name='celery-task-log'),
|
path('celery/task/<uuid:pk>/log/', api.CeleryTaskLogApi.as_view(), name='celery-task-log'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
|
||||||
from django.conf.urls import url
|
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
__all__ = ["urlpatterns"]
|
__all__ = ["urlpatterns"]
|
||||||
|
@ -10,13 +9,13 @@ __all__ = ["urlpatterns"]
|
||||||
app_name = "ops"
|
app_name = "ops"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# TResource Task url
|
# Resource Task url
|
||||||
url(r'^task/$', views.TaskListView.as_view(), name='task-list'),
|
path('task/', views.TaskListView.as_view(), name='task-list'),
|
||||||
url(r'^task/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.TaskDetailView.as_view(), name='task-detail'),
|
path('task/<uuid:pk>/', views.TaskDetailView.as_view(), name='task-detail'),
|
||||||
url(r'^task/(?P<pk>[0-9a-zA-Z\-]{36})/adhoc/$', views.TaskAdhocView.as_view(), name='task-adhoc'),
|
path('task/<uuid:pk>/adhoc/', views.TaskAdhocView.as_view(), name='task-adhoc'),
|
||||||
url(r'^task/(?P<pk>[0-9a-zA-Z\-]{36})/history/$', views.TaskHistoryView.as_view(), name='task-history'),
|
path('task/<uuid:pk>/history/', views.TaskHistoryView.as_view(), name='task-history'),
|
||||||
url(r'^adhoc/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AdHocDetailView.as_view(), name='adhoc-detail'),
|
path('adhoc/<uuid:pk>/', views.AdHocDetailView.as_view(), name='adhoc-detail'),
|
||||||
url(r'^adhoc/(?P<pk>[0-9a-zA-Z\-]{36})/history/$', views.AdHocHistoryView.as_view(), name='adhoc-history'),
|
path('adhoc/<uuid:pk>/history/', views.AdHocHistoryView.as_view(), name='adhoc-history'),
|
||||||
url(r'^adhoc/history/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AdHocHistoryDetailView.as_view(), name='adhoc-history-detail'),
|
path('adhoc/history/<uuid:pk>/', views.AdHocHistoryDetailView.as_view(), name='adhoc-history-detail'),
|
||||||
url(r'^celery/task/(?P<pk>[0-9a-zA-Z\-]{36})/log/$', views.CeleryTaskLogView.as_view(), name='celery-task-log'),
|
path('celery/task/<uuid:pk>/log/', views.CeleryTaskLogView.as_view(), name='celery-task-log'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.views.generic import ListView, DetailView, TemplateView
|
||||||
|
|
||||||
from common.mixins import DatetimeSearchMixin
|
from common.mixins import DatetimeSearchMixin
|
||||||
from .models import Task, AdHoc, AdHocRunHistory, CeleryTask
|
from .models import Task, AdHoc, AdHocRunHistory, CeleryTask
|
||||||
from .hands import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
|
||||||
|
|
||||||
class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from rest_framework import viewsets
|
||||||
|
|
||||||
|
from common.permissions import IsSuperUserOrAppUser
|
||||||
|
from .models import Organization
|
||||||
|
from .serializers import OrgSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class OrgViewSet(viewsets.ModelViewSet):
|
||||||
|
queryset = Organization.objects.all()
|
||||||
|
serializer_class = OrgSerializer
|
||||||
|
permission_classes = (IsSuperUserOrAppUser,)
|
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class OrgsConfig(AppConfig):
|
||||||
|
name = 'orgs'
|
|
@ -0,0 +1,15 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from .utils import current_org, get_current_org
|
||||||
|
from .models import Organization
|
||||||
|
|
||||||
|
|
||||||
|
def org_processor(request):
|
||||||
|
context = {
|
||||||
|
'ADMIN_ORGS': Organization.get_user_admin_orgs(request.user),
|
||||||
|
'CURRENT_ORG': get_current_org(),
|
||||||
|
'HAS_ORG_PERM': current_org.can_admin_by(request.user),
|
||||||
|
}
|
||||||
|
return context
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from .utils import get_org_from_request, set_current_org
|
||||||
|
|
||||||
|
|
||||||
|
class OrgMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
org = get_org_from_request(request)
|
||||||
|
request.current_org = org
|
||||||
|
set_current_org(org)
|
||||||
|
response = self.get_response(request)
|
||||||
|
return response
|
|
@ -0,0 +1,104 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
from threading import local
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
import warnings
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from django.http.response import HttpResponseForbidden
|
||||||
|
|
||||||
|
from common.utils import get_logger
|
||||||
|
from .utils import current_org, set_current_org, set_to_root_org
|
||||||
|
from .models import Organization
|
||||||
|
|
||||||
|
logger = get_logger(__file__)
|
||||||
|
tl = local()
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'OrgManager', 'OrgViewGenericMixin', 'OrgModelMixin', 'OrgModelForm',
|
||||||
|
'RootOrgViewMixin',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class OrgManager(models.Manager):
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = super(OrgManager, self).get_queryset()
|
||||||
|
kwargs = {}
|
||||||
|
if not hasattr(tl, 'times'):
|
||||||
|
tl.times = 0
|
||||||
|
# logger.debug("[{}]>>>>>>>>>> Get query set".format(tl.times))
|
||||||
|
if not current_org:
|
||||||
|
kwargs['id'] = None
|
||||||
|
elif current_org.is_real():
|
||||||
|
kwargs['org_id'] = current_org.id
|
||||||
|
elif current_org.is_default():
|
||||||
|
queryset = queryset.filter(Q(org_id="") | Q(org_id__isnull=True))
|
||||||
|
queryset = queryset.filter(**kwargs)
|
||||||
|
tl.times += 1
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def all(self):
|
||||||
|
if not current_org:
|
||||||
|
msg = 'You can `objects.set_current_org(org).all()` then run it'
|
||||||
|
warnings.warn(msg)
|
||||||
|
return self
|
||||||
|
else:
|
||||||
|
return super(OrgManager, self).all()
|
||||||
|
|
||||||
|
def set_current_org(self, org):
|
||||||
|
if isinstance(org, str):
|
||||||
|
org = Organization.objects.get(name=org)
|
||||||
|
set_current_org(org)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
class OrgModelMixin(models.Model):
|
||||||
|
org_id = models.CharField(max_length=36, null=True, blank=True)
|
||||||
|
objects = OrgManager()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if current_org and current_org.is_real():
|
||||||
|
self.org_id = current_org.id
|
||||||
|
return super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
class OrgViewGenericMixin:
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
print("Current org: {}".format(current_org))
|
||||||
|
if not current_org:
|
||||||
|
return redirect('orgs:switch-a-org')
|
||||||
|
|
||||||
|
if not current_org.can_admin_by(request.user):
|
||||||
|
print("{} cannot admin {}".format(request.user, current_org))
|
||||||
|
if request.user.is_org_admin:
|
||||||
|
print("Is org admin")
|
||||||
|
return redirect('orgs:switch-a-org')
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
else:
|
||||||
|
print(current_org.can_admin_by(request.user))
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class RootOrgViewMixin:
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
set_to_root_org()
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class OrgModelForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if 'initial' not in kwargs:
|
||||||
|
return
|
||||||
|
for name, field in self.fields.items():
|
||||||
|
if not hasattr(field, 'queryset'):
|
||||||
|
continue
|
||||||
|
model = field.queryset.model
|
||||||
|
field.queryset = model.objects.all()
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class Organization(models.Model):
|
||||||
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
|
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
|
||||||
|
users = models.ManyToManyField('users.User', related_name='orgs', blank=True)
|
||||||
|
admins = models.ManyToManyField('users.User', related_name='admin_orgs', blank=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'))
|
||||||
|
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
|
||||||
|
|
||||||
|
CACHE_PREFIX = 'JMS_ORG_{}'
|
||||||
|
ROOT_ID = 'ROOT'
|
||||||
|
DEFAULT_ID = 'DEFAULT'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def set_to_cache(self):
|
||||||
|
key = self.CACHE_PREFIX.format(self.id)
|
||||||
|
cache.set(key, self, 3600)
|
||||||
|
|
||||||
|
def expire_cache(self):
|
||||||
|
key = self.CACHE_PREFIX.format(self.id)
|
||||||
|
cache.set(key, self, 0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_instance_from_cache(cls, oid):
|
||||||
|
key = cls.CACHE_PREFIX.format(oid)
|
||||||
|
return cache.get(key, None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_instance(cls, oid, default=True):
|
||||||
|
cached = cls.get_instance_from_cache(oid)
|
||||||
|
if cached:
|
||||||
|
return cached
|
||||||
|
|
||||||
|
if oid == cls.DEFAULT_ID:
|
||||||
|
return cls.default()
|
||||||
|
elif oid == cls.ROOT_ID:
|
||||||
|
return cls.root()
|
||||||
|
|
||||||
|
try:
|
||||||
|
org = cls.objects.get(id=oid)
|
||||||
|
org.set_to_cache()
|
||||||
|
except cls.DoesNotExist:
|
||||||
|
org = cls.default() if default else None
|
||||||
|
return org
|
||||||
|
|
||||||
|
def get_org_users(self):
|
||||||
|
from users.models import User
|
||||||
|
if self.is_default():
|
||||||
|
users = User.objects.filter(orgs__isnull=True)
|
||||||
|
else:
|
||||||
|
users = self.users.all()
|
||||||
|
users = users.exclude(role=User.ROLE_APP)
|
||||||
|
return users
|
||||||
|
|
||||||
|
def get_org_admins(self):
|
||||||
|
if self.is_real():
|
||||||
|
return self.admins.all()
|
||||||
|
return []
|
||||||
|
|
||||||
|
def can_admin_by(self, user):
|
||||||
|
if user.is_superuser:
|
||||||
|
return True
|
||||||
|
if user in list(self.get_org_admins()):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_real(self):
|
||||||
|
return len(str(self.id)) == 36
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_user_admin_orgs(cls, user):
|
||||||
|
admin_orgs = []
|
||||||
|
if user.is_anonymous:
|
||||||
|
return admin_orgs
|
||||||
|
elif user.is_superuser:
|
||||||
|
admin_orgs = list(cls.objects.all())
|
||||||
|
admin_orgs.append(cls.default())
|
||||||
|
elif user.is_org_admin:
|
||||||
|
admin_orgs = user.admin_orgs.all()
|
||||||
|
return admin_orgs
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default(cls):
|
||||||
|
return cls(id=cls.DEFAULT_ID, name="Default")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def root(cls):
|
||||||
|
return cls(id=cls.ROOT_ID, name='Root')
|
||||||
|
|
||||||
|
def is_default(self):
|
||||||
|
if self.id is self.DEFAULT_ID:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
from rest_framework.serializers import ModelSerializer
|
||||||
|
from .models import Organization
|
||||||
|
|
||||||
|
|
||||||
|
class OrgSerializer(ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Organization
|
||||||
|
fields = '__all__'
|
||||||
|
read_only_fields = ['id', 'created_by', 'date_created']
|
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
|
@ -0,0 +1,2 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
|
@ -0,0 +1,16 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from rest_framework.routers import DefaultRouter
|
||||||
|
from .. import api
|
||||||
|
|
||||||
|
|
||||||
|
app_name = 'orgs'
|
||||||
|
router = DefaultRouter()
|
||||||
|
router.register(r'orgs', api.OrgViewSet, 'org')
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
]
|
||||||
|
|
||||||
|
urlpatterns += router.urls
|
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from .. import views
|
||||||
|
|
||||||
|
app_name = 'orgs'
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('<str:pk>/switch/', views.SwitchOrgView.as_view(), name='org-switch'),
|
||||||
|
path('switch-a-org/', views.SwitchToAOrgView.as_view(), name='switch-a-org')
|
||||||
|
]
|
|
@ -0,0 +1,47 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from common.utils import LocalProxy
|
||||||
|
from .models import Organization
|
||||||
|
|
||||||
|
try:
|
||||||
|
from threading import local
|
||||||
|
except ImportError:
|
||||||
|
from django.utils._threading_local import local
|
||||||
|
|
||||||
|
_thread_locals = local()
|
||||||
|
|
||||||
|
|
||||||
|
def get_org_from_request(request):
|
||||||
|
oid = request.session.get("oid")
|
||||||
|
if not oid:
|
||||||
|
oid = request.META.get("HTTP_X_JMS_ORG")
|
||||||
|
org = Organization.get_instance(oid)
|
||||||
|
return org
|
||||||
|
|
||||||
|
|
||||||
|
def set_current_org(org):
|
||||||
|
setattr(_thread_locals, 'current_org', org)
|
||||||
|
|
||||||
|
|
||||||
|
def set_to_default_org():
|
||||||
|
set_current_org(Organization.default())
|
||||||
|
|
||||||
|
|
||||||
|
def set_to_root_org():
|
||||||
|
set_current_org(Organization.root())
|
||||||
|
|
||||||
|
|
||||||
|
def _find(attr):
|
||||||
|
return getattr(_thread_locals, attr, None)
|
||||||
|
|
||||||
|
|
||||||
|
def get_current_org():
|
||||||
|
return _find('current_org')
|
||||||
|
|
||||||
|
|
||||||
|
current_org = LocalProxy(partial(_find, 'current_org'))
|
||||||
|
current_user = LocalProxy(partial(_find, 'current_user'))
|
||||||
|
current_request = LocalProxy(partial(_find, 'current_request'))
|
|
@ -0,0 +1,30 @@
|
||||||
|
from django.shortcuts import redirect, reverse
|
||||||
|
from django.http import HttpResponseForbidden
|
||||||
|
|
||||||
|
from django.views.generic import DetailView, View
|
||||||
|
|
||||||
|
from .models import Organization
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchOrgView(DetailView):
|
||||||
|
model = Organization
|
||||||
|
object = None
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
pk = kwargs.get('pk')
|
||||||
|
self.object = Organization.get_instance(pk)
|
||||||
|
request.session['oid'] = self.object.id.__str__()
|
||||||
|
return redirect('index')
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchToAOrgView(View):
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
admin_orgs = Organization.get_user_admin_orgs(request.user)
|
||||||
|
if not admin_orgs:
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
default_org = Organization.default()
|
||||||
|
if default_org in admin_orgs:
|
||||||
|
redirect_org = default_org
|
||||||
|
else:
|
||||||
|
redirect_org = admin_orgs[0]
|
||||||
|
return redirect(reverse('orgs:org-switch', kwargs={'pk': redirect_org.id}))
|
|
@ -7,7 +7,8 @@ from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveUpda
|
||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
from common.utils import set_or_append_attr_bulk, get_object_or_none
|
from common.utils import set_or_append_attr_bulk, get_object_or_none
|
||||||
from users.permissions import IsValidUser, IsSuperUser, IsSuperUserOrAppUser
|
from common.permissions import IsValidUser, IsOrgAdmin, IsOrgAdminOrAppUser
|
||||||
|
from orgs.mixins import RootOrgViewMixin
|
||||||
from .utils import AssetPermissionUtil
|
from .utils import AssetPermissionUtil
|
||||||
from .models import AssetPermission
|
from .models import AssetPermission
|
||||||
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, Node, \
|
from .hands import AssetGrantedSerializer, User, UserGroup, Asset, Node, \
|
||||||
|
@ -21,7 +22,7 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
queryset = AssetPermission.objects.all()
|
queryset = AssetPermission.objects.all()
|
||||||
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
serializer_class = serializers.AssetPermissionCreateUpdateSerializer
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
if self.action in ("list", 'retrieve'):
|
if self.action in ("list", 'retrieve'):
|
||||||
|
@ -54,11 +55,11 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
|
||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedAssetsApi(ListAPIView):
|
class UserGrantedAssetsApi(RootOrgViewMixin, ListAPIView):
|
||||||
"""
|
"""
|
||||||
用户授权的所有资产
|
用户授权的所有资产
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = AssetGrantedSerializer
|
serializer_class = AssetGrantedSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -83,8 +84,8 @@ class UserGrantedAssetsApi(ListAPIView):
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesApi(ListAPIView):
|
class UserGrantedNodesApi(RootOrgViewMixin, ListAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = NodeSerializer
|
serializer_class = NodeSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -103,8 +104,8 @@ class UserGrantedNodesApi(ListAPIView):
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodesWithAssetsApi(ListAPIView):
|
class UserGrantedNodesWithAssetsApi(RootOrgViewMixin, ListAPIView):
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = NodeGrantedSerializer
|
serializer_class = NodeGrantedSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -132,8 +133,8 @@ class UserGrantedNodesWithAssetsApi(ListAPIView):
|
||||||
return super().get_permissions()
|
return super().get_permissions()
|
||||||
|
|
||||||
|
|
||||||
class UserGrantedNodeAssetsApi(ListAPIView):
|
class UserGrantedNodeAssetsApi(RootOrgViewMixin, ListAPIView):
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = AssetGrantedSerializer
|
serializer_class = AssetGrantedSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -159,7 +160,7 @@ class UserGrantedNodeAssetsApi(ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class UserGroupGrantedAssetsApi(ListAPIView):
|
class UserGroupGrantedAssetsApi(ListAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = AssetGrantedSerializer
|
serializer_class = AssetGrantedSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -179,7 +180,7 @@ class UserGroupGrantedAssetsApi(ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class UserGroupGrantedNodesApi(ListAPIView):
|
class UserGroupGrantedNodesApi(ListAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = NodeSerializer
|
serializer_class = NodeSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -195,7 +196,7 @@ class UserGroupGrantedNodesApi(ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
|
class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = NodeGrantedSerializer
|
serializer_class = NodeGrantedSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -218,7 +219,7 @@ class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
serializer_class = AssetGrantedSerializer
|
serializer_class = AssetGrantedSerializer
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
@ -235,8 +236,8 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
|
|
||||||
class ValidateUserAssetPermissionView(APIView):
|
class ValidateUserAssetPermissionView(RootOrgViewMixin, APIView):
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get(request):
|
def get(request):
|
||||||
|
@ -260,7 +261,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
|
||||||
"""
|
"""
|
||||||
将用户从授权中移除,Detail页面会调用
|
将用户从授权中移除,Detail页面会调用
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
queryset = AssetPermission.objects.all()
|
||||||
|
|
||||||
|
@ -277,7 +278,7 @@ class AssetPermissionRemoveUserApi(RetrieveUpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
|
class AssetPermissionAddUserApi(RetrieveUpdateAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
serializer_class = serializers.AssetPermissionUpdateUserSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
queryset = AssetPermission.objects.all()
|
||||||
|
|
||||||
|
@ -297,7 +298,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
|
||||||
"""
|
"""
|
||||||
将用户从授权中移除,Detail页面会调用
|
将用户从授权中移除,Detail页面会调用
|
||||||
"""
|
"""
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
queryset = AssetPermission.objects.all()
|
||||||
|
|
||||||
|
@ -314,7 +315,7 @@ class AssetPermissionRemoveAssetApi(RetrieveUpdateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
|
class AssetPermissionAddAssetApi(RetrieveUpdateAPIView):
|
||||||
permission_classes = (IsSuperUser,)
|
permission_classes = (IsOrgAdmin,)
|
||||||
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
serializer_class = serializers.AssetPermissionUpdateAssetSerializer
|
||||||
queryset = AssetPermission.objects.all()
|
queryset = AssetPermission.objects.all()
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,13 @@ from __future__ import absolute_import, unicode_literals
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelForm
|
||||||
|
from orgs.utils import current_org
|
||||||
from .hands import User
|
from .hands import User
|
||||||
from .models import AssetPermission
|
from .models import AssetPermission
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionForm(forms.ModelForm):
|
class AssetPermissionForm(OrgModelForm):
|
||||||
users = forms.ModelMultipleChoiceField(
|
users = forms.ModelMultipleChoiceField(
|
||||||
queryset=User.objects.exclude(role=User.ROLE_APP),
|
queryset=User.objects.exclude(role=User.ROLE_APP),
|
||||||
label=_("User"),
|
label=_("User"),
|
||||||
|
@ -21,10 +23,18 @@ class AssetPermissionForm(forms.ModelForm):
|
||||||
required=False,
|
required=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if 'initial' not in kwargs:
|
||||||
|
return
|
||||||
|
users_field = self.fields.get('users')
|
||||||
|
if hasattr(users_field, 'queryset'):
|
||||||
|
users_field.queryset = current_org.get_org_users()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = AssetPermission
|
model = AssetPermission
|
||||||
exclude = (
|
exclude = (
|
||||||
'id', 'date_created', 'created_by'
|
'id', 'date_created', 'created_by', 'org_id'
|
||||||
)
|
)
|
||||||
widgets = {
|
widgets = {
|
||||||
'users': forms.SelectMultiple(
|
'users': forms.SelectMultiple(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
#
|
#
|
||||||
|
|
||||||
from users.utils import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
from users.models import User, UserGroup
|
from users.models import User, UserGroup
|
||||||
from assets.models import Asset, SystemUser, Node
|
from assets.models import Asset, SystemUser, Node
|
||||||
from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer, NodeSerializer
|
from assets.serializers import AssetGrantedSerializer, NodeGrantedSerializer, NodeSerializer
|
||||||
|
|
|
@ -6,6 +6,8 @@ from django.utils import timezone
|
||||||
|
|
||||||
from common.utils import date_expired_default, set_or_append_attr_bulk
|
from common.utils import date_expired_default, set_or_append_attr_bulk
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelMixin, OrgManager
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionQuerySet(models.QuerySet):
|
class AssetPermissionQuerySet(models.QuerySet):
|
||||||
def active(self):
|
def active(self):
|
||||||
|
@ -16,17 +18,14 @@ class AssetPermissionQuerySet(models.QuerySet):
|
||||||
.filter(date_expired__gt=timezone.now())
|
.filter(date_expired__gt=timezone.now())
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionManager(models.Manager):
|
class AssetPermissionManager(OrgManager):
|
||||||
def get_queryset(self):
|
|
||||||
return AssetPermissionQuerySet(self.model, using=self._db)
|
|
||||||
|
|
||||||
def valid(self):
|
def valid(self):
|
||||||
return self.get_queryset().valid()
|
return self.get_queryset().valid()
|
||||||
|
|
||||||
|
|
||||||
class AssetPermission(models.Model):
|
class AssetPermission(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
name = models.CharField(max_length=128, unique=True, verbose_name=_('Name'))
|
name = models.CharField(max_length=128, verbose_name=_('Name'))
|
||||||
users = models.ManyToManyField('users.User', related_name='asset_permissions', blank=True, verbose_name=_("User"))
|
users = models.ManyToManyField('users.User', related_name='asset_permissions', blank=True, verbose_name=_("User"))
|
||||||
user_groups = models.ManyToManyField('users.UserGroup', related_name='asset_permissions', blank=True, verbose_name=_("User group"))
|
user_groups = models.ManyToManyField('users.UserGroup', related_name='asset_permissions', blank=True, verbose_name=_("User group"))
|
||||||
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
|
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
|
||||||
|
@ -39,7 +38,10 @@ class AssetPermission(models.Model):
|
||||||
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
|
date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created'))
|
||||||
comment = models.TextField(verbose_name=_('Comment'), blank=True)
|
comment = models.TextField(verbose_name=_('Comment'), blank=True)
|
||||||
|
|
||||||
objects = AssetPermissionManager()
|
objects = AssetPermissionManager.from_queryset(AssetPermissionQuerySet)()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = [('org_id', 'name')]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -71,7 +73,7 @@ class AssetPermission(models.Model):
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
|
|
||||||
class NodePermission(models.Model):
|
class NodePermission(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
node = models.ForeignKey('assets.Node', on_delete=models.CASCADE, verbose_name=_("Node"))
|
node = models.ForeignKey('assets.Node', on_delete=models.CASCADE, verbose_name=_("Node"))
|
||||||
user_group = models.ForeignKey('users.UserGroup', on_delete=models.CASCADE, verbose_name=_("User group"))
|
user_group = models.ForeignKey('users.UserGroup', on_delete=models.CASCADE, verbose_name=_("User group"))
|
||||||
|
|
|
@ -80,12 +80,12 @@ function onSelected(event, treeNode) {
|
||||||
var url = table.ajax.url();
|
var url = table.ajax.url();
|
||||||
if (treeNode.is_node) {
|
if (treeNode.is_node) {
|
||||||
url = setUrlParam(url, 'asset', "");
|
url = setUrlParam(url, 'asset', "");
|
||||||
url = setUrlParam(url, 'node', treeNode.id)
|
url = setUrlParam(url, 'node', treeNode.node_id)
|
||||||
} else {
|
} else {
|
||||||
url = setUrlParam(url, 'node', "");
|
url = setUrlParam(url, 'node', "");
|
||||||
url = setUrlParam(url, 'asset', treeNode.id)
|
url = setUrlParam(url, 'asset', treeNode.node_id)
|
||||||
}
|
}
|
||||||
setCookie('node_selected', treeNode.id);
|
setCookie('node_selected', treeNode.node_id);
|
||||||
table.ajax.url(url);
|
table.ajax.url(url);
|
||||||
table.ajax.reload();
|
table.ajax.reload();
|
||||||
}
|
}
|
||||||
|
@ -111,10 +111,19 @@ function selectQueryNode() {
|
||||||
|
|
||||||
function filter(treeId, parentNode, childNodes) {
|
function filter(treeId, parentNode, childNodes) {
|
||||||
$.each(childNodes, function (index, value) {
|
$.each(childNodes, function (index, value) {
|
||||||
value["pId"] = value["parent"];
|
value["node_id"] = value["id"];
|
||||||
value["name"] = value["value"];
|
value["id"] = value["tree_id"];
|
||||||
value["isParent"] = value["is_node"];
|
if (value["tree_id"] !== value["tree_parent"]) {
|
||||||
|
value["pId"] = value["tree_parent"];
|
||||||
|
} else {
|
||||||
|
value["isParent"] = true;
|
||||||
|
}
|
||||||
|
value['name'] = value['value'];
|
||||||
value["iconSkin"] = value["is_node"] ? null : 'file';
|
value["iconSkin"] = value["is_node"] ? null : 'file';
|
||||||
|
|
||||||
|
{#value["pId"] = value["parent"];#}
|
||||||
|
{#value["name"] = value["value"];#}
|
||||||
|
value["isParent"] = value["is_node"];
|
||||||
});
|
});
|
||||||
return childNodes;
|
return childNodes;
|
||||||
}
|
}
|
||||||
|
@ -227,7 +236,7 @@ function initTree() {
|
||||||
async: {
|
async: {
|
||||||
enable: true,
|
enable: true,
|
||||||
url: "{% url 'api-assets:node-children-2' %}?assets=1&all=",
|
url: "{% url 'api-assets:node-children-2' %}?assets=1&all=",
|
||||||
autoParam:["id", "name=n", "level=lv"],
|
autoParam:["node_id=id", "name=n", "level=lv"],
|
||||||
dataFilter: filter,
|
dataFilter: filter,
|
||||||
type: 'get'
|
type: 'get'
|
||||||
},
|
},
|
||||||
|
@ -238,19 +247,22 @@ function initTree() {
|
||||||
};
|
};
|
||||||
|
|
||||||
var zNodes = [];
|
var zNodes = [];
|
||||||
$.get("{% url 'api-assets:node-children-2' %}?assets=1&all=", function(data, status){
|
$.get("{% url 'api-assets:node-children-2' %}?assets=1", function(data, status){
|
||||||
$.each(data, function (index, value) {
|
$.each(data, function (index, value) {
|
||||||
value["pId"] = value["parent"];
|
value["node_id"] = value["id"];
|
||||||
value["name"] = value["value"];
|
value["id"] = value["tree_id"];
|
||||||
value["open"] = value["key"] === "0";
|
if (value["tree_id"] !== value["tree_parent"]) {
|
||||||
|
value["pId"] = value["tree_parent"];
|
||||||
|
}
|
||||||
value["isParent"] = value["is_node"];
|
value["isParent"] = value["is_node"];
|
||||||
|
value['name'] = value['value'];
|
||||||
value["iconSkin"] = value["is_node"] ? null : 'file';
|
value["iconSkin"] = value["is_node"] ? null : 'file';
|
||||||
});
|
});
|
||||||
zNodes = data;
|
zNodes = data;
|
||||||
{#$.fn.zTree.init($("#assetTree"), setting);#}
|
|
||||||
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
$.fn.zTree.init($("#assetTree"), setting, zNodes);
|
||||||
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
zTree = $.fn.zTree.getZTreeObj("assetTree");
|
||||||
{#selectQueryNode();#}
|
var root = zTree.getNodes()[0];
|
||||||
|
zTree.expandNode(root);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -288,9 +300,9 @@ $(document).ready(function(){
|
||||||
var _assets = [];
|
var _assets = [];
|
||||||
$.each(nodes, function (id, node) {
|
$.each(nodes, function (id, node) {
|
||||||
if (node.is_node) {
|
if (node.is_node) {
|
||||||
_nodes.push(node.id)
|
_nodes.push(node.node_id)
|
||||||
} else {
|
} else {
|
||||||
_assets.push(node.id)
|
_assets.push(node.node_id)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
url += "?assets=" + _assets.join(",") + "&nodes=" + _nodes.join(",");
|
url += "?assets=" + _assets.join(",") + "&nodes=" + _nodes.join(",");
|
||||||
|
|
|
@ -1,63 +1,62 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
from .. import api
|
from .. import api
|
||||||
|
|
||||||
app_name = 'perms'
|
app_name = 'perms'
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
|
router.register('asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# 查询某个用户授权的资产和资产组
|
# 查询某个用户授权的资产和资产组
|
||||||
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
|
path('user/<uuid:pk>/assets/',
|
||||||
api.UserGrantedAssetsApi.as_view(), name='user-assets'),
|
api.UserGrantedAssetsApi.as_view(), name='user-assets'),
|
||||||
url(r'^v1/user/assets/$', api.UserGrantedAssetsApi.as_view(),
|
path('user/assets/', api.UserGrantedAssetsApi.as_view(),
|
||||||
name='my-assets'),
|
name='my-assets'),
|
||||||
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$',
|
path('user/<uuid:pk>/nodes/',
|
||||||
api.UserGrantedNodesApi.as_view(), name='user-nodes'),
|
api.UserGrantedNodesApi.as_view(), name='user-nodes'),
|
||||||
url(r'^v1/user/nodes/$', api.UserGrantedNodesApi.as_view(),
|
path('user/nodes/', api.UserGrantedNodesApi.as_view(),
|
||||||
name='my-nodes'),
|
name='my-nodes'),
|
||||||
url(
|
path('user/<uuid:pk>/nodes/<uuid:node_id>/assets/',
|
||||||
r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$',
|
api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'),
|
||||||
api.UserGrantedNodeAssetsApi.as_view(), name='user-node-assets'),
|
path('user/nodes/<uuid:node_id>/assets/',
|
||||||
url(r'^v1/user/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$',
|
api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'),
|
||||||
api.UserGrantedNodeAssetsApi.as_view(), name='my-node-assets'),
|
path('user/<uuid:pk>/nodes-assets/',
|
||||||
url(r'^v1/user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$',
|
api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
|
||||||
api.UserGrantedNodesWithAssetsApi.as_view(), name='user-nodes-assets'),
|
path('user/nodes-assets/', api.UserGrantedNodesWithAssetsApi.as_view(),
|
||||||
url(r'^v1/user/nodes-assets/$', api.UserGrantedNodesWithAssetsApi.as_view(),
|
name='my-nodes-assets'),
|
||||||
name='my-nodes-assets'),
|
|
||||||
|
|
||||||
# 查询某个用户组授权的资产和资产组
|
# 查询某个用户组授权的资产和资产组
|
||||||
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
|
path('user-group/<uuid:pk>/assets/',
|
||||||
api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
|
api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
|
||||||
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$',
|
path('user-group/<uuid:pk>/nodes/',
|
||||||
api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
|
api.UserGroupGrantedNodesApi.as_view(), name='user-group-nodes'),
|
||||||
url(r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes-assets/$',
|
path('user-group/<uuid:pk>/nodes-assets/',
|
||||||
api.UserGroupGrantedNodesWithAssetsApi.as_view(),
|
api.UserGroupGrantedNodesWithAssetsApi.as_view(),
|
||||||
name='user-group-nodes-assets'),
|
name='user-group-nodes-assets'),
|
||||||
url(
|
path('user-group/<uuid:pk>/nodes/<uuid:node_id>/assets/',
|
||||||
r'^v1/user-group/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/(?P<node_id>[0-9a-zA-Z\-]{36})/assets/$',
|
api.UserGroupGrantedNodeAssetsApi.as_view(),
|
||||||
api.UserGroupGrantedNodeAssetsApi.as_view(),
|
name='user-group-node-assets'),
|
||||||
name='user-group-node-assets'),
|
|
||||||
|
|
||||||
# 用户和资产授权变更
|
# 用户和资产授权变更
|
||||||
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/user/remove/$',
|
path('asset-permissions/<uuid:pk>/user/remove/',
|
||||||
api.AssetPermissionRemoveUserApi.as_view(),
|
api.AssetPermissionRemoveUserApi.as_view(),
|
||||||
name='asset-permission-remove-user'),
|
name='asset-permission-remove-user'),
|
||||||
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/user/add/$',
|
path('asset-permissions/<uuid:pk>/user/add/',
|
||||||
api.AssetPermissionAddUserApi.as_view(),
|
api.AssetPermissionAddUserApi.as_view(),
|
||||||
name='asset-permission-add-user'),
|
name='asset-permission-add-user'),
|
||||||
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/asset/remove/$',
|
path('asset-permissions/<uuid:pk>/asset/remove/',
|
||||||
api.AssetPermissionRemoveAssetApi.as_view(),
|
api.AssetPermissionRemoveAssetApi.as_view(),
|
||||||
name='asset-permission-remove-asset'),
|
name='asset-permission-remove-asset'),
|
||||||
url(r'^v1/asset-permissions/(?P<pk>[0-9a-zA-Z\-]{36})/asset/add/$',
|
path('asset-permissions/<uuid:pk>/asset/add/',
|
||||||
api.AssetPermissionAddAssetApi.as_view(),
|
api.AssetPermissionAddAssetApi.as_view(),
|
||||||
name='asset-permission-add-asset'),
|
name='asset-permission-add-asset'),
|
||||||
|
|
||||||
# 验证用户是否有某个资产和系统用户的权限
|
# 验证用户是否有某个资产和系统用户的权限
|
||||||
url(r'v1/asset-permission/user/validate/$', api.ValidateUserAssetPermissionView.as_view(), name='validate-user-asset-permission'),
|
path('asset-permission/user/validate/', api.ValidateUserAssetPermissionView.as_view(),
|
||||||
|
name='validate-user-asset-permission'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
from django.urls import path
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
app_name = 'perms'
|
app_name = 'perms'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^asset-permission/$', views.AssetPermissionListView.as_view(), name='asset-permission-list'),
|
path('asset-permission/', views.AssetPermissionListView.as_view(), name='asset-permission-list'),
|
||||||
url(r'^asset-permission/create/$', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'),
|
path('asset-permission/create/', views.AssetPermissionCreateView.as_view(), name='asset-permission-create'),
|
||||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'),
|
path('asset-permission/<uuid:pk>/update/', views.AssetPermissionUpdateView.as_view(), name='asset-permission-update'),
|
||||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'),
|
path('asset-permission/<uuid:pk>/', views.AssetPermissionDetailView.as_view(),name='asset-permission-detail'),
|
||||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/delete/$', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'),
|
path('asset-permission/<uuid:pk>/delete/', views.AssetPermissionDeleteView.as_view(), name='asset-permission-delete'),
|
||||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/user/$', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
|
path('asset-permission/<uuid:pk>/user/', views.AssetPermissionUserView.as_view(), name='asset-permission-user-list'),
|
||||||
url(r'^asset-permission/(?P<pk>[0-9a-zA-Z\-]{36})/asset/$', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
|
path('asset-permission/<uuid:pk>/asset/', views.AssetPermissionAssetView.as_view(), name='asset-permission-asset-list'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -13,7 +13,7 @@ logger = get_logger(__file__)
|
||||||
|
|
||||||
class Tree:
|
class Tree:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.__all_nodes = list(Node.objects.all().prefetch_related('assets'))
|
self.__all_nodes = Node.objects.all().prefetch_related('assets')
|
||||||
self.__node_asset_map = defaultdict(set)
|
self.__node_asset_map = defaultdict(set)
|
||||||
self.nodes = defaultdict(dict)
|
self.nodes = defaultdict(dict)
|
||||||
self.root = Node.root()
|
self.root = Node.root()
|
||||||
|
@ -21,7 +21,7 @@ class Tree:
|
||||||
|
|
||||||
def init_node_asset_map(self):
|
def init_node_asset_map(self):
|
||||||
for node in self.__all_nodes:
|
for node in self.__all_nodes:
|
||||||
assets = node.get_assets().values_list('id', flat=True)
|
assets = [a.id for a in node.assets.all()]
|
||||||
for asset in assets:
|
for asset in assets:
|
||||||
self.__node_asset_map[str(asset)].add(node)
|
self.__node_asset_map[str(asset)].add(node)
|
||||||
|
|
||||||
|
|
|
@ -3,22 +3,20 @@
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
from django.views.generic import ListView, CreateView, UpdateView, DetailView
|
from django.views.generic import ListView, CreateView, UpdateView, DetailView, TemplateView
|
||||||
from django.views.generic.edit import DeleteView, SingleObjectMixin
|
from django.views.generic.edit import DeleteView, SingleObjectMixin
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from common.mixins import AdminUserRequiredMixin
|
from common.permissions import AdminUserRequiredMixin
|
||||||
|
from orgs.utils import current_org
|
||||||
from .hands import Node, Asset, SystemUser, User, UserGroup
|
from .hands import Node, Asset, SystemUser, User, UserGroup
|
||||||
from .models import AssetPermission
|
from .models import AssetPermission
|
||||||
from .forms import AssetPermissionForm
|
from .forms import AssetPermissionForm
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionListView(AdminUserRequiredMixin, ListView):
|
class AssetPermissionListView(AdminUserRequiredMixin, TemplateView):
|
||||||
model = AssetPermission
|
|
||||||
template_name = 'perms/asset_permission_list.html'
|
template_name = 'perms/asset_permission_list.html'
|
||||||
paginate_by = settings.DISPLAY_PER_PAGE
|
|
||||||
user = user_group = asset = node = system_user = q = ""
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = {
|
||||||
|
@ -87,7 +85,6 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
|
||||||
'system_users_remain': SystemUser.objects.exclude(
|
'system_users_remain': SystemUser.objects.exclude(
|
||||||
granted_by_permissions=self.object
|
granted_by_permissions=self.object
|
||||||
),
|
),
|
||||||
|
|
||||||
}
|
}
|
||||||
kwargs.update(context)
|
kwargs.update(context)
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
@ -116,11 +113,13 @@ class AssetPermissionUserView(AdminUserRequiredMixin,
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'app': _('Perms'),
|
'app': _('Perms'),
|
||||||
'action': _('Asset permission user list'),
|
'action': _('Asset permission user list'),
|
||||||
'users_remain': User.objects.exclude(asset_permissions=self.object)
|
'users_remain': current_org.get_org_users().exclude(
|
||||||
.exclude(role=User.ROLE_APP),
|
asset_permissions=self.object
|
||||||
|
),
|
||||||
'user_groups_remain': UserGroup.objects.exclude(
|
'user_groups_remain': UserGroup.objects.exclude(
|
||||||
asset_permissions=self.object
|
asset_permissions=self.object
|
||||||
)
|
)
|
||||||
|
@ -138,7 +137,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin,
|
||||||
object = None
|
object = None
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object = self.get_object(queryset=AssetPermission.objects.all())
|
self.object = self.get_object(queryset = AssetPermission.objects.all())
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
|
|
|
@ -335,13 +335,13 @@ div.dataTables_wrapper div.dataTables_filter {
|
||||||
|
|
||||||
.nav-header, body.mini-navbar .nav-header {
|
.nav-header, body.mini-navbar .nav-header {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #202c37;
|
/*background: #202c37;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-element div:first-child {
|
.profile-element div:first-child {
|
||||||
line-height: 60px;
|
line-height: 60px;
|
||||||
/*width: 70px;*/
|
/*width: 70px;*/
|
||||||
float: left;
|
/*float: left;*/
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 5.7 KiB |
|
@ -179,7 +179,7 @@ function APIUpdateAttr(props) {
|
||||||
toastr.error(fail_message);
|
toastr.error(fail_message);
|
||||||
}
|
}
|
||||||
if (typeof props.error === 'function') {
|
if (typeof props.error === 'function') {
|
||||||
return props.error(jqXHR.responseText);
|
return props.error(jqXHR.responseText, jqXHR.status);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// return true;
|
// return true;
|
||||||
|
@ -224,6 +224,47 @@ function objectDelete(obj, name, url, redirectTo) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function orgDelete(obj, name, url, redirectTo){
|
||||||
|
function doDelete() {
|
||||||
|
var body = {};
|
||||||
|
var success = function() {
|
||||||
|
if (!redirectTo) {
|
||||||
|
$(obj).parent().parent().remove();
|
||||||
|
} else {
|
||||||
|
window.location.href=redirectTo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var fail = function(responseText, status) {
|
||||||
|
if (status === 400){
|
||||||
|
swal("错误", "[ " + name + " ] 组织中包含未删除信息,请删除后重试", "error");
|
||||||
|
}
|
||||||
|
else if (status === 405){
|
||||||
|
swal("错误", "请勿在组织 [ "+ name + " ] 下执行此操作,切换到其他组织后重试", "error");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
APIUpdateAttr({
|
||||||
|
url: url,
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
method: 'DELETE',
|
||||||
|
success_message: "删除成功",
|
||||||
|
success: success,
|
||||||
|
error: fail
|
||||||
|
});
|
||||||
|
}
|
||||||
|
swal({
|
||||||
|
title: "请确保组织内的以下信息已删除",
|
||||||
|
text: "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权规则",
|
||||||
|
type: "warning",
|
||||||
|
showCancelButton: true,
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
confirmButtonColor: "#ed5565",
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
closeOnConfirm: true
|
||||||
|
}, function () {
|
||||||
|
doDelete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$.fn.serializeObject = function()
|
$.fn.serializeObject = function()
|
||||||
{
|
{
|
||||||
var o = {};
|
var o = {};
|
||||||
|
@ -250,6 +291,28 @@ function makeLabel(data) {
|
||||||
var jumpserver = {};
|
var jumpserver = {};
|
||||||
jumpserver.checked = false;
|
jumpserver.checked = false;
|
||||||
jumpserver.selected = {};
|
jumpserver.selected = {};
|
||||||
|
jumpserver.language = {
|
||||||
|
processing: "加载中",
|
||||||
|
search: "搜索",
|
||||||
|
select: {
|
||||||
|
rows: {
|
||||||
|
_: "选中 %d 项",
|
||||||
|
0: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lengthMenu: "每页 _MENU_",
|
||||||
|
info: "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项",
|
||||||
|
infoFiltered: "",
|
||||||
|
infoEmpty: "",
|
||||||
|
zeroRecords: "没有匹配项",
|
||||||
|
emptyTable: "没有记录",
|
||||||
|
paginate: {
|
||||||
|
first: "«",
|
||||||
|
previous: "‹",
|
||||||
|
next: "›",
|
||||||
|
last: "»"
|
||||||
|
}
|
||||||
|
};
|
||||||
jumpserver.initDataTable = function (options) {
|
jumpserver.initDataTable = function (options) {
|
||||||
// options = {
|
// options = {
|
||||||
// ele *: $('#dataTable_id'),
|
// ele *: $('#dataTable_id'),
|
||||||
|
@ -293,21 +356,7 @@ jumpserver.initDataTable = function (options) {
|
||||||
},
|
},
|
||||||
columns: options.columns || [],
|
columns: options.columns || [],
|
||||||
select: options.select || select,
|
select: options.select || select,
|
||||||
language: {
|
language: jumpserver.language,
|
||||||
search: "搜索",
|
|
||||||
lengthMenu: "每页 _MENU_",
|
|
||||||
info: "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项",
|
|
||||||
infoFiltered: "",
|
|
||||||
infoEmpty: "",
|
|
||||||
zeroRecords: "没有匹配项",
|
|
||||||
emptyTable: "没有记录",
|
|
||||||
paginate: {
|
|
||||||
first: "«",
|
|
||||||
previous: "‹",
|
|
||||||
next: "›",
|
|
||||||
last: "»"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lengthMenu: [[10, 15, 25, 50, -1], [10, 15, 25, 50, "All"]]
|
lengthMenu: [[10, 15, 25, 50, -1], [10, 15, 25, 50, "All"]]
|
||||||
});
|
});
|
||||||
table.on('select', function(e, dt, type, indexes) {
|
table.on('select', function(e, dt, type, indexes) {
|
||||||
|
@ -343,6 +392,16 @@ jumpserver.initDataTable = function (options) {
|
||||||
return table;
|
return table;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jumpserver.initStaticTable = function (selector) {
|
||||||
|
$(selector).DataTable({
|
||||||
|
"searching": false,
|
||||||
|
"bInfo": false,
|
||||||
|
"paging": false,
|
||||||
|
"order": [],
|
||||||
|
"language": jumpserver.language
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
jumpserver.initServerSideDataTable = function (options) {
|
jumpserver.initServerSideDataTable = function (options) {
|
||||||
// options = {
|
// options = {
|
||||||
// ele *: $('#dataTable_id'),
|
// ele *: $('#dataTable_id'),
|
||||||
|
@ -375,9 +434,8 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
};
|
};
|
||||||
var table = ele.DataTable({
|
var table = ele.DataTable({
|
||||||
pageLength: options.pageLength || 15,
|
pageLength: options.pageLength || 15,
|
||||||
dom: options.dom || '<"#uc.pull-left">flt<"row m-t"<"col-md-8"<"#op.col-md-6"><"col-md-6 text-center"i>><"col-md-4"p>>',
|
dom: options.dom || '<"#uc.pull-left">fltr<"row m-t"<"col-md-8"<"#op.col-md-6"><"col-md-6 text-center"i>><"col-md-4"p>>',
|
||||||
order: options.order || [],
|
order: options.order || [],
|
||||||
// select: options.select || 'multi',
|
|
||||||
buttons: [],
|
buttons: [],
|
||||||
columnDefs: columnDefs,
|
columnDefs: columnDefs,
|
||||||
serverSide: true,
|
serverSide: true,
|
||||||
|
@ -432,21 +490,7 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
},
|
},
|
||||||
columns: options.columns || [],
|
columns: options.columns || [],
|
||||||
select: options.select || select,
|
select: options.select || select,
|
||||||
language: {
|
language: jumpserver.language,
|
||||||
search: "搜索",
|
|
||||||
lengthMenu: "每页 _MENU_",
|
|
||||||
info: "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项",
|
|
||||||
infoFiltered: "",
|
|
||||||
infoEmpty: "",
|
|
||||||
zeroRecords: "没有匹配项",
|
|
||||||
emptyTable: "没有记录",
|
|
||||||
paginate: {
|
|
||||||
first: "«",
|
|
||||||
previous: "‹",
|
|
||||||
next: "›",
|
|
||||||
last: "»"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]]
|
lengthMenu: [[10, 15, 25, 50], [10, 15, 25, 50]]
|
||||||
});
|
});
|
||||||
table.selected = [];
|
table.selected = [];
|
||||||
|
@ -477,8 +521,7 @@ jumpserver.initServerSideDataTable = function (options) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}).
|
}).on('draw', function(){
|
||||||
on('draw', function(){
|
|
||||||
$('#op').html(options.op_html || '');
|
$('#op').html(options.op_html || '');
|
||||||
$('#uc').html(options.uc_html || '');
|
$('#uc').html(options.uc_html || '');
|
||||||
var table_data = [];
|
var table_data = [];
|
||||||
|
@ -683,7 +726,7 @@ function popoverPasswordRules(password_check_rules, $el) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化弹窗popover
|
// 初始化弹窗popover
|
||||||
function initPopover($container, $progress, $idPassword, $el, password_check_rules){
|
function initPopover($container, $progress, $idPassword, $el, password_check_rules, i18n_fallback){
|
||||||
options = {};
|
options = {};
|
||||||
// User Interface
|
// User Interface
|
||||||
options.ui = {
|
options.ui = {
|
||||||
|
@ -695,6 +738,14 @@ function initPopover($container, $progress, $idPassword, $el, password_check_rul
|
||||||
showProgressbar: true,
|
showProgressbar: true,
|
||||||
showVerdictsInsideProgressBar: true
|
showVerdictsInsideProgressBar: true
|
||||||
};
|
};
|
||||||
|
options.i18n = {
|
||||||
|
fallback: i18n_fallback,
|
||||||
|
t: function (key) {
|
||||||
|
var result = '';
|
||||||
|
result = options.i18n.fallback[key];
|
||||||
|
return result === key ? '' : result;
|
||||||
|
}
|
||||||
|
};
|
||||||
$idPassword.pwstrength(options);
|
$idPassword.pwstrength(options);
|
||||||
popoverPasswordRules(password_check_rules, $el);
|
popoverPasswordRules(password_check_rules, $el);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="footer fixed">
|
<div class="footer fixed">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
Version <strong>1.3.3-{% include '_build.html' %}</strong> GPLv2.
|
Version <strong>1.4.0-{% include '_build.html' %}</strong> GPLv2.
|
||||||
<!--<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">-->
|
<!--<img style="display: none" src="http://www.jumpserver.org/img/evaluate_avatar1.jpg">-->
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -35,7 +35,7 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu animated fadeInRight m-t-xs profile-dropdown">
|
<ul class="dropdown-menu animated fadeInRight m-t-xs profile-dropdown">
|
||||||
<li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
|
<li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_org_admin %}
|
||||||
{% if request.COOKIES.IN_ADMIN_PAGE == 'No' %}
|
{% if request.COOKIES.IN_ADMIN_PAGE == 'No' %}
|
||||||
<li><a id="switch_admin"><i class="fa fa-exchange"></i><span> {% trans 'Admin page' %}</span></a></li>
|
<li><a id="switch_admin"><i class="fa fa-exchange"></i><span> {% trans 'Admin page' %}</span></a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="sidebar-collapse">
|
<div class="sidebar-collapse">
|
||||||
<ul class="nav" id="side-menu">
|
<ul class="nav" id="side-menu">
|
||||||
{% include '_user_profile.html' %}
|
{% include '_user_profile.html' %}
|
||||||
{% if request.user.is_superuser and request.COOKIES.IN_ADMIN_PAGE != "No" %}
|
{% if request.user.is_org_admin and request.COOKIES.IN_ADMIN_PAGE != "No" %}
|
||||||
{% include '_nav.html' %}
|
{% include '_nav.html' %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include '_nav_user.html' %}
|
{% include '_nav_user.html' %}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<li id="index">
|
<li id="index">
|
||||||
<a href="{% url 'index' %}">
|
<a href="{% url 'index' %}">
|
||||||
<i class="fa fa-dashboard" style="width: 14px"></i> <span class="nav-label">{% trans 'Dashboard' %}</span><span
|
<i class="fa fa-dashboard" style="width: 14px"></i> <span class="nav-label">{% trans 'Dashboard' %}</span>
|
||||||
class="label label-info pull-right"></span>
|
<span class="label label-info pull-right"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li id="users">
|
<li id="users">
|
||||||
|
@ -48,9 +48,12 @@
|
||||||
<span class="nav-label">{% trans 'Web terminal' %}</span>
|
<span class="nav-label">{% trans 'Web terminal' %}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if request.user.is_superuser %}
|
||||||
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
|
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
{% if request.user.is_superuser %}
|
||||||
<li id="ops">
|
<li id="ops">
|
||||||
<a>
|
<a>
|
||||||
<i class="fa fa-coffee" style="width: 14px"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
|
<i class="fa fa-coffee" style="width: 14px"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
|
||||||
|
@ -59,6 +62,7 @@
|
||||||
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li>
|
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li id="audits">
|
<li id="audits">
|
||||||
<a>
|
<a>
|
||||||
<i class="fa fa-history" style="width: 14px"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span>
|
<i class="fa fa-history" style="width: 14px"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span>
|
||||||
|
@ -76,8 +80,29 @@
|
||||||
{# <li id="download"><a href="">{% trans 'File download' %}</a></li>#}
|
{# <li id="download"><a href="">{% trans 'File download' %}</a></li>#}
|
||||||
{# </ul>#}
|
{# </ul>#}
|
||||||
{#</li>#}
|
{#</li>#}
|
||||||
|
{% if XPACK_ENABLED %}
|
||||||
|
<li id="xpack">
|
||||||
|
<a>
|
||||||
|
<i class="fa fa-sitemap" style="width: 14px"></i> <span class="nav-label">{% trans 'XPack' %}</span><span class="fa arrow"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="nav nav-second-level">
|
||||||
|
{% for plugin in XPACK_PLUGINS %}
|
||||||
|
<li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if request.user.is_superuser %}
|
||||||
<li id="settings">
|
<li id="settings">
|
||||||
<a href="{% url 'settings:basic-setting' %}">
|
<a href="{% url 'settings:basic-setting' %}">
|
||||||
<i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
|
<i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
var current_org = '{{ CURRENT_ORG.name }}';
|
||||||
|
console.log(current_org);
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -1,15 +1,41 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<li class="nav-header">
|
<li class="nav-header">
|
||||||
<div class="dropdown profile-element">
|
<div class="profile-element" style="height: 65px">
|
||||||
<div href="http://www.jumpserver.org" target="_blank">
|
<div href="http://www.jumpserver.org" target="_blank" style="width: 100%; background-image: url({% static 'img/header-profile.png' %})">
|
||||||
<img alt="logo" height="55" width="185" src="/static/img/logo-text.png" style="margin-left: 20px"/>
|
<img alt="logo" height="55" width="185" src="/static/img/logo-text.png"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
|
||||||
<div class="logo-element">
|
<div class="logo-element">
|
||||||
<img alt="image" height="40" src="/static/img/logo.png"/>
|
<img alt="image" height="40" src="/static/img/logo.png"/>
|
||||||
</div>
|
</div>
|
||||||
|
{% if ADMIN_ORGS and request.COOKIES.IN_ADMIN_PAGE != 'No' %}
|
||||||
|
{% if ADMIN_ORGS|length > 1 or not CURRENT_ORG.is_default %}
|
||||||
|
<div style="height: 55px;">
|
||||||
|
<a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="display: block; background-color: transparent; color: #8095a8; padding: 14px 20px 14px 25px">
|
||||||
|
<i class="fa fa-bookmark" style="width: 14px; "></i>
|
||||||
|
<span class="nav-label" style="padding-left: 7px">
|
||||||
|
{{ CURRENT_ORG.name }}
|
||||||
|
</span>
|
||||||
|
<span class="fa fa-sort-desc pull-right"></span>
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu" style="width: 219px;">
|
||||||
|
{% for org in ADMIN_ORGS %}
|
||||||
|
<li>
|
||||||
|
<a class="org-dropdown"
|
||||||
|
href="{% url 'orgs:org-switch' pk=org.id %}"
|
||||||
|
data-id="{{ org.id }}">
|
||||||
|
{{ org.name }}
|
||||||
|
{% if org.id == CURRENT_ORG.id %}
|
||||||
|
<span class="fa fa-circle pull-right" style="padding-top: 5px; color: #1ab394"></span>
|
||||||
|
{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|
|
@ -25,8 +25,7 @@ from .hands import SystemUser
|
||||||
from .models import Terminal, Status, Session, Task
|
from .models import Terminal, Status, Session, Task
|
||||||
from .serializers import TerminalSerializer, StatusSerializer, \
|
from .serializers import TerminalSerializer, StatusSerializer, \
|
||||||
SessionSerializer, TaskSerializer, ReplaySerializer
|
SessionSerializer, TaskSerializer, ReplaySerializer
|
||||||
from .hands import IsSuperUserOrAppUser, IsAppUser, \
|
from common.permissions import IsAppUser, IsOrgAdminOrAppUser
|
||||||
IsSuperUserOrAppUserOrUserReadonly
|
|
||||||
from .backends import get_command_storage, get_multi_command_storage, \
|
from .backends import get_command_storage, get_multi_command_storage, \
|
||||||
SessionCommandSerializer
|
SessionCommandSerializer
|
||||||
|
|
||||||
|
@ -36,7 +35,7 @@ logger = logging.getLogger(__file__)
|
||||||
class TerminalViewSet(viewsets.ModelViewSet):
|
class TerminalViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Terminal.objects.filter(is_deleted=False)
|
queryset = Terminal.objects.filter(is_deleted=False)
|
||||||
serializer_class = TerminalSerializer
|
serializer_class = TerminalSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUserOrUserReadonly,)
|
permission_classes = (AllowAny,)
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
name = request.data.get('name')
|
name = request.data.get('name')
|
||||||
|
@ -105,7 +104,7 @@ class TerminalTokenApi(APIView):
|
||||||
class StatusViewSet(viewsets.ModelViewSet):
|
class StatusViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Status.objects.all()
|
queryset = Status.objects.all()
|
||||||
serializer_class = StatusSerializer
|
serializer_class = StatusSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
session_serializer_class = SessionSerializer
|
session_serializer_class = SessionSerializer
|
||||||
task_serializer_class = TaskSerializer
|
task_serializer_class = TaskSerializer
|
||||||
|
|
||||||
|
@ -177,7 +176,7 @@ class StatusViewSet(viewsets.ModelViewSet):
|
||||||
class SessionViewSet(viewsets.ModelViewSet):
|
class SessionViewSet(viewsets.ModelViewSet):
|
||||||
queryset = Session.objects.all()
|
queryset = Session.objects.all()
|
||||||
serializer_class = SessionSerializer
|
serializer_class = SessionSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
terminal_id = self.kwargs.get("terminal", None)
|
terminal_id = self.kwargs.get("terminal", None)
|
||||||
|
@ -200,11 +199,11 @@ class SessionViewSet(viewsets.ModelViewSet):
|
||||||
class TaskViewSet(BulkModelViewSet):
|
class TaskViewSet(BulkModelViewSet):
|
||||||
queryset = Task.objects.all()
|
queryset = Task.objects.all()
|
||||||
serializer_class = TaskSerializer
|
serializer_class = TaskSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
|
|
||||||
class KillSessionAPI(APIView):
|
class KillSessionAPI(APIView):
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
model = Task
|
model = Task
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
@ -236,7 +235,7 @@ class CommandViewSet(viewsets.ViewSet):
|
||||||
command_store = get_command_storage()
|
command_store = get_command_storage()
|
||||||
multi_command_storage = get_multi_command_storage()
|
multi_command_storage = get_multi_command_storage()
|
||||||
serializer_class = SessionCommandSerializer
|
serializer_class = SessionCommandSerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
self.command_store.filter(**dict(self.request.query_params))
|
self.command_store.filter(**dict(self.request.query_params))
|
||||||
|
@ -244,13 +243,14 @@ class CommandViewSet(viewsets.ViewSet):
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
serializer = self.serializer_class(data=request.data, many=True)
|
serializer = self.serializer_class(data=request.data, many=True)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
|
print(serializer.validated_data)
|
||||||
ok = self.command_store.bulk_save(serializer.validated_data)
|
ok = self.command_store.bulk_save(serializer.validated_data)
|
||||||
if ok:
|
if ok:
|
||||||
return Response("ok", status=201)
|
return Response("ok", status=201)
|
||||||
else:
|
else:
|
||||||
return Response("Save error", status=500)
|
return Response("Save error", status=500)
|
||||||
else:
|
else:
|
||||||
msg = "Not valid: {}".format(serializer.errors)
|
msg = "Command not valid: {}".format(serializer.errors)
|
||||||
logger.error(msg)
|
logger.error(msg)
|
||||||
return Response({"msg": msg}, status=401)
|
return Response({"msg": msg}, status=401)
|
||||||
|
|
||||||
|
@ -262,7 +262,7 @@ class CommandViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
class SessionReplayViewSet(viewsets.ViewSet):
|
class SessionReplayViewSet(viewsets.ViewSet):
|
||||||
serializer_class = ReplaySerializer
|
serializer_class = ReplaySerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
session = None
|
session = None
|
||||||
upload_to = 'replay' # 仅添加到本地存储中
|
upload_to = 'replay' # 仅添加到本地存储中
|
||||||
|
|
||||||
|
@ -348,7 +348,7 @@ class SessionReplayViewSet(viewsets.ViewSet):
|
||||||
|
|
||||||
class SessionReplayV2ViewSet(SessionReplayViewSet):
|
class SessionReplayV2ViewSet(SessionReplayViewSet):
|
||||||
serializer_class = ReplaySerializer
|
serializer_class = ReplaySerializer
|
||||||
permission_classes = (IsSuperUserOrAppUser,)
|
permission_classes = (IsOrgAdminOrAppUser,)
|
||||||
session = None
|
session = None
|
||||||
|
|
||||||
def retrieve(self, request, *args, **kwargs):
|
def retrieve(self, request, *args, **kwargs):
|
||||||
|
|
|
@ -21,7 +21,7 @@ class CommandStore(CommandBase):
|
||||||
user=command["user"], asset=command["asset"],
|
user=command["user"], asset=command["asset"],
|
||||||
system_user=command["system_user"], input=command["input"],
|
system_user=command["system_user"], input=command["input"],
|
||||||
output=command["output"], session=command["session"],
|
output=command["output"], session=command["session"],
|
||||||
timestamp=command["timestamp"]
|
org_id=command["org_id"], timestamp=command["timestamp"]
|
||||||
)
|
)
|
||||||
|
|
||||||
def bulk_save(self, commands):
|
def bulk_save(self, commands):
|
||||||
|
@ -33,7 +33,7 @@ class CommandStore(CommandBase):
|
||||||
_commands.append(self.model(
|
_commands.append(self.model(
|
||||||
user=c["user"], asset=c["asset"], system_user=c["system_user"],
|
user=c["user"], asset=c["asset"], system_user=c["system_user"],
|
||||||
input=c["input"], output=c["output"], session=c["session"],
|
input=c["input"], output=c["output"], session=c["session"],
|
||||||
timestamp=c["timestamp"]
|
org_id=c["org_id"], timestamp=c["timestamp"]
|
||||||
))
|
))
|
||||||
return self.model.objects.bulk_create(_commands)
|
return self.model.objects.bulk_create(_commands)
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@ import uuid
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
|
|
||||||
class AbstractSessionCommand(models.Model):
|
|
||||||
|
class AbstractSessionCommand(OrgModelMixin):
|
||||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
|
||||||
user = models.CharField(max_length=64, db_index=True, verbose_name=_("User"))
|
user = models.CharField(max_length=64, db_index=True, verbose_name=_("User"))
|
||||||
asset = models.CharField(max_length=128, db_index=True, verbose_name=_("Asset"))
|
asset = models.CharField(max_length=128, db_index=True, verbose_name=_("Asset"))
|
||||||
|
|
|
@ -12,5 +12,6 @@ class SessionCommandSerializer(serializers.Serializer):
|
||||||
input = serializers.CharField(max_length=128)
|
input = serializers.CharField(max_length=128)
|
||||||
output = serializers.CharField(max_length=1024, allow_blank=True)
|
output = serializers.CharField(max_length=1024, allow_blank=True)
|
||||||
session = serializers.CharField(max_length=36)
|
session = serializers.CharField(max_length=36)
|
||||||
|
org_id = serializers.CharField(max_length=36, required=False, default='', allow_null=True)
|
||||||
timestamp = serializers.IntegerField()
|
timestamp = serializers.IntegerField()
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,4 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from users.permissions import IsSuperUserOrAppUser, IsAppUser, \
|
from assets.models import SystemUser
|
||||||
IsSuperUserOrAppUserOrUserReadonly
|
|
||||||
from users.utils import AdminUserRequiredMixin
|
|
||||||
from assets.models import SystemUser
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.utils import timezone
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
from orgs.mixins import OrgModelMixin
|
||||||
from .backends.command.models import AbstractSessionCommand
|
from .backends.command.models import AbstractSessionCommand
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,7 +113,7 @@ class Status(models.Model):
|
||||||
return self.date_created.strftime("%Y-%m-%d %H:%M:%S")
|
return self.date_created.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
class Session(models.Model):
|
class Session(OrgModelMixin):
|
||||||
LOGIN_FROM_CHOICES = (
|
LOGIN_FROM_CHOICES = (
|
||||||
('ST', 'SSH Terminal'),
|
('ST', 'SSH Terminal'),
|
||||||
('WT', 'Web Terminal'),
|
('WT', 'Web Terminal'),
|
||||||
|
|
|
@ -150,12 +150,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('table').DataTable({
|
jumpserver.initStaticTable('table');
|
||||||
"searching": false,
|
|
||||||
"paging": false,
|
|
||||||
"bInfo" : false,
|
|
||||||
"order": []
|
|
||||||
});
|
|
||||||
$('.select2').select2({
|
$('.select2').select2({
|
||||||
dropdownAutoWidth: true,
|
dropdownAutoWidth: true,
|
||||||
width: "auto"
|
width: "auto"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
from .. import api
|
from .. import api
|
||||||
|
@ -10,26 +10,26 @@ from .. import api
|
||||||
app_name = 'terminal'
|
app_name = 'terminal'
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register(r'v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status')
|
router.register(r'terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?status', api.StatusViewSet, 'terminal-status')
|
||||||
router.register(r'v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions')
|
router.register(r'terminal/(?P<terminal>[a-zA-Z0-9\-]{36})?/?sessions', api.SessionViewSet, 'terminal-sessions')
|
||||||
router.register(r'v1/tasks', api.TaskViewSet, 'tasks')
|
router.register(r'terminal', api.TerminalViewSet, 'terminal')
|
||||||
router.register(r'v1/terminal', api.TerminalViewSet, 'terminal')
|
router.register(r'tasks', api.TaskViewSet, 'tasks')
|
||||||
router.register(r'v1/command', api.CommandViewSet, 'command')
|
router.register(r'command', api.CommandViewSet, 'command')
|
||||||
router.register(r'v1/sessions', api.SessionViewSet, 'session')
|
router.register(r'sessions', api.SessionViewSet, 'session')
|
||||||
router.register(r'v1/status', api.StatusViewSet, 'session')
|
router.register(r'status', api.StatusViewSet, 'session')
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^v1/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
|
path('sessions/<uuid:pk>/replay/',
|
||||||
api.SessionReplayViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
api.SessionReplayV2ViewSet.as_view({'get': 'retrieve', 'post': 'create'}),
|
||||||
name='session-replay'),
|
name='session-replay'),
|
||||||
url(r'^v1/tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'),
|
path('tasks/kill-session/', api.KillSessionAPI.as_view(), name='kill-session'),
|
||||||
url(r'^v1/terminal/(?P<terminal>[a-zA-Z0-9\-]{36})/access-key', api.TerminalTokenApi.as_view(),
|
path('terminal/<uuid:terminal>/access-key/', api.TerminalTokenApi.as_view(),
|
||||||
name='terminal-access-key'),
|
name='terminal-access-key'),
|
||||||
url(r'^v1/terminal/config', api.TerminalConfig.as_view(), name='terminal-config'),
|
path('terminal/config/', api.TerminalConfig.as_view(), name='terminal-config'),
|
||||||
# v2: get session's replay
|
# v2: get session's replay
|
||||||
url(r'^v2/sessions/(?P<pk>[0-9a-zA-Z\-]{36})/replay/$',
|
# path('v2/sessions/<uuid:pk>/replay/',
|
||||||
api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}),
|
# api.SessionReplayV2ViewSet.as_view({'get': 'retrieve'}),
|
||||||
name='session-replay-v2'),
|
# name='session-replay-v2'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns += router.urls
|
urlpatterns += router.urls
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
#
|
#
|
||||||
|
|
||||||
from django.conf.urls import url
|
from django.urls import path
|
||||||
|
|
||||||
from .. import views
|
from .. import views
|
||||||
|
|
||||||
|
@ -10,20 +10,20 @@ app_name = 'terminal'
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Terminal view
|
# Terminal view
|
||||||
url(r'^terminal/$', views.TerminalListView.as_view(), name='terminal-list'),
|
path('terminal/', views.TerminalListView.as_view(), name='terminal-list'),
|
||||||
url(r'^terminal/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.TerminalDetailView.as_view(), name='terminal-detail'),
|
path('terminal/<uuid:pk>/', views.TerminalDetailView.as_view(), name='terminal-detail'),
|
||||||
url(r'^terminal/(?P<pk>[0-9a-zA-Z\-]{36})/connect/$', views.TerminalConnectView.as_view(), name='terminal-connect'),
|
path('terminal/<uuid:pk>/connect/', views.TerminalConnectView.as_view(), name='terminal-connect'),
|
||||||
url(r'^terminal/(?P<pk>[0-9a-zA-Z\-]{36})/update/$', views.TerminalUpdateView.as_view(), name='terminal-update'),
|
path('terminal/<uuid:pk>/update/', views.TerminalUpdateView.as_view(), name='terminal-update'),
|
||||||
url(r'^(?P<pk>[0-9a-zA-Z\-]{36})/accept/$', views.TerminalAcceptView.as_view(), name='terminal-accept'),
|
path('<uuid:pk>/accept/', views.TerminalAcceptView.as_view(), name='terminal-accept'),
|
||||||
url(r'^web-terminal/$', views.WebTerminalView.as_view(), name='web-terminal'),
|
path('web-terminal/', views.WebTerminalView.as_view(), name='web-terminal'),
|
||||||
|
|
||||||
# Session view
|
# Session view
|
||||||
url(r'^session-online/$', views.SessionOnlineListView.as_view(), name='session-online-list'),
|
path('session-online/', views.SessionOnlineListView.as_view(), name='session-online-list'),
|
||||||
url(r'^session-offline/$', views.SessionOfflineListView.as_view(), name='session-offline-list'),
|
path('session-offline/', views.SessionOfflineListView.as_view(), name='session-offline-list'),
|
||||||
url(r'^session/(?P<pk>[0-9a-zA-Z\-]{36})/$', views.SessionDetailView.as_view(), name='session-detail'),
|
path('session/<uuid:pk>/', views.SessionDetailView.as_view(), name='session-detail'),
|
||||||
|
|
||||||
# Command view
|
# Command view
|
||||||
url(r'^command/$', views.CommandListView.as_view(), name='command-list'),
|
path('command/', views.CommandListView.as_view(), name='command-list'),
|
||||||
url(r'^command/export/$', views.CommandExportView.as_view(), name='command-export')
|
path('command/export/', views.CommandExportView.as_view(), name='command-export')
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,7 +8,8 @@ from django.http import HttpResponse
|
||||||
from django.template import loader
|
from django.template import loader
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from common.mixins import DatetimeSearchMixin, AdminUserRequiredMixin
|
from common.mixins import DatetimeSearchMixin
|
||||||
|
from common.permissions import AdminUserRequiredMixin
|
||||||
from ..models import Command
|
from ..models import Command
|
||||||
from .. import utils
|
from .. import utils
|
||||||
from ..backends import get_multi_command_storage
|
from ..backends import get_multi_command_storage
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue