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

pull/9234/head
ibuler 2022-12-21 18:37:37 +08:00
commit df1950d063
23 changed files with 414 additions and 525 deletions

View File

@ -33,11 +33,13 @@ ARG TOOLS=" \
ca-certificates \ ca-certificates \
curl \ curl \
default-libmysqlclient-dev \ default-libmysqlclient-dev \
default-mysql-client \
locales \ locales \
openssh-client \ openssh-client \
sshpass \ sshpass \
telnet \ telnet \
unzip \ unzip \
vim \
wget" wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn ARG APT_MIRROR=http://mirrors.ustc.edu.cn

View File

@ -33,11 +33,13 @@ ARG TOOLS=" \
ca-certificates \ ca-certificates \
curl \ curl \
default-libmysqlclient-dev \ default-libmysqlclient-dev \
default-mysql-client \
locales \ locales \
openssh-client \ openssh-client \
sshpass \ sshpass \
telnet \ telnet \
unzip \ unzip \
vim \
wget" wget"
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \

View File

@ -1,16 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from urllib3.exceptions import MaxRetryError from urllib3.exceptions import MaxRetryError
from urllib.parse import urlencode, parse_qsl from urllib.parse import urlencode
from kubernetes import client from kubernetes import client
from kubernetes.client import api_client from kubernetes.client import api_client
from kubernetes.client.api import core_v1_api from kubernetes.client.api import core_v1_api
from kubernetes.client.exceptions import ApiException from kubernetes.client.exceptions import ApiException
from rest_framework.generics import get_object_or_404
from common.utils import get_logger from common.utils import get_logger
from assets.models import Account, Asset
from ..const import CloudTypes, Category from ..const import CloudTypes, Category
@ -94,57 +91,45 @@ class KubernetesClient:
return f'{gateway.address}:{gateway.port}' return f'{gateway.address}:{gateway.port}'
@classmethod @classmethod
def get_kubernetes_data(cls, app_id, username): def get_kubernetes_data(cls, asset, secret):
asset = get_object_or_404(Asset, id=app_id)
account = get_object_or_404(Account, asset=asset, username=username)
k8s_url = f'{asset.address}:{asset.port}' k8s_url = f'{asset.address}:{asset.port}'
proxy_url = cls.get_proxy_url(asset) proxy_url = cls.get_proxy_url(asset)
k8s = cls(k8s_url, account.secret, proxy=proxy_url) k8s = cls(k8s_url, secret, proxy=proxy_url)
return k8s.get_pods() return k8s.get_pods()
class KubernetesTree: class KubernetesTree:
def __init__(self, tree_id): def __init__(self, asset, secret):
self.tree_id = str(tree_id) self.asset = asset
self.secret = secret
@staticmethod def as_asset_tree_node(self):
def create_tree_id(pid, tp, v): i = str(self.asset.id)
i = dict(parse_qsl(pid)) name = str(self.asset)
i[tp] = v
tree_id = urlencode(i)
return tree_id
def as_tree_node(self, app):
pid = app.create_app_tree_pid(self.tree_id)
app_id = str(app.id)
node = self.create_tree_node( node = self.create_tree_node(
app_id, pid, app.name, 'k8s' i, i, name, 'asset', is_open=True,
) )
return node return node
def as_asset_tree_node(self, asset): def as_namespace_node(self, name, tp, counts=0):
i = urlencode({'asset_id': self.tree_id}) i = urlencode({'namespace': name})
node = self.create_tree_node( pid = str(self.asset.id)
i, str(asset.id), str(asset), 'asset', is_open=True, name = f'{name}({counts})'
) node = self.create_tree_node(i, pid, name, tp, icon='cloud')
return node return node
def as_account_tree_node(self, account, parent_info): def as_pod_tree_node(self, namespace, name, tp, counts=0):
username = account.username pid = urlencode({'namespace': namespace})
name = str(account) i = urlencode({'namespace': namespace, 'pod': name})
pid = urlencode({'asset_id': self.tree_id}) name = f'{name}({counts})'
i = self.create_tree_id(pid, 'account', username) node = self.create_tree_node(i, pid, name, tp, icon='cloud')
parent_info.update({'account': username})
node = self.create_tree_node(
i, pid, name, 'account', icon='user-tie'
)
return node return node
def as_namespace_pod_tree_node(self, name, tp, counts=0, is_container=False): def as_container_tree_node(self, namespace, pod, name, tp):
i = self.create_tree_id(self.tree_id, tp, name) pid = urlencode({'namespace': namespace, 'pod': pod})
name = name if is_container else f'{name}({counts})' i = urlencode({'namespace': namespace, 'pod': pod, 'container': name})
node = self.create_tree_node( node = self.create_tree_node(
i, self.tree_id, name, tp, icon='cloud', is_container=is_container i, pid, name, tp, icon='cloud', is_container=True
) )
return node return node
@ -169,36 +154,31 @@ class KubernetesTree:
} }
return node return node
def async_tree_node(self, parent_info): def async_tree_node(self, namespace, pod):
pod_name = parent_info.get('pod')
asset_id = parent_info.get('asset_id')
namespace = parent_info.get('namespace')
account_username = parent_info.get('account')
tree = [] tree = []
data = KubernetesClient.get_kubernetes_data(asset_id, account_username) data = KubernetesClient.get_kubernetes_data(self.asset, self.secret)
if not data: if not data:
return tree return tree
if pod_name: if pod:
for container in next( for container in next(
filter( filter(
lambda x: x['pod_name'] == pod_name, data[namespace] lambda x: x['pod_name'] == pod, data[namespace]
) )
)['containers']: )['containers']:
container_node = self.as_namespace_pod_tree_node( container_node = self.as_container_tree_node(
container, 'container', is_container=True namespace, pod, container, 'container'
) )
tree.append(container_node) tree.append(container_node)
elif namespace: elif namespace:
for pod in data[namespace]: for pod in data[namespace]:
pod_nodes = self.as_namespace_pod_tree_node( pod_nodes = self.as_pod_tree_node(
pod['pod_name'], 'pod', len(pod['containers']) namespace, pod['pod_name'], 'pod', len(pod['containers'])
) )
tree.append(pod_nodes) tree.append(pod_nodes)
elif account_username: else:
for namespace, pods in data.items(): for namespace, pods in data.items():
namespace_node = self.as_namespace_pod_tree_node( namespace_node = self.as_namespace_node(
namespace, 'namespace', len(pods) namespace, 'namespace', len(pods)
) )
tree.append(namespace_node) tree.append(namespace_node)

