[Merge] Merge branch dev of github.com:jumpserver/jumpserver into github_dev

pull/1397/head
BaiJiangJie 2018-06-05 17:31:55 +08:00
commit 4c4430661b
17 changed files with 266 additions and 505 deletions

View File

@ -11,8 +11,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 ..hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser
NodePermissionUtil
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, \
@ -22,7 +21,7 @@ from ..utils import LabelFilter
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = [ __all__ = [
'AssetViewSet', 'UserAssetListView', 'AssetListUpdateApi', 'AssetViewSet', 'AssetListUpdateApi',
'AssetRefreshHardwareApi', 'AssetAdminUserTestApi' 'AssetRefreshHardwareApi', 'AssetAdminUserTestApi'
] ]
@ -71,19 +70,6 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
return queryset return queryset
class UserAssetListView(generics.ListAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsValidUser,)
def get_queryset(self):
assets_granted = NodePermissionUtil.get_user_assets(self.request.user).keys()
queryset = self.queryset.filter(
id__in=[asset.id for asset in assets_granted]
)
return queryset
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
""" """
Asset bulk update api Asset bulk update api

View File

@ -31,7 +31,7 @@ from .. import serializers
logger = get_logger(__file__) logger = get_logger(__file__)
__all__ = [ __all__ = [
'NodeViewSet', 'NodeChildrenApi', 'NodeViewSet', 'NodeChildrenApi',
'NodeAssetsApi', 'NodeWithAssetsApi', 'NodeAssetsApi',
'NodeAddAssetsApi', 'NodeRemoveAssetsApi', 'NodeAddAssetsApi', 'NodeRemoveAssetsApi',
'NodeReplaceAssetsApi', 'NodeReplaceAssetsApi',
'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi', 'NodeAddChildrenApi', 'RefreshNodeHardwareInfoApi',
@ -42,14 +42,7 @@ __all__ = [
class NodeViewSet(BulkModelViewSet): class NodeViewSet(BulkModelViewSet):
queryset = Node.objects.all() queryset = Node.objects.all()
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
# serializer_class = serializers.NodeSerializer serializer_class = serializers.NodeSerializer
def get_serializer_class(self):
show_current_asset = self.request.query_params.get('show_current_asset')
if show_current_asset:
return serializers.NodeCurrentSerializer
else:
return serializers.NodeSerializer
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()
@ -57,32 +50,32 @@ class NodeViewSet(BulkModelViewSet):
serializer.save() serializer.save()
class NodeWithAssetsApi(generics.ListAPIView): # class NodeWithAssetsApi(generics.ListAPIView):
permission_classes = (IsSuperUser,) # permission_classes = (IsSuperUser,)
serializers = serializers.NodeSerializer # serializers = serializers.NodeSerializer
#
def get_node(self): # def get_node(self):
pk = self.kwargs.get('pk') or self.request.query_params.get('node') # pk = self.kwargs.get('pk') or self.request.query_params.get('node')
if not pk: # if not pk:
node = Node.root() # node = Node.root()
else: # else:
node = get_object_or_404(Node, pk) # node = get_object_or_404(Node, pk)
return node # return node
#
def get_queryset(self): # def get_queryset(self):
queryset = [] # queryset = []
node = self.get_node() # node = self.get_node()
children = node.get_children() # children = node.get_children()
assets = node.get_assets() # assets = node.get_assets()
queryset.extend(list(children)) # queryset.extend(list(children))
#
for asset in assets: # for asset in assets:
node = Node() # node = Node()
node.id = asset.id # node.id = asset.id
node.parent = node.id # node.parent = node.id
node.value = asset.hostname # node.value = asset.hostname
queryset.append(node) # queryset.append(node)
return queryset # return queryset
class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView): class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
@ -147,9 +140,9 @@ class NodeChildrenApi(mixins.ListModelMixin, generics.CreateAPIView):
for asset in assets: for asset in assets:
node_fake = Node() node_fake = Node()
node_fake.id = asset.id node_fake.id = asset.id
node_fake.parent = node
node_fake.value = asset.hostname
node_fake.is_node = False node_fake.is_node = False
node_fake.parent_id = node.id
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)
return queryset return queryset
@ -185,7 +178,7 @@ class NodeAddChildrenApi(generics.UpdateAPIView):
for node in children: for node in children:
if not node: if not node:
continue continue
node.set_parent(instance) node.parent = instance
return Response("OK") return Response("OK")

View File

@ -14,4 +14,3 @@
from common.mixins import AdminUserRequiredMixin from common.mixins import AdminUserRequiredMixin
from common.permissions import IsAppUser, IsSuperUser, IsValidUser, IsSuperUserOrAppUser from common.permissions import IsAppUser, IsSuperUser, IsValidUser, IsSuperUserOrAppUser
from users.models import User, UserGroup from users.models import User, UserGroup
from perms.utils import NodePermissionUtil

View File

@ -5,6 +5,7 @@
import uuid import uuid
import logging import logging
import random import random
from functools import reduce
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 _
@ -149,22 +150,15 @@ class Asset(models.Model):
nodes = self.nodes.all() or [Node.root()] nodes = self.nodes.all() or [Node.root()]
return nodes return nodes
@property def get_all_nodes(self, flat=False):
def nodes_cache_key(self): nodes = []
key = "NODES_OF_{}".format(str(self.id)) for node in self.get_nodes():
return key _nodes = node.get_ancestor(with_self=True)
_nodes.append(_nodes)
def get_nodes_or_cache(self): if flat:
cached = cache.get(self.nodes_cache_key) nodes = list(reduce(lambda x, y: set(x) | set(y), nodes))
if cached is not None:
return cached
nodes = list(self.get_nodes())
cache.set(self.nodes_cache_key, nodes, 3600)
return nodes return nodes
def expire_nodes_cache(self):
cache.delete(self.nodes_cache_key)
@property @property
def hardware_info(self): def hardware_info(self):
if self.cpu_count: if self.cpu_count:

View File

@ -5,7 +5,7 @@ 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
__all__ = ['Node'] __all__ = ['Node']
@ -22,32 +22,36 @@ class Node(models.Model):
def __str__(self): def __str__(self):
return self.full_value return self.full_value
def __eq__(self, other):
return self.key == other.key
def __gt__(self, other):
if self.is_root():
return True
self_key = [int(k) for k in self.key.split(':')]
other_key = [int(k) for k in other.key.split(':')]
if len(self_key) < len(other_key):
return True
elif len(self_key) > len(other_key):
return False
else:
return self_key[-1] < other_key[-1]
@property @property
def name(self): def name(self):
return self.value return self.value
@property @property
def full_value(self): def full_value(self):
ancestor = [a.value for a in self.ancestor] ancestor = [a.value for a in self.get_ancestor(with_self=True)]
if self.is_root(): if self.is_root():
return self.value return self.value
ancestor.append(self.value)
return ' / '.join(ancestor) return ' / '.join(ancestor)
@property @property
def level(self): def level(self):
return len(self.key.split(':')) return len(self.key.split(':'))
def set_parent(self, instance):
children = self.get_all_children()
old_key = self.key
with transaction.atomic():
self.parent = instance
for child in children:
child.key = child.key.replace(old_key, self.key, 1)
child.save()
self.save()
def get_next_child_key(self): def get_next_child_key(self):
mark = self.child_mark mark = self.child_mark
self.child_mark += 1 self.child_mark += 1
@ -55,32 +59,35 @@ class Node(models.Model):
return "{}:{}".format(self.key, mark) return "{}:{}".format(self.key, mark)
def create_child(self, value): def create_child(self, value):
child_key = self.get_next_child_key() with transaction.atomic():
child = self.__class__.objects.create(key=child_key, value=value) child_key = self.get_next_child_key()
return child child = self.__class__.objects.create(key=child_key, value=value)
return child
def get_children(self): def get_children(self, with_self=False):
pattern = r'^{0}$|^{}:[0-9]+$' if with_self else r'^{}:[0-9]+$'
return self.__class__.objects.filter( return self.__class__.objects.filter(
key__regex=r'^{}:[0-9]+$'.format(self.key) key__regex=pattern.format(self.key)
) )
def get_children_with_self(self): def get_all_children(self, with_self=False):
pattern = r'^{0}$|^{0}:' if with_self else r'^{0}'
return self.__class__.objects.filter( return self.__class__.objects.filter(
key__regex=r'^{0}$|^{0}:[0-9]+$'.format(self.key) key__regex=pattern.format(self.key)
) )
def get_all_children(self): def get_sibling(self, with_self=False):
return self.__class__.objects.filter( key = ':'.join(self.key.split(':')[:-1])
key__startswith='{}:'.format(self.key) pattern = r'^{}:[0-9]+$'.format(key)
) sibling = self.__class__.objects.filter(
key__regex=pattern.format(self.key)
def get_all_children_with_self(self):
return self.__class__.objects.filter(
key__regex=r'^{0}$|^{0}:'.format(self.key)
) )
if not with_self:
sibling = sibling.exclude(key=self.key)
return sibling
def get_family(self): def get_family(self):
ancestor = self.ancestor ancestor = self.get_ancestor()
children = self.get_all_children() children = self.get_all_children()
return [*tuple(ancestor), self, *tuple(children)] return [*tuple(ancestor), self, *tuple(children)]
@ -91,7 +98,7 @@ class Node(models.Model):
Q(nodes__id=self.id) | Q(nodes__isnull=True) Q(nodes__id=self.id) | Q(nodes__isnull=True)
) )
else: else:
assets = Asset.objects.filter(nodes__id=self.id) assets = self.assets.all()
return assets return assets
def get_valid_assets(self): def get_valid_assets(self):
@ -102,8 +109,8 @@ class Node(models.Model):
if self.is_root(): if self.is_root():
assets = Asset.objects.all() assets = Asset.objects.all()
else: else:
nodes = self.get_all_children_with_self() pattern = r'^{0}$|^{0}:'.format(self.key)
assets = Asset.objects.filter(nodes__in=nodes).distinct() assets = Asset.objects.filter(nodes__key__regex=pattern)
return assets return assets
def get_all_valid_assets(self): def get_all_valid_assets(self):
@ -125,26 +132,33 @@ class Node(models.Model):
@parent.setter @parent.setter
def parent(self, parent): def parent(self, parent):
self.key = parent.get_next_child_key() if self.is_node:
children = self.get_all_children()
old_key = self.key
with transaction.atomic():
self.key = parent.get_next_child_key()
for child in children:
child.key = child.key.replace(old_key, self.key, 1)
child.save()
self.save()
else:
self.key = parent.key+':fake'
@property def get_ancestor(self, with_self=False):
def ancestor(self):
if self.is_root(): if self.is_root():
ancestor = self.__class__.objects.filter(key='0') ancestor = self.__class__.objects.filter(key='0')
else: return ancestor
_key = self.key.split(':')
ancestor_keys = []
for i in range(len(_key)-1):
_key.pop()
ancestor_keys.append(':'.join(_key))
ancestor = self.__class__.objects.filter(key__in=ancestor_keys)
ancestor = list(ancestor)
return ancestor
@property _key = self.key.split(':')
def ancestor_with_self(self): if not with_self:
ancestor = list(self.ancestor) _key.pop()
ancestor.insert(0, self) ancestor_keys = []
for i in range(len(_key)):
ancestor_keys.append(':'.join(_key))
_key.pop()
ancestor = self.__class__.objects.filter(
key__in=ancestor_keys
).order_by('key')
return ancestor return ancestor
@classmethod @classmethod
@ -152,4 +166,6 @@ class Node(models.Model):
obj, created = cls.objects.get_or_create( obj, created = cls.objects.get_or_create(
key='0', defaults={"key": '0', 'value': "ROOT"} key='0', defaults={"key": '0', 'value': "ROOT"}
) )
print(obj)
return obj return obj

View File

@ -16,8 +16,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
""" """
资产的数据结构 资产的数据结构
""" """
nodes = serializers.SerializerMethodField()
class Meta: class Meta:
model = Asset model = Asset
list_serializer_class = BulkListSerializer list_serializer_class = BulkListSerializer
@ -31,10 +29,6 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
]) ])
return fields return fields
@staticmethod
def get_nodes(obj):
return [n.id for n in obj.get_nodes_or_cache()]
class AssetGrantedSerializer(serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
""" """

View File

@ -9,7 +9,7 @@ from .asset import AssetGrantedSerializer
__all__ = [ __all__ = [
'NodeSerializer', "NodeGrantedSerializer", "NodeAddChildrenSerializer", 'NodeSerializer', "NodeGrantedSerializer", "NodeAddChildrenSerializer",
"NodeAssetsSerializer", "NodeCurrentSerializer", "NodeAssetsSerializer",
] ]
@ -64,11 +64,11 @@ class NodeSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_parent(obj): def get_parent(obj):
return obj.parent.id return obj.parent.id if obj.is_node else obj.parent_id
@staticmethod @staticmethod
def get_assets_amount(obj): def get_assets_amount(obj):
return obj.get_all_assets().count() return obj.get_all_assets().count() if obj.is_node else 0
def get_fields(self): def get_fields(self):
fields = super().get_fields() fields = super().get_fields()
@ -77,12 +77,6 @@ class NodeSerializer(serializers.ModelSerializer):
return fields return fields
class NodeCurrentSerializer(NodeSerializer):
@staticmethod
def get_assets_amount(obj):
return obj.get_assets().count()
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())