View File

@ -1,6 +1,7 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from common.drf.fields import EncryptedField
from orgs.mixins.serializers import OrgResourceModelSerializerMixin from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from ..models import ConnectionToken from ..models import ConnectionToken
@ -11,6 +12,9 @@ __all__ = [
class ConnectionTokenSerializer(OrgResourceModelSerializerMixin): class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
expire_time = serializers.IntegerField(read_only=True, label=_('Expired time')) expire_time = serializers.IntegerField(read_only=True, label=_('Expired time'))
input_secret = EncryptedField(
label=_("Input secret"), max_length=40960, required=False, allow_blank=True
)
class Meta: class Meta:
model = ConnectionToken model = ConnectionToken

View File

@ -9,9 +9,8 @@
- 此文件中添加代码的时候注意不要跟 `django.db.models` 中的命名冲突 - 此文件中添加代码的时候注意不要跟 `django.db.models` 中的命名冲突
""" """
import inspect
import uuid import uuid
from functools import reduce, partial from functools import reduce
from django.db import models from django.db import models
from django.db import transaction from django.db import transaction
@ -96,87 +95,6 @@ def output_as_string(field_name):
return ExpressionWrapper(F(field_name), output_field=models.CharField()) return ExpressionWrapper(F(field_name), output_field=models.CharField())
class UnionQuerySet(QuerySet):
after_union = ['order_by']
not_return_qs = [
'query', 'get', 'create', 'get_or_create',
'update_or_create', 'bulk_create', 'count',
'latest', 'earliest', 'first', 'last', 'aggregate',
'exists', 'update', 'delete', 'as_manager', 'explain',
]
def __init__(self, *queryset_list):
self.queryset_list = queryset_list
self.after_union_items = []
self.before_union_items = []
def __execute(self):
queryset_list = []
for qs in self.queryset_list:
for attr, args, kwargs in self.before_union_items:
qs = getattr(qs, attr)(*args, **kwargs)
queryset_list.append(qs)
union_qs = reduce(lambda x, y: x.union(y), queryset_list)
for attr, args, kwargs in self.after_union_items:
union_qs = getattr(union_qs, attr)(*args, **kwargs)
return union_qs
def __before_union_perform(self, item, *args, **kwargs):
self.before_union_items.append((item, args, kwargs))
return self.__clone(*self.queryset_list)
def __after_union_perform(self, item, *args, **kwargs):
self.after_union_items.append((item, args, kwargs))
return self.__clone(*self.queryset_list)
def __clone(self, *queryset_list):
uqs = UnionQuerySet(*queryset_list)
uqs.after_union_items = self.after_union_items
uqs.before_union_items = self.before_union_items
return uqs
def __getattribute__(self, item):
if item.startswith('__') or item in UnionQuerySet.__dict__ or item in [
'queryset_list', 'after_union_items', 'before_union_items'
]:
return object.__getattribute__(self, item)
if item in UnionQuerySet.not_return_qs:
return getattr(self.__execute(), item)
origin_item = object.__getattribute__(self, 'queryset_list')[0]
origin_attr = getattr(origin_item, item, None)
if not inspect.ismethod(origin_attr):
return getattr(self.__execute(), item)
if item in UnionQuerySet.after_union:
attr = partial(self.__after_union_perform, item)
else:
attr = partial(self.__before_union_perform, item)
return attr
def __getitem__(self, item):
return self.__execute()[item]
def __iter__(self):
return iter(self.__execute())
def __str__(self):
return str(self.__execute())
def __repr__(self):
return repr(self.__execute())
@classmethod
def test_it(cls):
from assets.models import Asset
assets1 = Asset.objects.filter(hostname__startswith='a')
assets2 = Asset.objects.filter(hostname__startswith='b')
qs = cls(assets1, assets2)
return qs
class MultiTableChildQueryset(QuerySet): class MultiTableChildQueryset(QuerySet):
def bulk_create(self, objs, batch_size=None): def bulk_create(self, objs, batch_size=None):

View File

@ -15,6 +15,8 @@ class AdHocViewSet(OrgBulkModelViewSet):
permission_classes = () permission_classes = ()
model = AdHoc model = AdHoc
def allow_bulk_destroy(self, qs, filtered):
return True
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
return queryset.filter(creator=self.request.user) return queryset.filter(creator=self.request.user)

View File

@ -23,6 +23,9 @@ class JobViewSet(OrgBulkModelViewSet):
permission_classes = () permission_classes = ()
model = Job model = Job
def allow_bulk_destroy(self, qs, filtered):
return True
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
queryset = queryset.filter(creator=self.request.user) queryset = queryset.filter(creator=self.request.user)
@ -31,20 +34,21 @@ class JobViewSet(OrgBulkModelViewSet):
return queryset return queryset
def perform_create(self, serializer): def perform_create(self, serializer):
run_after_save = serializer.validated_data.pop('run_after_save', False)
instance = serializer.save() instance = serializer.save()
run_after_save = serializer.validated_data.get('run_after_save', False)
if instance.instant or run_after_save: if instance.instant or run_after_save:
self.run_job(instance, serializer) self.run_job(instance, serializer)
def perform_update(self, serializer): def perform_update(self, serializer):
run_after_save = serializer.validated_data.pop('run_after_save', False)
instance = serializer.save() instance = serializer.save()
run_after_save = serializer.validated_data.get('run_after_save', False)
if run_after_save: if run_after_save:
self.run_job(instance, serializer) self.run_job(instance, serializer)
@staticmethod def run_job(self, job, serializer):
def run_job(job, serializer):
execution = job.create_execution() execution = job.create_execution()
execution.creator = self.request.user
execution.save()
task = run_ops_job_execution.delay(execution.id) task = run_ops_job_execution.delay(execution.id)
set_task_to_serializer_data(serializer, task) set_task_to_serializer_data(serializer, task)
@ -58,6 +62,7 @@ class JobExecutionViewSet(OrgBulkModelViewSet):
def perform_create(self, serializer): def perform_create(self, serializer):
instance = serializer.save() instance = serializer.save()
instance.job_version = instance.job.version instance.job_version = instance.job.version
instance.creator = self.request.user
instance.save() instance.save()
task = run_ops_job_execution.delay(instance.id) task = run_ops_job_execution.delay(instance.id)
set_task_to_serializer_data(serializer, task) set_task_to_serializer_data(serializer, task)

View File

@ -21,6 +21,9 @@ class PlaybookViewSet(OrgBulkModelViewSet):
permission_classes = () permission_classes = ()
model = Playbook model = Playbook
def allow_bulk_destroy(self, qs, filtered):
return True
def get_queryset(self): def get_queryset(self):
queryset = super().get_queryset() queryset = super().get_queryset()
queryset = queryset.filter(creator=self.request.user) queryset = queryset.filter(creator=self.request.user)

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.14 on 2022-12-21 07:13
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('ops', '0031_auto_20221220_1956'),
]
operations = [
migrations.AlterField(
model_name='historicaljob',
name='created_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='historicaljob',
name='updated_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by'),
),
]

View File

@ -163,9 +163,9 @@ class JobExecution(JMSOrgBaseModel):
def compile_shell(self): def compile_shell(self):
if self.current_job.type != 'adhoc': if self.current_job.type != 'adhoc':
return return
result = "{}{}{} ".format('\'', self.current_job.args, '\'') result = self.current_job.args
result += " chdir={}".format(self.current_job.chdir) result += " chdir={}".format(self.current_job.chdir)
return result return self.job.args
def get_runner(self): def get_runner(self):
inv = self.current_job.inventory inv = self.current_job.inventory

View File

@ -9,12 +9,11 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer
class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
run_after_save = serializers.BooleanField(label=_("Run after save"), read_only=True, default=False, required=False) run_after_save = serializers.BooleanField(label=_("Run after save"), default=False, required=False)
class Meta: class Meta:
model = Job model = Job
read_only_fields = ["id", "date_last_run", "date_created", "date_updated", "average_time_cost", read_only_fields = ["id", "date_last_run", "date_created", "date_updated", "average_time_cost"]
"run_after_save"]
fields = read_only_fields + [ fields = read_only_fields + [
"name", "instant", "type", "module", "args", "playbook", "assets", "runas_policy", "runas", "creator", "name", "instant", "type", "module", "args", "playbook", "assets", "runas_policy", "runas", "creator",
"use_parameter_define", "use_parameter_define",
@ -23,7 +22,7 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
"chdir", "chdir",
"comment", "comment",
"summary", "summary",
"is_periodic", "interval", "crontab" "is_periodic", "interval", "crontab", "run_after_save"
] ]

View File