View File

@ -64,7 +64,6 @@ def on_system_user_assets_change(sender, instance=None, **kwargs):
@receiver(m2m_changed, sender=Asset.nodes.through) @receiver(m2m_changed, sender=Asset.nodes.through)
def on_asset_node_changed(sender, instance=None, **kwargs): def on_asset_node_changed(sender, instance=None, **kwargs):
if isinstance(instance, Asset): if isinstance(instance, Asset):
instance.expire_nodes_cache()
if kwargs['action'] == 'post_add': if kwargs['action'] == 'post_add':
logger.debug("Asset node change signal received") logger.debug("Asset node change signal received")
nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) nodes = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
@ -81,10 +80,6 @@ def on_asset_node_changed(sender, instance=None, **kwargs):
def on_node_assets_changed(sender, instance=None, **kwargs): def on_node_assets_changed(sender, instance=None, **kwargs):
if isinstance(instance, Node): if isinstance(instance, Node):
assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) assets = kwargs['model'].objects.filter(pk__in=kwargs['pk_set'])
# 清理资产节点缓存
for asset in assets:
asset.expire_nodes_cache()
if kwargs['action'] == 'post_add': if kwargs['action'] == 'post_add':
logger.debug("Node assets change signal received") logger.debug("Node assets change signal received")
# 重新关联系统用户和资产的关系 # 重新关联系统用户和资产的关系

View File