@ -9,7 +9,7 @@ from orgs.mixins.api import OrgBulkModelViewSet
from orgs.utils import current_org from orgs.utils import current_org
from perms import serializers from perms import serializers
from perms import models from perms import models
from perms.utils.user_permission import UserGrantedAssetsQueryUtils from perms.utils import AssetPermissionPermAssetUtil
from assets.serializers import AccountSerializer from assets.serializers import AccountSerializer
__all__ = [ __all__ = [
@ -95,8 +95,7 @@ class AssetPermissionAllAssetListApi(generics.ListAPIView):
def get_queryset(self): def get_queryset(self):
pk = self.kwargs.get("pk") pk = self.kwargs.get("pk")
query_utils = UserGrantedAssetsQueryUtils(None, asset_perm_ids=[pk]) assets = AssetPermissionPermAssetUtil(perm_ids=[pk]).get_all_assets()
assets = query_utils.get_all_granted_assets()
return assets return assets

View File

@ -6,7 +6,7 @@ from assets.api.asset.asset import AssetFilterSet
from perms import serializers from perms import serializers
from perms.pagination import AllPermedAssetPagination from perms.pagination import AllPermedAssetPagination
from perms.pagination import NodePermedAssetPagination from perms.pagination import NodePermedAssetPagination
from perms.utils.user_permission import UserGrantedAssetsQueryUtils from perms.utils import UserPermAssetUtil
from common.utils import get_logger, lazyproperty from common.utils import get_logger, lazyproperty
from .mixin import ( from .mixin import (
@ -43,21 +43,23 @@ class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView):
def get_assets(self): def get_assets(self):
return Asset.objects.none() return Asset.objects.none()
query_asset_util: UserPermAssetUtil
@lazyproperty @lazyproperty
def query_asset_util(self): def query_asset_util(self):
return UserGrantedAssetsQueryUtils(self.user) return UserPermAssetUtil(self.user)
class UserAllPermedAssetsApi(BaseUserPermedAssetsApi): class UserAllPermedAssetsApi(BaseUserPermedAssetsApi):
pagination_class = AllPermedAssetPagination pagination_class = AllPermedAssetPagination
def get_assets(self): def get_assets(self):
return self.query_asset_util.get_all_granted_assets() return self.query_asset_util.get_all_assets()
class UserDirectPermedAssetsApi(BaseUserPermedAssetsApi): class UserDirectPermedAssetsApi(BaseUserPermedAssetsApi):
def get_assets(self): def get_assets(self):
return self.query_asset_util.get_direct_granted_assets() return self.query_asset_util.get_direct_assets()
class UserFavoriteAssetsApi(BaseUserPermedAssetsApi): class UserFavoriteAssetsApi(BaseUserPermedAssetsApi):

View File

@ -7,7 +7,7 @@ from rest_framework.generics import ListAPIView
from assets.models import Node from assets.models import Node
from common.utils import get_logger, lazyproperty from common.utils import get_logger, lazyproperty
from perms import serializers from perms import serializers
from perms.utils.user_permission import UserGrantedNodesQueryUtils from perms.utils import UserPermNodeUtil
from .mixin import SelfOrPKUserMixin from .mixin import SelfOrPKUserMixin
logger = get_logger(__name__) logger = get_logger(__name__)
@ -32,7 +32,7 @@ class BaseUserPermedNodesApi(SelfOrPKUserMixin, ListAPIView):
@lazyproperty @lazyproperty
def query_node_util(self): def query_node_util(self):
return UserGrantedNodesQueryUtils(self.user) return UserPermNodeUtil(self.user)
class UserAllPermedNodesApi(BaseUserPermedNodesApi): class UserAllPermedNodesApi(BaseUserPermedNodesApi):

View File

@ -3,23 +3,22 @@ from urllib.parse import parse_qsl
from django.conf import settings from django.conf import settings
from django.db.models import F, Value, CharField from django.db.models import F, Value, CharField
from rest_framework.generics import ListAPIView
from rest_framework.generics import get_object_or_404
from rest_framework.request import Request from rest_framework.request import Request
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.generics import ListAPIView
from rest_framework.generics import get_object_or_404
from rest_framework.exceptions import PermissionDenied, NotFound
from assets.api import SerializeToTreeNodeMixin
from assets.models import Asset, Account
from assets.utils import KubernetesTree from assets.utils import KubernetesTree
from assets.models import Asset, Account
from assets.api import SerializeToTreeNodeMixin
from authentication.models import ConnectionToken
from common.utils import get_object_or_none, lazyproperty from common.utils import get_object_or_none, lazyproperty
from common.utils.common import timeit from common.utils.common import timeit
from perms.hands import Node from perms.hands import Node
from perms.models import PermNode from perms.models import PermNode
from perms.utils import PermAccountUtil from perms.utils import PermAccountUtil, UserPermNodeUtil, AssetPermissionUtil
from perms.utils.permission import AssetPermissionUtil from perms.utils import UserPermAssetUtil
from perms.utils.user_permission import (
UserGrantedNodesQueryUtils, UserGrantedAssetsQueryUtils,
)
from .mixin import RebuildTreeMixin from .mixin import RebuildTreeMixin
from ..mixin import SelfOrPKUserMixin from ..mixin import SelfOrPKUserMixin
@ -52,13 +51,12 @@ class BaseUserNodeWithAssetAsTreeApi(
class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi): class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
query_node_util: UserGrantedNodesQueryUtils query_node_util: UserPermNodeUtil
query_asset_util: UserGrantedAssetsQueryUtils query_asset_util: UserPermAssetUtil
def get_nodes_assets(self): def get_nodes_assets(self):
perm_ids = AssetPermissionUtil().get_permissions_for_user(self.request.user, flat=True) self.query_node_util = UserPermNodeUtil(self.request.user)
self.query_node_util = UserGrantedNodesQueryUtils(self.request.user, perm_ids) self.query_asset_util = UserPermAssetUtil(self.request.user)
self.query_asset_util = UserGrantedAssetsQueryUtils(self.request.user, perm_ids)
ung_nodes, ung_assets = self._get_nodes_assets_for_ungrouped() ung_nodes, ung_assets = self._get_nodes_assets_for_ungrouped()
fav_nodes, fav_assets = self._get_nodes_assets_for_favorite() fav_nodes, fav_assets = self._get_nodes_assets_for_favorite()
all_nodes, all_assets = self._get_nodes_assets_for_all() all_nodes, all_assets = self._get_nodes_assets_for_all()
@ -87,9 +85,9 @@ class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
def _get_nodes_assets_for_all(self): def _get_nodes_assets_for_all(self):
nodes = self.query_node_util.get_whole_tree_nodes(with_special=False) nodes = self.query_node_util.get_whole_tree_nodes(with_special=False)
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE: if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
assets = self.query_asset_util.get_direct_granted_nodes_assets() assets = self.query_asset_util.get_perm_nodes_assets()
else: else:
assets = self.query_asset_util.get_all_granted_assets() assets = self.query_asset_util.get_all_assets()
assets = assets.annotate(parent_key=F('nodes__key')).prefetch_related('platform') assets = assets.annotate(parent_key=F('nodes__key')).prefetch_related('platform')
return nodes, assets return nodes, assets
@ -98,20 +96,21 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
""" 用户授权的节点的子节点与资产树 """ """ 用户授权的节点的子节点与资产树 """
def get_nodes_assets(self): def get_nodes_assets(self):
nodes = PermNode.objects.none() query_node_util = UserPermNodeUtil(self.user)
assets = Asset.objects.none() query_asset_util = UserPermAssetUtil(self.user)
query_node_util = UserGrantedNodesQueryUtils(self.user)
query_asset_util = UserGrantedAssetsQueryUtils(self.user)
node_key = self.query_node_key node_key = self.query_node_key
if not node_key: if not node_key:
nodes = query_node_util.get_top_level_nodes() nodes = query_node_util.get_top_level_nodes()
assets = Asset.objects.none()
elif node_key == PermNode.UNGROUPED_NODE_KEY: elif node_key == PermNode.UNGROUPED_NODE_KEY:
nodes = PermNode.objects.none()
assets = query_asset_util.get_ungroup_assets() assets = query_asset_util.get_ungroup_assets()
elif node_key == PermNode.FAVORITE_NODE_KEY: elif node_key == PermNode.FAVORITE_NODE_KEY:
nodes = PermNode.objects.none()
assets = query_asset_util.get_favorite_assets() assets = query_asset_util.get_favorite_assets()
else: else:
nodes = query_node_util.get_node_children(node_key) nodes = query_node_util.get_node_children(node_key)
assets = query_asset_util.get_node_assets(node_key) assets = query_asset_util.get_node_assets(key=node_key)
assets = assets.prefetch_related('platform') assets = assets.prefetch_related('platform')
return nodes, assets return nodes, assets
@ -133,41 +132,48 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView): class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView):
""" 用户授权的K8s树 """ """ 用户授权的K8s树 """
@staticmethod def get_token(self):
def asset(asset_id): token_id = self.request.query_params.get('token')
kwargs = {'id': asset_id, 'is_active': True} token = get_object_or_404(ConnectionToken, pk=token_id)
asset = get_object_or_404(Asset, **kwargs) if token.is_expired:
return asset raise PermissionDenied('Token is expired')
token.renewal()
return token
def get_accounts(self, asset): def get_account_secret(self, token: ConnectionToken):
util = PermAccountUtil() util = PermAccountUtil()
accounts = util.get_permed_accounts_for_user(self.user, asset) accounts = util.get_permed_accounts_for_user(self.user, token.asset)
ignore_username = [Account.AliasAccount.INPUT, Account.AliasAccount.USER] account_username = token.account
accounts = filter(lambda x: x.username not in ignore_username, accounts) accounts = filter(lambda x: x.username == account_username, accounts)
accounts = list(accounts) accounts = list(accounts)
return accounts if not accounts:
raise NotFound('Account is not found')
account = accounts[0]
if account.username in [
Account.AliasAccount.INPUT, Account.AliasAccount.USER
]:
return token.input_secret
else:
return account.secret
@staticmethod
def get_namespace_and_pod(key):
namespace_and_pod = dict(parse_qsl(key))
pod = namespace_and_pod.get('pod')
namespace = namespace_and_pod.get('namespace')
return namespace, pod
def list(self, request: Request, *args, **kwargs): def list(self, request: Request, *args, **kwargs):
tree_id = request.query_params.get('tree_id') token = self.get_token()
key = request.query_params.get('key', {}) asset = token.asset
secret = self.get_account_secret(token)
key = self.request.query_params.get('key')
namespace, pod = self.get_namespace_and_pod(key)
tree = [] tree = []
parent_info = dict(parse_qsl(key)) k8s_tree_instance = KubernetesTree(asset, secret)
account_username = parent_info.get('account') if not any([namespace, pod]) and not key:
asset_node = k8s_tree_instance.as_asset_tree_node()
asset_id = parent_info.get('asset_id')
asset_id = tree_id if not asset_id else asset_id
if tree_id and not key and not account_username:
asset = self.asset(asset_id)
accounts = self.get_accounts(asset)
asset_node = KubernetesTree(tree_id).as_asset_tree_node(asset)
tree.append(asset_node) tree.append(asset_node)
for account in accounts: tree.extend(k8s_tree_instance.async_tree_node(namespace, pod))
account_node = KubernetesTree(tree_id).as_account_tree_node(
account, parent_info,
)
tree.append(account_node)
elif key and account_username:
tree = KubernetesTree(key).async_tree_node(parent_info)
return Response(data=tree) return Response(data=tree)

View File

@ -1,7 +1,6 @@
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from django.db.models import QuerySet, Q from django.db.models import QuerySet, Q
from common.db.models import UnionQuerySet
from common.drf.filters import BaseFilterSet from common.drf.filters import BaseFilterSet
from common.utils import get_object_or_none from common.utils import get_object_or_none
from users.models import User, UserGroup from users.models import User, UserGroup
@ -169,10 +168,10 @@ class AssetPermissionFilter(PermissionBaseFilter):
inherit_all_node_ids = Node.objects.filter(key__in=inherit_all_node_keys).values_list('id', flat=True) inherit_all_node_ids = Node.objects.filter(key__in=inherit_all_node_keys).values_list('id', flat=True)
inherit_all_node_ids = list(inherit_all_node_ids) inherit_all_node_ids = list(inherit_all_node_ids)
qs1 = queryset.filter(assets__in=asset_ids).distinct() qs1_ids = queryset.filter(assets__in=asset_ids).distinct().values_list('id', flat=True)
qs2 = queryset.filter(nodes__in=inherit_all_node_ids).distinct() qs2_ids = queryset.filter(nodes__in=inherit_all_node_ids).distinct().values_list('id', flat=True)
qs_ids = list(qs1_ids) + list(qs2_ids)
qs = UnionQuerySet(qs1, qs2) qs = queryset.filter(id__in=qs_ids)
return qs return qs
def filter_effective(self, queryset): def filter_effective(self, queryset):

View File

@ -41,11 +41,6 @@ class Migration(migrations.Migration):
name='created_by', name='created_by',
field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'), field=models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by'),
), ),
migrations.AlterField(
model_name='userassetgrantedtreenoderelation',
name='id',
field=models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False),
),
migrations.AlterField( migrations.AlterField(
model_name='userassetgrantedtreenoderelation', model_name='userassetgrantedtreenoderelation',
name='updated_by', name='updated_by',

View File

@ -5,14 +5,14 @@ from django.db.models import Q
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 users.models import User
from assets.models import Asset, Account from assets.models import Asset, Account
from common.db.models import UnionQuerySet
from common.utils import date_expired_default
from common.utils.timezone import local_now
from orgs.mixins.models import JMSOrgBaseModel from orgs.mixins.models import JMSOrgBaseModel
from orgs.mixins.models import OrgManager from orgs.mixins.models import OrgManager
from common.utils import date_expired_default
from common.utils.timezone import local_now
from perms.const import ActionChoices from perms.const import ActionChoices
from users.models import User
__all__ = ['AssetPermission', 'ActionChoices'] __all__ = ['AssetPermission', 'ActionChoices']
@ -104,9 +104,10 @@ class AssetPermission(JMSOrgBaseModel):
group_ids = self.user_groups.all().values_list('id', flat=True) group_ids = self.user_groups.all().values_list('id', flat=True)
user_ids = list(user_ids) user_ids = list(user_ids)
group_ids = list(group_ids) group_ids = list(group_ids)
qs1 = User.objects.filter(id__in=user_ids).distinct() qs1_ids = User.objects.filter(id__in=user_ids).distinct().values_list('id', flat=True)
qs2 = User.objects.filter(groups__id__in=group_ids).distinct() qs2_ids = User.objects.filter(groups__id__in=group_ids).distinct().values_list('id', flat=True)
qs = UnionQuerySet(qs1, qs2) qs_ids = list(qs1_ids) + list(qs2_ids)
qs = User.objects.filter(id__in=qs_ids)
return qs return qs
def get_all_assets(self, flat=False): def get_all_assets(self, flat=False):

View File

@ -16,12 +16,18 @@ class NodeFrom(TextChoices):
class UserAssetGrantedTreeNodeRelation(FamilyMixin, JMSOrgBaseModel): class UserAssetGrantedTreeNodeRelation(FamilyMixin, JMSOrgBaseModel):
NodeFrom = NodeFrom NodeFrom = NodeFrom
id = models.AutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name=_('ID')
)
user = models.ForeignKey('users.User', db_constraint=False, on_delete=models.CASCADE) user = models.ForeignKey('users.User', db_constraint=False, on_delete=models.CASCADE)
node = models.ForeignKey('assets.Node', default=None, on_delete=models.CASCADE, node = models.ForeignKey(
db_constraint=False, null=False, related_name='granted_node_rels') 'assets.Node', default=None, on_delete=models.CASCADE, db_constraint=False, null=False,
related_name='granted_node_rels'
)
node_key = models.CharField(max_length=64, verbose_name=_("Key"), db_index=True) node_key = models.CharField(max_length=64, verbose_name=_("Key"), db_index=True)
node_parent_key = models.CharField(max_length=64, default='', verbose_name=_('Parent key'), node_parent_key = models.CharField(
db_index=True) max_length=64, default='', verbose_name=_('Parent key'), db_index=True
)
node_from = models.CharField(choices=NodeFrom.choices, max_length=16, db_index=True) node_from = models.CharField(choices=NodeFrom.choices, max_length=16, db_index=True)
node_assets_amount = models.IntegerField(default=0) node_assets_amount = models.IntegerField(default=0)
comment = '' comment = ''
@ -35,15 +41,14 @@ class UserAssetGrantedTreeNodeRelation(FamilyMixin, JMSOrgBaseModel):
return self.node_parent_key return self.node_parent_key
@classmethod @classmethod
def get_node_granted_status(cls, user, key): def get_node_from_with_node(cls, user, key):
ancestor_keys = set(cls.get_node_ancestor_keys(key, with_self=True)) ancestor_keys = set(cls.get_node_ancestor_keys(key, with_self=True))
ancestor_rel_nodes = cls.objects.filter(user=user, node_key__in=ancestor_keys) ancestor_nodes = cls.objects.filter(user=user, node_key__in=ancestor_keys)
for node in ancestor_nodes:
for rel_node in ancestor_rel_nodes: if node.key == key:
if rel_node.key == key: return node.node_from, node
return rel_node.node_from, rel_node if node.node_from == cls.NodeFrom.granted:
if rel_node.node_from == cls.NodeFrom.granted: return node.node_from, None
return cls.NodeFrom.granted, None
return '', None return '', None
@ -68,6 +73,9 @@ class PermNode(Node):
'node_from': F('granted_node_rels__node_from') 'node_from': F('granted_node_rels__node_from')
} }
def __str__(self):
return f'{self.name}'
def use_granted_assets_amount(self): def use_granted_assets_amount(self):
self.assets_amount = self.granted_assets_amount self.assets_amount = self.granted_assets_amount
@ -90,15 +98,16 @@ class PermNode(Node):
node.assets_amount = assets_amount node.assets_amount = assets_amount
return node return node
def get_granted_status(self, user): def compute_node_from_and_assets_amount(self, user):
status, rel_node = UserAssetGrantedTreeNodeRelation.get_node_granted_status(user, self.key) node_from, node = UserAssetGrantedTreeNodeRelation.get_node_from_with_node(
self.node_from = status user, self.key
if rel_node: )
self.granted_assets_amount = rel_node.node_assets_amount self.node_from = node_from
return status if node:
self.granted_assets_amount = node.node_assets_amount
def save(self): def save(self):
# 这是个只读 Model """ 这是个只读 Model """
raise NotImplementedError raise NotImplementedError

View File

@ -1,4 +1,4 @@
from .permission import * from .permission import *
from .user_permission import *
from .account import * from .account import *
from .user_perm_tree import * from .user_perm_tree import *
from .user_perm import *

View File

@ -0,0 +1,220 @@
from assets.models import FavoriteAsset, Asset
from django.conf import settings
from django.db.models import Q
from common.utils.common import timeit
from perms.models import AssetPermission, PermNode, UserAssetGrantedTreeNodeRelation
from .permission import AssetPermissionUtil
__all__ = ['AssetPermissionPermAssetUtil', 'UserPermAssetUtil', 'UserPermNodeUtil']
class AssetPermissionPermAssetUtil:
def __init__(self, perm_ids):
self.perm_ids = perm_ids
def get_all_assets(self):
""" 获取所有授权的资产 """
node_asset_ids = self.get_perm_nodes_assets(flat=True)
direct_asset_ids = self.get_direct_assets(flat=True)
asset_ids = list(node_asset_ids) + list(direct_asset_ids)
assets = Asset.objects.filter(id__in=asset_ids)
return assets
def get_perm_nodes_assets(self, flat=False):
""" 获取所有授权节点下的资产 """
node_ids = AssetPermission.nodes.through.objects \
.filter(assetpermission_id__in=self.perm_ids) \
.values_list('node_id', flat=True) \
.distinct()
node_ids = list(node_ids)
nodes = PermNode.objects.filter(id__in=node_ids).only('id', 'key')
assets = PermNode.get_nodes_all_assets(*nodes)
if flat:
return assets.values_list('id', flat=True)
return assets
def get_direct_assets(self, flat=False):
""" 获取直接授权的资产 """
assets = Asset.objects.order_by() \
.filter(granted_by_permissions__id__in=self.perm_ids) \
.distinct()
if flat:
return assets.values_list('id', flat=True)
return assets
class UserPermAssetUtil(AssetPermissionPermAssetUtil):
def __init__(self, user):
self.user = user
perm_ids = AssetPermissionUtil().get_permissions_for_user(self.user, flat=True)
super().__init__(perm_ids)
def get_ungroup_assets(self):
return self.get_direct_assets()
def get_favorite_assets(self):
assets = self.get_all_assets()
asset_ids = FavoriteAsset.objects.filter(user=self.user).values_list('asset_id', flat=True)
assets = assets.filter(id__in=list(asset_ids))
return assets
def get_node_assets(self, key):
node = PermNode.objects.get(key=key)
node.compute_node_from_and_assets_amount(self.user)
if node.node_from == node.NodeFrom.granted:
assets = Asset.objects.filter(nodes__id=node.id).order_by()
elif node.node_from == node.NodeFrom.asset:
assets = self._get_indirect_perm_node_assets(node)
else:
assets = Asset.objects.none()
assets = assets.order_by('name')
return assets
def get_node_all_assets(self, node_id):
""" 获取节点下的所有资产 """
node = PermNode.objects.get(id=node_id)
node.compute_node_from_and_assets_amount(self.user)
if node.node_from == node.NodeFrom.granted:
assets = PermNode.get_nodes_all_assets()
elif node.node_from in (node.NodeFrom.asset, node.NodeFrom.child):
node.assets_amount = node.granted_assets_amount
assets = self._get_indirect_perm_node_all_assets(node)
else:
node.assets_amount = 0
assets = Asset.objects.none()
return node, assets
def _get_indirect_perm_node_assets(self, node):
""" 获取间接授权节点下的直接资产 """
assets = self.get_direct_assets()
assets = assets.filter(nodes__id=node.id).order_by().distinct()
return assets
def _get_indirect_perm_node_all_assets(self, node):
""" 获取间接授权节点下的所有资产
此算法依据 `UserAssetGrantedTreeNodeRelation` 的数据查询
1. 查询该节点下的直接授权节点
2. 查询该节点下授权资产关联的节点
"""
# 查询节点下直接授权的子节点
asset_ids = set()
children_from_granted = UserAssetGrantedTreeNodeRelation.objects \
.filter(user=self.user) \
.filter(node_key__startwith=f'{node.key}:', node_from=node.NodeFrom.granted) \
.only('node_id', 'node_key')
for n in children_from_granted:
n.id = n.node_id
_assets = PermNode.get_nodes_all_assets(*children_from_granted)
_asset_ids = _assets.values_list('id', flat=True)
asset_ids.update(list(_asset_ids))
# 查询节点下资产授权的节点
children_from_assets = UserAssetGrantedTreeNodeRelation.objects \
.filter(user=self.user) \
.filter(node_key__startwith=f'{node.key}:', node_from=node.NodeFrom.asset) \
.values_list('node_id', flat=True)
children_from_assets = set(children_from_assets)
if node.node_from == node.NodeFrom.asset:
children_from_assets.add(node.id)
_asset_ids = Asset.objects \
.filter(node__id__in=children_from_assets) \
.filter(granted_by_permissions__id__in=self.perm_ids) \
.distinct() \
.order_by() \
.values_list('id', flat=True)
asset_ids.update(list(_asset_ids))
return Asset.objects.filter(id__in=asset_ids)
class UserPermNodeUtil:
def __init__(self, user):
self.user = user
self.perm_ids = AssetPermissionUtil().get_permissions_for_user(self.user, flat=True)
def get_favorite_node(self):
assets_amount = UserPermAssetUtil(self.user).get_favorite_assets().count()
return PermNode.get_favorite_node(assets_amount)
def get_ungrouped_node(self):
assets_amount = UserPermAssetUtil(self.user).get_direct_assets().count()
return PermNode.get_favorite_node(assets_amount)
def get_top_level_nodes(self):
nodes = self.get_special_nodes()
real_nodes = self._get_indirect_perm_node_children(key='')
nodes.extend(real_nodes)
if len(real_nodes) == 1:
children = self.get_node_children(real_nodes[0].key)
nodes.extend(children)
return nodes
def get_special_nodes(self):
nodes = []
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
ung_node = self.get_ungrouped_node()
nodes.append(ung_node)
fav_node = self.get_favorite_node()
nodes.append(fav_node)
return nodes
def get_node_children(self, key):
if not key:
return self.get_top_level_nodes()
if key in [PermNode.FAVORITE_NODE_KEY, PermNode.UNGROUPED_NODE_KEY]:
return PermNode.objects.none()
node = PermNode.objects.get(key=key)
node.compute_node_from_and_assets_amount(self.user)
if node.node_from == node.NodeFrom.granted:
children = PermNode.objects.filter(parent_key=key)
elif node.node_from in (node.NodeFrom.asset, node.NodeFrom.child):
children = self._get_indirect_perm_node_children(key)
else:
children = PermNode.objects.none()
children = sorted(children, key=lambda x: x.value)
return children
def _get_indirect_perm_node_children(self, key):
""" 获取未直接授权节点的子节点 """
children = PermNode.objects.filter(granted_node_rels__user=self.user, parent_key=key)
children = children.annotate(**PermNode.annotate_granted_node_rel_fields).distinct()
for node in children:
node.assets_amount = node.granted_assets_amount
return children
@timeit
def get_whole_tree_nodes(self, with_special=True):
user_nodes = PermNode.objects.filter(granted_node_rels__user=self.user)
user_nodes = user_nodes.annotate(**PermNode.annotate_granted_node_rel_fields).distinct()
key_node_mapper = {}
q_nodes_descendant = Q()
for node in user_nodes:
node.assets_amount = node.granted_assets_amount
key_node_mapper[node.key] = node
if node.node_from == node.NodeFrom.granted:
""" 直接授权的节点, 增加后代节点的过滤条件 """
q_nodes_descendant |= Q(key__startswith=f'{node.key}:')
if q_nodes_descendant:
descendant_nodes = PermNode.objects.filter(q_nodes_descendant)
for node in descendant_nodes:
key_node_mapper[node.key] = node
nodes = []
if with_special:
special_nodes = self.get_special_nodes()
nodes.extend(special_nodes)
nodes.extend(list(key_node_mapper.values()))
return nodes