@ -95,7 +95,7 @@ function initTree2() {
}; };
var zNodes = []; var zNodes = [];
$.get("{% url 'api-assets:node-list' %}?show_current_asset=1", 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["pId"] = value["parent"];
{#value["open"] = true;#} {#value["open"] = true;#}

View File

@ -399,8 +399,7 @@ function initTree() {
}; };
var zNodes = []; var zNodes = [];
var query_params = {'show_current_asset': getCookie('show_current_asset')}; $.get("{% url 'api-assets:node-list' %}", function(data, status){
$.get("{% url 'api-assets:node-list' %}", query_params, function(data, status){
$.each(data, function (index, value) { $.each(data, function (index, value) {
value["pId"] = value["parent"]; value["pId"] = value["parent"];
if (value["key"] === "0") { if (value["key"] === "0") {
@ -436,7 +435,7 @@ $(document).ready(function(){
initTable(); initTable();
initTree(); initTree();
if(getCookie('show_current_asset') === 'yes'){ if(getCookie('show_current_asset') === '1'){
$('#show_all_asset').css('display', 'inline-block'); $('#show_all_asset').css('display', 'inline-block');
} }
else{ else{
@ -564,7 +563,7 @@ $(document).ready(function(){
hideRMenu(); hideRMenu();
$(this).css('display', 'none'); $(this).css('display', 'none');
$('#show_all_asset').css('display', 'inline-block'); $('#show_all_asset').css('display', 'inline-block');
setCookie('show_current_asset', 'yes'); setCookie('show_current_asset', '1');
location.reload(); location.reload();
}) })
.on('click', '.btn-show-all-asset', function(){ .on('click', '.btn-show-all-asset', function(){

View File

@ -23,8 +23,6 @@ urlpatterns = [
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/$', url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/alive/$',
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'), api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
url(r'^v1/assets/user-assets/$',
api.UserAssetListView.as_view(), name='user-asset-list'),
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/nodes/$', url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/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/$', url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/auth/$',
@ -35,17 +33,26 @@ urlpatterns = [
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/$', url(r'^v1/system-user/(?P<pk>[0-9a-zA-Z\-]{36})/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/$', api.NodeChildrenApi.as_view(), name='node-children'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/$',
api.NodeChildrenApi.as_view(), name='node-children'),
url(r'^v1/nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'), url(r'^v1/nodes/children/$', api.NodeChildrenApi.as_view(), name='node-children-2'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$', api.NodeAddChildrenApi.as_view(), name='node-add-children'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/children/add/$',
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', api.NodeAssetsApi.as_view(), name='node-assets'), api.NodeAddChildrenApi.as_view(), name='node-add-children'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$', api.NodeAddAssetsApi.as_view(), name='node-add-assets'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/replace/$', api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'), api.NodeAssetsApi.as_view(), name='node-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$', api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$',
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/refresh-hardware-info/$', api.RefreshNodeHardwareInfoApi.as_view(), name='node-refresh-hardware-info'), api.NodeAddAssetsApi.as_view(), name='node-add-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$', api.TestNodeConnectiveApi.as_view(), name='node-test-connective'), url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/replace/$',
api.NodeReplaceAssetsApi.as_view(), name='node-replace-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/assets/remove/$',
api.NodeRemoveAssetsApi.as_view(), name='node-remove-assets'),
url(r'^v1/nodes/(?P<pk>[0-9a-zA-Z\-]{36})/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/$',
api.TestNodeConnectiveApi.as_view(), name='node-test-connective'),
url(r'^v1/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$', api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'), url(r'^v1/gateway/(?P<pk>[0-9a-zA-Z\-]{36})/test-connective/$',
api.GatewayTestConnectionApi.as_view(), name='test-gateway-connective'),
] ]
urlpatterns += router.urls urlpatterns += router.urls

View File

@ -21,23 +21,13 @@ class MailTestingAPI(APIView):
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
email_host_user = serializer.validated_data["EMAIL_HOST_USER"] email_host_user = serializer.validated_data["EMAIL_HOST_USER"]
kwargs = { for k, v in serializer.validated_data.items():
"host": serializer.validated_data["EMAIL_HOST"], if k.startswith('EMAIL'):
"port": serializer.validated_data["EMAIL_PORT"], setattr(settings, k, v)
"username": serializer.validated_data["EMAIL_HOST_USER"],
"password": serializer.validated_data["EMAIL_HOST_PASSWORD"],
"use_ssl": serializer.validated_data["EMAIL_USE_SSL"],
"use_tls": serializer.validated_data["EMAIL_USE_TLS"]
}
connection = get_connection(timeout=5, **kwargs)
try: try:
connection.open() subject = "Test"
except Exception as e: message = "Test smtp setting"
return Response({"error": str(e)}, status=401) send_mail(subject, message, email_host_user, [email_host_user])
try:
send_mail("Test", "Test smtp setting", email_host_user,
[email_host_user], connection=connection)
except Exception as e: except Exception as e:
return Response({"error": str(e)}, status=401) return Response({"error": str(e)}, status=401)

View File

@ -2,6 +2,7 @@ from django.core.mail import send_mail
from django.conf import settings from django.conf import settings
from celery import shared_task from celery import shared_task
from .utils import get_logger from .utils import get_logger
from .models import Setting
logger = get_logger(__file__) logger = get_logger(__file__)
@ -21,6 +22,10 @@ def send_mail_async(*args, **kwargs):
Example: Example:
send_mail_sync.delay(subject, message, recipient_list, fail_silently=False, html_message=None) send_mail_sync.delay(subject, message, recipient_list, fail_silently=False, html_message=None)
""" """
configs = Setting.objects.filter(name__startswith='EMAIL')
for config in configs:
setattr(settings, config.name, config.cleaned_value)
if len(args) == 3: if len(args) == 3:
args = list(args) args = list(args)
args[0] = settings.EMAIL_SUBJECT_PREFIX + args[0] args[0] = settings.EMAIL_SUBJECT_PREFIX + args[0]

View File

@ -16,6 +16,7 @@ import calendar
import threading import threading
from io import StringIO from io import StringIO
import uuid import uuid
from functools import wraps
import paramiko import paramiko
import sshpubkeys import sshpubkeys
@ -395,3 +396,17 @@ class TeeObj:
def close(self): def close(self):
self.file_obj.close() self.file_obj.close()
def with_cache(func):
cache = {}
key = "_{}.{}".format(func.__module__, func.__name__)
@wraps(func)
def wrapper(*args, **kwargs):
cached = cache.get(key)
if cached:
return cached
res = func(*args, **kwargs)
cache[key] = res
return res
return wrapper

View File

@ -41,11 +41,11 @@ class AssetPermissionViewSet(viewsets.ModelViewSet):
asset = get_object_or_404(Asset, pk=asset_id) asset = get_object_or_404(Asset, pk=asset_id)
permissions = set(queryset.filter(assets=asset)) permissions = set(queryset.filter(assets=asset))
for node in asset.nodes.all(): for node in asset.nodes.all():
inherit_nodes.update(set(node.ancestor_with_self)) inherit_nodes.update(set(node.get_ancestor(with_self=True)))
elif node_id: elif node_id:
node = get_object_or_404(Node, pk=node_id) node = get_object_or_404(Node, pk=node_id)
permissions = set(queryset.filter(nodes=node)) permissions = set(queryset.filter(nodes=node))
inherit_nodes = node.ancestor inherit_nodes = node.get_ancestor()
for n in inherit_nodes: for n in inherit_nodes:
_permissions = queryset.filter(nodes=n) _permissions = queryset.filter(nodes=n)
@ -70,7 +70,8 @@ class UserGrantedAssetsApi(ListAPIView):
else: else:
user = self.request.user user = self.request.user
for k, v in AssetPermissionUtil.get_user_assets(user).items(): util = AssetPermissionUtil(user)
for k, v in util.get_assets().items():
if k.is_unixlike(): if k.is_unixlike():
system_users_granted = [s for s in v if s.protocol == 'ssh'] system_users_granted = [s for s in v if s.protocol == 'ssh']
else: else:
@ -95,7 +96,8 @@ class UserGrantedNodesApi(ListAPIView):
user = get_object_or_404(User, id=user_id) user = get_object_or_404(User, id=user_id)
else: else:
user = self.request.user user = self.request.user
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user) util = AssetPermissionUtil(user)
nodes = util.get_nodes_with_assets()
return nodes.keys() return nodes.keys()
def get_permissions(self): def get_permissions(self):
@ -116,7 +118,8 @@ class UserGrantedNodesWithAssetsApi(ListAPIView):
else: else:
user = get_object_or_404(User, id=user_id) user = get_object_or_404(User, id=user_id)
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user) util = AssetPermissionUtil(user)
nodes = util.get_nodes_with_assets()
for node, _assets in nodes.items(): for node, _assets in nodes.items():
assets = _assets.keys() assets = _assets.keys()
for k, v in _assets.items(): for k, v in _assets.items():
@ -147,8 +150,9 @@ class UserGrantedNodeAssetsApi(ListAPIView):
user = get_object_or_404(User, id=user_id) user = get_object_or_404(User, id=user_id)
else: else:
user = self.request.user user = self.request.user
util = AssetPermissionUtil(user)
node = get_object_or_404(Node, id=node_id) node = get_object_or_404(Node, id=node_id)
nodes = AssetPermissionUtil.get_user_nodes_with_assets(user) nodes = util.get_nodes_with_assets()
assets = nodes.get(node, []) assets = nodes.get(node, [])
for asset, system_users in assets.items(): for asset, system_users in assets.items():
asset.system_users_granted = system_users asset.system_users_granted = system_users
@ -172,7 +176,8 @@ class UserGroupGrantedAssetsApi(ListAPIView):
return queryset return queryset
user_group = get_object_or_404(UserGroup, id=user_group_id) user_group = get_object_or_404(UserGroup, id=user_group_id)
assets = AssetPermissionUtil.get_user_group_assets(user_group) util = AssetPermissionUtil(user_group)
assets = util.get_assets()
for k, v in assets.items(): for k, v in assets.items():
k.system_users_granted = v k.system_users_granted = v
queryset.append(k) queryset.append(k)
@ -189,7 +194,8 @@ class UserGroupGrantedNodesApi(ListAPIView):
if group_id: if group_id:
group = get_object_or_404(UserGroup, id=group_id) group = get_object_or_404(UserGroup, id=group_id)
nodes = AssetPermissionUtil.get_user_group_nodes_with_assets(group) util = AssetPermissionUtil(group)
nodes = util.get_nodes_with_assets()
return nodes.keys() return nodes.keys()
return queryset return queryset
@ -206,7 +212,8 @@ class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
return queryset return queryset
user_group = get_object_or_404(UserGroup, id=user_group_id) user_group = get_object_or_404(UserGroup, id=user_group_id)
nodes = AssetPermissionUtil.get_user_group_nodes_with_assets(user_group) util = AssetPermissionUtil(user_group)
nodes = util.get_nodes_with_assets()
for node, _assets in nodes.items(): for node, _assets in nodes.items():
assets = _assets.keys() assets = _assets.keys()
for asset, system_users in _assets.items(): for asset, system_users in _assets.items():
@ -226,7 +233,8 @@ class UserGroupGrantedNodeAssetsApi(ListAPIView):
user_group = get_object_or_404(UserGroup, id=user_group_id) user_group = get_object_or_404(UserGroup, id=user_group_id)
node = get_object_or_404(Node, id=node_id) node = get_object_or_404(Node, id=node_id)
nodes = AssetPermissionUtil.get_user_group_nodes_with_assets(user_group) util = AssetPermissionUtil(user_group)
nodes = util.get_nodes_with_assets()
assets = nodes.get(node, []) assets = nodes.get(node, [])
for asset, system_users in assets.items(): for asset, system_users in assets.items():
asset.system_users_granted = system_users asset.system_users_granted = system_users
@ -246,7 +254,8 @@ class ValidateUserAssetPermissionView(APIView):
asset = get_object_or_404(Asset, id=asset_id) asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_id) system_user = get_object_or_404(SystemUser, id=system_id)
assets_granted = AssetPermissionUtil.get_user_assets(user) util = AssetPermissionUtil(user)
assets_granted = util.get_assets()
if system_user in assets_granted.get(asset, []): if system_user in assets_granted.get(asset, []):
return Response({'msg': True}, status=200) return Response({'msg': True}, status=200)
else: else:

View File

@ -1,359 +1,123 @@
# coding: utf-8 # coding: utf-8
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import collections
from collections import defaultdict from collections import defaultdict
from django.utils import timezone from django.db.models import Q
import copy
from common.utils import set_or_append_attr_bulk, get_logger from common.utils import get_logger
from .models import AssetPermission from .models import AssetPermission
from .hands import Node from .hands import Node
logger = get_logger(__file__) logger = get_logger(__file__)
class Tree: def get_user_permissions(user, include_group=True):
def __init__(self): if include_group:
self.__all_nodes = list(Node.objects.all().prefetch_related('assets')) groups = user.groups.all()
self.__node_asset_map = defaultdict(set) arg = Q(users=user) | Q(user_groups=groups)
self.nodes = defaultdict(dict) else:
self.root = Node.root() arg = Q(users=user)
self.init_node_asset_map() return AssetPermission.objects.all().valid().filter(arg)
def init_node_asset_map(self):
for node in self.__all_nodes:
assets = node.get_assets().values_list('id', flat=True)
for asset in assets:
self.__node_asset_map[str(asset)].add(node)
def add_asset(self, asset, system_users): def get_user_group_permissions(user_group):
nodes = self.__node_asset_map.get(str(asset.id), []) return AssetPermission.objects.all().valid().filter(
self.add_nodes(nodes) user_groups=user_group
for node in nodes: )
self.nodes[node][asset].update(system_users)
def add_node(self, node):
if node in self.nodes:
return
else:
self.nodes[node] = defaultdict(set)
if node.key == self.root.key:
return
parent_key = ':'.join(node.key.split(':')[:-1])
for n in self.__all_nodes:
if n.key == parent_key:
self.add_node(n)
break
def add_nodes(self, nodes): def get_asset_permissions(asset, include_node=True):
for node in nodes: if include_node:
self.add_node(node) nodes = asset.get_all_nodes(flat=True)
arg = Q(assets=asset) | Q(nodes=nodes)
else:
arg = Q(assets=asset)
return AssetPermission.objects.all().valid().filter(arg)
def get_node_permissions(node):
return AssetPermission.objects.all().valid().filter(nodes=node)
def get_system_user_permissions(system_user):
return AssetPermission.objects.valid().all().filter(
system_users=system_user
)
class AssetPermissionUtil: class AssetPermissionUtil:
@staticmethod get_permissions_map = {
def get_user_permissions(user): "User": get_user_permissions,
return AssetPermission.objects.all().valid().filter(users=user) "UserGroup": get_user_group_permissions,
"Asset": get_asset_permissions,
"Node": get_node_permissions,
"SystemUser": get_node_permissions,
}
@staticmethod def __init__(self, obj):
def get_user_group_permissions(user_group): self.object = obj
return AssetPermission.objects.all().valid().filter( self._permissions = None
user_groups=user_group
)
@staticmethod @property
def get_asset_permissions(asset): def permissions(self):
return AssetPermission.objects.all().valid().filter( if self._permissions:
assets=asset return self._permissions
) object_cls = self.object.__class__.__name__
func = self.get_permissions_map[object_cls]
permissions = func(self.object)
self._permissions = permissions
return permissions
@staticmethod def get_nodes_direct(self):
def get_node_permissions(node): """
return AssetPermission.objects.all().valid().filter(nodes=node) 返回用户/组授权规则直接关联的节点
:return: {node1: set(system_user1,)}
@staticmethod """
def get_system_user_permissions(system_user):
return AssetPermission.objects.valid().all().filter(
system_users=system_user
)
@classmethod
def get_user_group_nodes(cls, group):
nodes = defaultdict(set) nodes = defaultdict(set)
permissions = cls.get_user_group_permissions(group) permissions = self.permissions.prefetch_related('nodes', 'system_users')
for perm in permissions: for perm in permissions:
_nodes = perm.nodes.all() for node in perm.nodes.all():
_system_users = perm.system_users.all() nodes[node].update(perm.system_users.all())
set_or_append_attr_bulk(_nodes, 'permission', perm.id)
for node in _nodes:
nodes[node].update(set(_system_users))
return nodes return nodes
@classmethod def get_assets_direct(self):
def get_user_group_assets_direct(cls, group): """
返回用户授权规则直接关联的资产
:return: {asset1: set(system_user1,)}
"""
assets = defaultdict(set) assets = defaultdict(set)
permissions = cls.get_user_group_permissions(group) permissions = self.permissions.prefetch_related('assets', 'system_users')
for perm in permissions: for perm in permissions:
_assets = perm.assets.all().valid() for asset in perm.assets.all().valid().prefetch_related('nodes'):
_system_users = perm.system_users.all() assets[asset].update(perm.system_users.all())
set_or_append_attr_bulk(_assets, 'permission', perm.id)
for asset in _assets:
assets[asset].update(set(_system_users))
return assets return assets
@classmethod def get_assets(self):
def get_user_group_nodes_assets(cls, group): assets = self.get_assets_direct()
assets = defaultdict(set) nodes = self.get_nodes_direct()
nodes = cls.get_user_group_nodes(group)
for node, _system_users in nodes.items():
_assets = node.get_all_valid_assets()
set_or_append_attr_bulk(_assets, 'inherit_node', node.id)
set_or_append_attr_bulk(_assets, 'permission', getattr(node, 'permission', None))
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_group_assets(cls, group):
assets = defaultdict(set)
_assets = cls.get_user_group_assets_direct(group)
_nodes_assets = cls.get_user_group_nodes_assets(group)
for asset, _system_users in _assets.items():
assets[asset].update(set(_system_users))
for asset, _system_users in _nodes_assets.items():
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_group_nodes_with_assets(cls, group):
"""
:param group:
:return: {node: {asset: set(su1, su2)}}
"""
_assets = cls.get_user_group_assets(group)
tree = Tree()
for asset, _system_users in _assets.items():
_nodes = asset.get_nodes_or_cache()
tree.add_nodes(_nodes)
for node in _nodes:
tree.nodes[node][asset].update(_system_users)
return tree.nodes
@classmethod
def get_user_assets_direct(cls, user):
assets = defaultdict(set)
permissions = list(cls.get_user_permissions(user))
for perm in permissions:
_assets = perm.assets.all().valid()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_assets, 'permission', perm.id)
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_nodes_direct(cls, user):
nodes = defaultdict(set)
permissions = cls.get_user_permissions(user)
for perm in permissions:
_nodes = perm.nodes.all()
_system_users = perm.system_users.all()
set_or_append_attr_bulk(_nodes, 'permission', perm.id)
for node in _nodes:
nodes[node].update(set(_system_users))
return nodes
@classmethod
def get_user_nodes_inherit_group(cls, user):
nodes = defaultdict(set)
groups = user.groups.all()
for group in groups:
_nodes = cls.get_user_group_nodes(group)
for node, system_users in _nodes.items():
nodes[node].update(set(system_users))
return nodes
@classmethod
def get_user_nodes(cls, user):
nodes = cls.get_user_nodes_direct(user)
nodes_inherit = cls.get_user_nodes_inherit_group(user)
for node, system_users in nodes_inherit.items():
nodes[node].update(set(system_users))
return nodes
@classmethod
def get_user_nodes_assets_direct(cls, user):
assets = defaultdict(set)
nodes = cls.get_user_nodes_direct(user)
for node, _system_users in nodes.items():
_assets = node.get_all_valid_assets()
set_or_append_attr_bulk(_assets, 'inherit_node', node.id)
set_or_append_attr_bulk(_assets, 'permission', getattr(node, 'permission', None))
for asset in _assets:
assets[asset].update(set(_system_users))
return assets
@classmethod
def get_user_assets_inherit_group(cls, user):
assets = defaultdict(set)
for group in user.groups.all():
_assets = cls.get_user_group_assets(group)
set_or_append_attr_bulk(_assets, 'inherit_group', group.id)
for asset, _system_users in _assets.items():
assets[asset].update(_system_users)
return assets
@classmethod
def get_user_assets(cls, user):
assets = defaultdict(set)
_assets_direct = cls.get_user_assets_direct(user)
_nodes_assets_direct = cls.get_user_nodes_assets_direct(user)
_assets_inherit_group = cls.get_user_assets_inherit_group(user)
for asset, _system_users in _assets_direct.items():
assets[asset].update(_system_users)
for asset, _system_users in _nodes_assets_direct.items():
assets[asset].update(_system_users)
for asset, _system_users in _assets_inherit_group.items():
assets[asset].update(_system_users)
return assets
@classmethod
def get_user_nodes_with_assets(cls, user):
"""
:param user:
:return: {node: {asset: set(su1, su2)}}
"""
tree = Tree()
_assets = cls.get_user_assets(user)
for asset, _system_users in _assets.items():
tree.add_asset(asset, _system_users)
# _nodes = asset.get_nodes()
# tree.add_nodes(_nodes)
# for node in _nodes:
# tree.nodes[node][asset].update(_system_users)
return tree.nodes
@classmethod
def get_system_user_assets(cls, system_user):
assets = set()
permissions = cls.get_system_user_permissions(system_user)
for perm in permissions:
assets.update(set(perm.assets.all().valid()))
nodes = perm.nodes.all()
for node in nodes:
assets.update(set(node.get_all_valid_assets()))
return assets
@classmethod
def get_node_system_users(cls, node):
system_users = set()
permissions = cls.get_node_permissions(node)
for perm in permissions:
system_users.update(perm.system_users.all())
return system_users
# Abandon
class NodePermissionUtil:
"""
"""
@staticmethod
def get_user_group_permissions(user_group):
return user_group.nodepermission_set.all() \
.filter(is_active=True) \
.filter(date_expired__gt=timezone.now())
@staticmethod
def get_system_user_permissions(system_user):
return system_user.nodepermission_set.all() \
.filter(is_active=True) \
.filter(date_expired__gt=timezone.now())
@classmethod
def get_user_group_nodes(cls, user_group):
"""
获取用户组授权的node和系统用户
:param user_group:
:return: {"node": set(systemuser1, systemuser2), ..}
"""
permissions = cls.get_user_group_permissions(user_group)
nodes_directed = collections.defaultdict(set)
for perm in permissions:
nodes_directed[perm.node].add(perm.system_user)
nodes = copy.deepcopy(nodes_directed)
for node, system_users in nodes_directed.items():
for child in node.get_all_children_with_self():
nodes[child].update(system_users)
return nodes
@classmethod
def get_user_group_nodes_with_assets(cls, user_group):
"""
获取用户组授权的节点和系统用户节点下带有资产
:param user_group:
:return: {"node": {"assets": "", "system_user": ""}, {}}
"""
nodes = cls.get_user_group_nodes(user_group)
nodes_with_assets = dict()
for node, system_users in nodes.items(): for node, system_users in nodes.items():
nodes_with_assets[node] = { _assets = node.get_all_assets().valid().prefetch_related('nodes')
'assets': node.get_valid_assets(), for asset in _assets:
'system_users': system_users if isinstance(asset, Node):
} print(_assets)
return nodes_with_assets assets[asset].update(system_users)
@classmethod
def get_user_group_assets(cls, user_group):
assets = collections.defaultdict(set)
permissions = cls.get_user_group_permissions(user_group)
for perm in permissions:
for asset in perm.node.get_all_assets():
assets[asset].add(perm.system_user)
return assets return assets
@classmethod def get_nodes_with_assets(self):
def get_user_nodes(cls, user): """
nodes = collections.defaultdict(set) 返回节点并且包含资产
groups = user.groups.all() {"node": {"assets": set("system_user")}}
for group in groups: :return:
group_nodes = cls.get_user_group_nodes(group) """
for node, system_users in group_nodes.items(): assets = self.get_assets()
nodes[node].update(system_users) nodes = defaultdict(dict)
for asset, system_users in assets.items():
_nodes = asset.nodes.all()
for node in _nodes:
if asset in nodes[node]:
nodes[node][asset].update(system_users)
else:
nodes[node][asset] = system_users
return nodes return nodes
@classmethod
def get_user_nodes_with_assets(cls, user):
nodes = cls.get_user_nodes(user)
nodes_with_assets = dict()
for node, system_users in nodes.items():
nodes_with_assets[node] = {
'assets': node.get_valid_assets(),
'system_users': system_users
}
return nodes_with_assets
@classmethod
def get_user_assets(cls, user):
assets = collections.defaultdict(set)
nodes_with_assets = cls.get_user_nodes_with_assets(user)
for v in nodes_with_assets.values():
for asset in v['assets']:
assets[asset].update(v['system_users'])
return assets
@classmethod
def get_system_user_assets(cls, system_user):
assets = set()
permissions = cls.get_system_user_permissions(system_user)
for perm in permissions:
assets.update(perm.node.get_all_assets())
return assets

View File

@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
if grep -q 'source ~/.autoenv/activate.sh' ~/.bashrc; then if grep -q 'source /opt/autoenv/activate.sh' ~/.bashrc; then
echo -e "\033[31m 正在自动载入 python 环境 \033[0m" echo -e "\033[31m 正在自动载入 python 环境 \033[0m"
else else
echo -e "\033[31m 不支持自动升级,请参考 http://docs.jumpserver.org/zh/docs/upgrade.html 手动升级 \033[0m" echo -e "\033[31m 不支持自动升级,请参考 http://docs.jumpserver.org/zh/docs/upgrade.html 手动升级 \033[0m"
@ -40,5 +40,6 @@ git pull && pip install -r requirements/requirements.txt && cd utils && sh make_
cd .. && ./jms start all -d cd .. && ./jms start all -d
echo -e "\033[31m 请检查jumpserver是否启动成功 \033[0m" echo -e "\033[31m 请检查jumpserver是否启动成功 \033[0m"
echo -e "\033[31m 备份文件存放于$jumpserver_backup目录 \033[0m" echo -e "\033[31m 备份文件存放于$jumpserver_backup目录 \033[0m"
stty erase ^?
exit 0 exit 0