View File

@ -1,280 +0,0 @@
from collections import defaultdict
from typing import List, Tuple
from django.conf import settings
from django.db.models import Q, QuerySet
from django.utils.translation import gettext as _
from users.models import User
from assets.utils import NodeAssetsUtil
from assets.models import (
Asset,
FavoriteAsset,
AssetQuerySet,
NodeQuerySet
)
from orgs.utils import (
tmp_to_org,
current_org,
ensure_in_real_or_default_org,
)
from common.db.models import output_as_string, UnionQuerySet
from common.utils import get_logger
from common.utils.common import lazyproperty, timeit
from perms.models import (
AssetPermission,
PermNode,
UserAssetGrantedTreeNodeRelation
)
from .permission import AssetPermissionUtil
NodeFrom = UserAssetGrantedTreeNodeRelation.NodeFrom
NODE_ONLY_FIELDS = ('id', 'key', 'parent_key', 'org_id')
logger = get_logger(__name__)
class UserGrantedUtilsBase:
user: User
def __init__(self, user, asset_perm_ids=None):
self.user = user
self._asset_perm_ids = asset_perm_ids and set(asset_perm_ids)
@lazyproperty
def asset_perm_ids(self) -> set:
if self._asset_perm_ids:
return self._asset_perm_ids
asset_perm_ids = AssetPermissionUtil().get_permissions_for_user(self.user, flat=True)
return asset_perm_ids
class UserGrantedAssetsQueryUtils(UserGrantedUtilsBase):
def get_favorite_assets(self) -> QuerySet:
assets = self.get_all_granted_assets()
asset_ids = FavoriteAsset.objects.filter(user=self.user).values_list('asset_id', flat=True)
assets = assets.filter(id__in=list(asset_ids))
return assets
def get_ungroup_assets(self) -> AssetQuerySet:
return self.get_direct_granted_assets()
def get_direct_granted_assets(self) -> AssetQuerySet:
queryset = Asset.objects.order_by().filter(
granted_by_permissions__id__in=self.asset_perm_ids
).distinct()
return queryset
def get_direct_granted_nodes_assets(self) -> AssetQuerySet:
granted_node_ids = AssetPermission.nodes.through.objects.filter(
assetpermission_id__in=self.asset_perm_ids
).values_list('node_id', flat=True).distinct()
granted_node_ids = list(granted_node_ids)
granted_nodes = PermNode.objects.filter(id__in=granted_node_ids).only('id', 'key')
queryset = PermNode.get_nodes_all_assets(*granted_nodes)
return queryset
def get_all_granted_assets(self) -> QuerySet:
nodes_assets = self.get_direct_granted_nodes_assets()
assets = self.get_direct_granted_assets()
# queryset = UnionQuerySet(nodes_assets, assets)
# return queryset
node_asset_ids = nodes_assets.values_list('id', flat=True)
direct_asset_ids = assets.values_list('id', flat=True)
asset_ids = list(node_asset_ids) + list(direct_asset_ids)
asset = Asset.objects.filter(id__in=asset_ids)
return asset
def get_node_all_assets(self, id) -> Tuple[PermNode, QuerySet]:
node = PermNode.objects.get(id=id)
granted_status = node.get_granted_status(self.user)
if granted_status == NodeFrom.granted:
assets = PermNode.get_nodes_all_assets(node)
return node, assets
elif granted_status in (NodeFrom.asset, NodeFrom.child):
node.use_granted_assets_amount()
assets = self._get_indirect_granted_node_all_assets(node)
return node, assets
else:
node.assets_amount = 0
return node, Asset.objects.none()
def get_node_assets(self, key) -> AssetQuerySet:
node = PermNode.objects.get(key=key)
granted_status = node.get_granted_status(self.user)
if granted_status == NodeFrom.granted:
assets = Asset.objects.order_by().filter(nodes__id=node.id)
elif granted_status == NodeFrom.asset:
assets = self._get_indirect_granted_node_assets(node.id)
else:
assets = Asset.objects.none()
assets = assets.order_by('name')
return assets
def _get_indirect_granted_node_assets(self, id) -> AssetQuerySet:
assets = Asset.objects.order_by().filter(nodes__id=id).distinct() & self.get_direct_granted_assets()
return assets
def _get_indirect_granted_node_all_assets(self, node) -> QuerySet:
"""
此算法依据 `UserAssetGrantedTreeNodeRelation` 的数据查询
1. 查询该节点下的直接授权节点
2. 查询该节点下授权资产关联的节点
"""
user = self.user
# 查询该节点下的授权节点
granted_nodes = UserAssetGrantedTreeNodeRelation.objects.filter(
user=user, node_from=NodeFrom.granted
).filter(
Q(node_key__startswith=f'{node.key}:')
).only('node_id', 'node_key')
for n in granted_nodes:
n.id = n.node_id
node_assets = PermNode.get_nodes_all_assets(*granted_nodes)
# 查询该节点下的资产授权节点
only_asset_granted_node_ids = UserAssetGrantedTreeNodeRelation.objects.filter(
user=user, node_from=NodeFrom.asset
).filter(
Q(node_key__startswith=f'{node.key}:')
).values_list('node_id', flat=True)
only_asset_granted_node_ids = list(only_asset_granted_node_ids)
if node.node_from == NodeFrom.asset:
only_asset_granted_node_ids.append(node.id)
assets = Asset.objects.filter(
nodes__id__in=only_asset_granted_node_ids,
granted_by_permissions__id__in=self.asset_perm_ids
).distinct().order_by()
granted_assets = UnionQuerySet(node_assets, assets)
return granted_assets
class UserGrantedNodesQueryUtils(UserGrantedUtilsBase):
def sort(self, nodes):
nodes = sorted(nodes, key=lambda x: x.value)
return nodes
def get_node_children(self, key):
if not key:
return self.get_top_level_nodes()
nodes = PermNode.objects.none()
if key in [PermNode.FAVORITE_NODE_KEY, PermNode.UNGROUPED_NODE_KEY]:
return nodes
node = PermNode.objects.get(key=key)
granted_status = node.get_granted_status(self.user)
if granted_status == NodeFrom.granted:
nodes = PermNode.objects.filter(parent_key=key)
elif granted_status in (NodeFrom.asset, NodeFrom.child):
nodes = self.get_indirect_granted_node_children(key)
nodes = self.sort(nodes)
return nodes
def get_indirect_granted_node_children(self, key):
"""
获取用户授权树中未授权节点的子节点
只匹配在 `UserAssetGrantedTreeNodeRelation` 中存在的节点
"""
user = self.user
nodes = PermNode.objects.filter(
granted_node_rels__user=user,
parent_key=key
).annotate(
**PermNode.annotate_granted_node_rel_fields
).distinct()
# 设置节点授权资产数量
for node in nodes:
node.use_granted_assets_amount()
return nodes
def get_top_level_nodes(self):
nodes = self.get_special_nodes()
real_nodes = self.get_indirect_granted_node_children('')
nodes.extend(real_nodes)
if len(real_nodes) == 1:
children = self.get_node_children(real_nodes[0].key)
nodes.extend(children)
return nodes
def get_ungrouped_node(self):
assets_util = UserGrantedAssetsQueryUtils(self.user, self.asset_perm_ids)
assets_amount = assets_util.get_direct_granted_assets().count()
return PermNode.get_ungrouped_node(assets_amount)
def get_favorite_node(self):
assets_query_utils = UserGrantedAssetsQueryUtils(self.user, self.asset_perm_ids)
assets_amount = assets_query_utils.get_favorite_assets().values_list('id').count()
return PermNode.get_favorite_node(assets_amount)
@staticmethod
def get_root_node():
name = _('My assets')
node = {
'id': '',
'name': name,
'title': name,
'pId': '',
'open': True,
'isParent': True,
'meta': {
'type': 'root'
}
}
return node
def get_special_nodes(self):
nodes = []
if settings.PERM_SINGLE_ASSET_TO_UNGROUP_NODE:
ungrouped_node = self.get_ungrouped_node()
nodes.append(ungrouped_node)
favorite_node = self.get_favorite_node()
nodes.append(favorite_node)
return nodes
@timeit
def get_whole_tree_nodes(self, with_special=True):
"""
这里的 granted nodes, 是整棵树需要的node推算出来的也算
:param with_special:
:return:
"""
nodes = PermNode.objects.filter(granted_node_rels__user=self.user) \
.annotate(**PermNode.annotate_granted_node_rel_fields) \
.distinct()
key_to_node_mapper = {}
nodes_descendant_q = Q()
for node in nodes:
node.use_granted_assets_amount()
key_to_node_mapper[node.key] = node
if node.node_from == NodeFrom.granted:
# 直接授权的节点
# 增加查询后代节点的过滤条件
nodes_descendant_q |= Q(key__startswith=f'{node.key}:')
if nodes_descendant_q:
descendant_nodes = PermNode.objects.filter(
nodes_descendant_q
)
for node in descendant_nodes:
key_to_node_mapper[node.key] = node
all_nodes = []
if with_special:
special_nodes = self.get_special_nodes()
all_nodes.extend(special_nodes)
all_nodes.extend(key_to_node_mapper.values())
return all_nodes

View File

@ -153,7 +153,7 @@ class ConnectMethodUtil:
protocols = { protocols = {
TerminalType.koko: { TerminalType.koko: {
'web_methods': [WebMethod.web_cli, WebMethod.web_sftp], 'web_methods': [WebMethod.web_cli, WebMethod.web_sftp],
'listen': [Protocol.ssh, Protocol.http], 'listen': [Protocol.http],
'support': [ 'support': [
Protocol.ssh, Protocol.telnet, Protocol.ssh, Protocol.telnet,
Protocol.mysql, Protocol.postgresql, Protocol.mysql, Protocol.postgresql,