mirror of https://github.com/jumpserver/jumpserver
Merge branch 'v3' of github.com:jumpserver/jumpserver into v3
commit
df1950d063
|
@ -33,11 +33,13 @@ ARG TOOLS=" \
|
|||
ca-certificates \
|
||||
curl \
|
||||
default-libmysqlclient-dev \
|
||||
default-mysql-client \
|
||||
locales \
|
||||
openssh-client \
|
||||
sshpass \
|
||||
telnet \
|
||||
unzip \
|
||||
vim \
|
||||
wget"
|
||||
|
||||
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
|
||||
|
|
|
@ -33,11 +33,13 @@ ARG TOOLS=" \
|
|||
ca-certificates \
|
||||
curl \
|
||||
default-libmysqlclient-dev \
|
||||
default-mysql-client \
|
||||
locales \
|
||||
openssh-client \
|
||||
sshpass \
|
||||
telnet \
|
||||
unzip \
|
||||
vim \
|
||||
wget"
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from urllib3.exceptions import MaxRetryError
|
||||
from urllib.parse import urlencode, parse_qsl
|
||||
from urllib.parse import urlencode
|
||||
|
||||
from kubernetes import client
|
||||
from kubernetes.client import api_client
|
||||
from kubernetes.client.api import core_v1_api
|
||||
from kubernetes.client.exceptions import ApiException
|
||||
|
||||
from rest_framework.generics import get_object_or_404
|
||||
|
||||
from common.utils import get_logger
|
||||
from assets.models import Account, Asset
|
||||
|
||||
from ..const import CloudTypes, Category
|
||||
|
||||
|
@ -94,57 +91,45 @@ class KubernetesClient:
|
|||
return f'{gateway.address}:{gateway.port}'
|
||||
|
||||
@classmethod
|
||||
def get_kubernetes_data(cls, app_id, username):
|
||||
asset = get_object_or_404(Asset, id=app_id)
|
||||
account = get_object_or_404(Account, asset=asset, username=username)
|
||||
def get_kubernetes_data(cls, asset, secret):
|
||||
k8s_url = f'{asset.address}:{asset.port}'
|
||||
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()
|
||||
|
||||
|
||||
class KubernetesTree:
|
||||
def __init__(self, tree_id):
|
||||
self.tree_id = str(tree_id)
|
||||
def __init__(self, asset, secret):
|
||||
self.asset = asset
|
||||
self.secret = secret
|
||||
|
||||
@staticmethod
|
||||
def create_tree_id(pid, tp, v):
|
||||
i = dict(parse_qsl(pid))
|
||||
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)
|
||||
def as_asset_tree_node(self):
|
||||
i = str(self.asset.id)
|
||||
name = str(self.asset)
|
||||
node = self.create_tree_node(
|
||||
app_id, pid, app.name, 'k8s'
|
||||
i, i, name, 'asset', is_open=True,
|
||||
)
|
||||
return node
|
||||
|
||||
def as_asset_tree_node(self, asset):
|
||||
i = urlencode({'asset_id': self.tree_id})
|
||||
node = self.create_tree_node(
|
||||
i, str(asset.id), str(asset), 'asset', is_open=True,
|
||||
)
|
||||
def as_namespace_node(self, name, tp, counts=0):
|
||||
i = urlencode({'namespace': name})
|
||||
pid = str(self.asset.id)
|
||||
name = f'{name}({counts})'
|
||||
node = self.create_tree_node(i, pid, name, tp, icon='cloud')
|
||||
return node
|
||||
|
||||
def as_account_tree_node(self, account, parent_info):
|
||||
username = account.username
|
||||
name = str(account)
|
||||
pid = urlencode({'asset_id': self.tree_id})
|
||||
i = self.create_tree_id(pid, 'account', username)
|
||||
parent_info.update({'account': username})
|
||||
node = self.create_tree_node(
|
||||
i, pid, name, 'account', icon='user-tie'
|
||||
)
|
||||
def as_pod_tree_node(self, namespace, name, tp, counts=0):
|
||||
pid = urlencode({'namespace': namespace})
|
||||
i = urlencode({'namespace': namespace, 'pod': name})
|
||||
name = f'{name}({counts})'
|
||||
node = self.create_tree_node(i, pid, name, tp, icon='cloud')
|
||||
return node
|
||||
|
||||
def as_namespace_pod_tree_node(self, name, tp, counts=0, is_container=False):
|
||||
i = self.create_tree_id(self.tree_id, tp, name)
|
||||
name = name if is_container else f'{name}({counts})'
|
||||
def as_container_tree_node(self, namespace, pod, name, tp):
|
||||
pid = urlencode({'namespace': namespace, 'pod': pod})
|
||||
i = urlencode({'namespace': namespace, 'pod': pod, 'container': name})
|
||||
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
|
||||
|
||||
|
@ -169,36 +154,31 @@ class KubernetesTree:
|
|||
}
|
||||
return node
|
||||
|
||||
def async_tree_node(self, parent_info):
|
||||
pod_name = parent_info.get('pod')
|
||||
asset_id = parent_info.get('asset_id')
|
||||
namespace = parent_info.get('namespace')
|
||||
account_username = parent_info.get('account')
|
||||
|
||||
def async_tree_node(self, namespace, pod):
|
||||
tree = []
|
||||
data = KubernetesClient.get_kubernetes_data(asset_id, account_username)
|
||||
data = KubernetesClient.get_kubernetes_data(self.asset, self.secret)
|
||||
if not data:
|
||||
return tree
|
||||
|
||||
if pod_name:
|
||||
if pod:
|
||||
for container in next(
|
||||
filter(
|
||||
lambda x: x['pod_name'] == pod_name, data[namespace]
|
||||
lambda x: x['pod_name'] == pod, data[namespace]
|
||||
)
|
||||
)['containers']:
|
||||
container_node = self.as_namespace_pod_tree_node(
|
||||
container, 'container', is_container=True
|
||||
container_node = self.as_container_tree_node(
|
||||
namespace, pod, container, 'container'
|
||||
)
|
||||
tree.append(container_node)
|
||||
elif namespace:
|
||||
for pod in data[namespace]:
|
||||
pod_nodes = self.as_namespace_pod_tree_node(
|
||||
pod['pod_name'], 'pod', len(pod['containers'])
|
||||
pod_nodes = self.as_pod_tree_node(
|
||||
namespace, pod['pod_name'], 'pod', len(pod['containers'])
|
||||
)
|
||||
tree.append(pod_nodes)
|
||||
elif account_username:
|
||||
else:
|
||||
for namespace, pods in data.items():
|
||||
namespace_node = self.as_namespace_pod_tree_node(
|
||||
namespace_node = self.as_namespace_node(
|
||||
namespace, 'namespace', len(pods)
|
||||
)
|
||||
tree.append(namespace_node)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.drf.fields import EncryptedField
|
||||
from orgs.mixins.serializers import OrgResourceModelSerializerMixin
|
||||
from ..models import ConnectionToken
|
||||
|
||||
|
@ -11,6 +12,9 @@ __all__ = [
|
|||
|
||||
class ConnectionTokenSerializer(OrgResourceModelSerializerMixin):
|
||||
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:
|
||||
model = ConnectionToken
|
||||
|
|
|
@ -9,9 +9,8 @@
|
|||
- 此文件中添加代码的时候,注意不要跟 `django.db.models` 中的命名冲突
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import uuid
|
||||
from functools import reduce, partial
|
||||
from functools import reduce
|
||||
|
||||
from django.db import models
|
||||
from django.db import transaction
|
||||
|
@ -96,87 +95,6 @@ def output_as_string(field_name):
|
|||
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):
|
||||
|
||||
def bulk_create(self, objs, batch_size=None):
|
||||
|
|
|
@ -15,6 +15,8 @@ class AdHocViewSet(OrgBulkModelViewSet):
|
|||
permission_classes = ()
|
||||
model = AdHoc
|
||||
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
return True
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
return queryset.filter(creator=self.request.user)
|
||||
|
|
|
@ -23,6 +23,9 @@ class JobViewSet(OrgBulkModelViewSet):
|
|||
permission_classes = ()
|
||||
model = Job
|
||||
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
return True
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.filter(creator=self.request.user)
|
||||
|
@ -31,20 +34,21 @@ class JobViewSet(OrgBulkModelViewSet):
|
|||
return queryset
|
||||
|
||||
def perform_create(self, serializer):
|
||||
run_after_save = serializer.validated_data.pop('run_after_save', False)
|
||||
instance = serializer.save()
|
||||
run_after_save = serializer.validated_data.get('run_after_save', False)
|
||||
if instance.instant or run_after_save:
|
||||
self.run_job(instance, serializer)
|
||||
|
||||
def perform_update(self, serializer):
|
||||
run_after_save = serializer.validated_data.pop('run_after_save', False)
|
||||
instance = serializer.save()
|
||||
run_after_save = serializer.validated_data.get('run_after_save', False)
|
||||
if run_after_save:
|
||||
self.run_job(instance, serializer)
|
||||
|
||||
@staticmethod
|
||||
def run_job(job, serializer):
|
||||
def run_job(self, job, serializer):
|
||||
execution = job.create_execution()
|
||||
execution.creator = self.request.user
|
||||
execution.save()
|
||||
task = run_ops_job_execution.delay(execution.id)
|
||||
set_task_to_serializer_data(serializer, task)
|
||||
|
||||
|
@ -58,6 +62,7 @@ class JobExecutionViewSet(OrgBulkModelViewSet):
|
|||
def perform_create(self, serializer):
|
||||
instance = serializer.save()
|
||||
instance.job_version = instance.job.version
|
||||
instance.creator = self.request.user
|
||||
instance.save()
|
||||
task = run_ops_job_execution.delay(instance.id)
|
||||
set_task_to_serializer_data(serializer, task)
|
||||
|
|
|
@ -21,6 +21,9 @@ class PlaybookViewSet(OrgBulkModelViewSet):
|
|||
permission_classes = ()
|
||||
model = Playbook
|
||||
|
||||
def allow_bulk_destroy(self, qs, filtered):
|
||||
return True
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
queryset = queryset.filter(creator=self.request.user)
|
||||
|
|
|
@ -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'),
|
||||
),
|
||||
]
|
|
@ -163,9 +163,9 @@ class JobExecution(JMSOrgBaseModel):
|
|||
def compile_shell(self):
|
||||
if self.current_job.type != 'adhoc':
|
||||
return
|
||||
result = "{}{}{} ".format('\'', self.current_job.args, '\'')
|
||||
result += "chdir={}".format(self.current_job.chdir)
|
||||
return result
|
||||
result = self.current_job.args
|
||||
result += " chdir={}".format(self.current_job.chdir)
|
||||
return self.job.args
|
||||
|
||||
def get_runner(self):
|
||||
inv = self.current_job.inventory
|
||||
|
|
|
@ -9,12 +9,11 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer
|
|||
|
||||
class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
|
||||
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:
|
||||
model = Job
|
||||
read_only_fields = ["id", "date_last_run", "date_created", "date_updated", "average_time_cost",
|
||||
"run_after_save"]
|
||||
read_only_fields = ["id", "date_last_run", "date_created", "date_updated", "average_time_cost"]
|
||||
fields = read_only_fields + [
|
||||
"name", "instant", "type", "module", "args", "playbook", "assets", "runas_policy", "runas", "creator",
|
||||
"use_parameter_define",
|
||||
|
@ -23,7 +22,7 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
|
|||
"chdir",
|
||||
"comment",
|
||||
"summary",
|
||||
"is_periodic", "interval", "crontab"
|
||||
"is_periodic", "interval", "crontab", "run_after_save"
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from orgs.mixins.api import OrgBulkModelViewSet
|
|||
from orgs.utils import current_org
|
||||
from perms import serializers
|
||||
from perms import models
|
||||
from perms.utils.user_permission import UserGrantedAssetsQueryUtils
|
||||
from perms.utils import AssetPermissionPermAssetUtil
|
||||
from assets.serializers import AccountSerializer
|
||||
|
||||
__all__ = [
|
||||
|
@ -95,8 +95,7 @@ class AssetPermissionAllAssetListApi(generics.ListAPIView):
|
|||
|
||||
def get_queryset(self):
|
||||
pk = self.kwargs.get("pk")
|
||||
query_utils = UserGrantedAssetsQueryUtils(None, asset_perm_ids=[pk])
|
||||
assets = query_utils.get_all_granted_assets()
|
||||
assets = AssetPermissionPermAssetUtil(perm_ids=[pk]).get_all_assets()
|
||||
return assets
|
||||
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ from assets.api.asset.asset import AssetFilterSet
|
|||
from perms import serializers
|
||||
from perms.pagination import AllPermedAssetPagination
|
||||
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 .mixin import (
|
||||
|
@ -43,21 +43,23 @@ class BaseUserPermedAssetsApi(SelfOrPKUserMixin, ListAPIView):
|
|||
def get_assets(self):
|
||||
return Asset.objects.none()
|
||||
|
||||
query_asset_util: UserPermAssetUtil
|
||||
|
||||
@lazyproperty
|
||||
def query_asset_util(self):
|
||||
return UserGrantedAssetsQueryUtils(self.user)
|
||||
return UserPermAssetUtil(self.user)
|
||||
|
||||
|
||||
class UserAllPermedAssetsApi(BaseUserPermedAssetsApi):
|
||||
pagination_class = AllPermedAssetPagination
|
||||
|
||||
def get_assets(self):
|
||||
return self.query_asset_util.get_all_granted_assets()
|
||||
return self.query_asset_util.get_all_assets()
|
||||
|
||||
|
||||
class UserDirectPermedAssetsApi(BaseUserPermedAssetsApi):
|
||||
def get_assets(self):
|
||||
return self.query_asset_util.get_direct_granted_assets()
|
||||
return self.query_asset_util.get_direct_assets()
|
||||
|
||||
|
||||
class UserFavoriteAssetsApi(BaseUserPermedAssetsApi):
|
||||
|
|
|
@ -7,7 +7,7 @@ from rest_framework.generics import ListAPIView
|
|||
from assets.models import Node
|
||||
from common.utils import get_logger, lazyproperty
|
||||
from perms import serializers
|
||||
from perms.utils.user_permission import UserGrantedNodesQueryUtils
|
||||
from perms.utils import UserPermNodeUtil
|
||||
from .mixin import SelfOrPKUserMixin
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
@ -32,7 +32,7 @@ class BaseUserPermedNodesApi(SelfOrPKUserMixin, ListAPIView):
|
|||
|
||||
@lazyproperty
|
||||
def query_node_util(self):
|
||||
return UserGrantedNodesQueryUtils(self.user)
|
||||
return UserPermNodeUtil(self.user)
|
||||
|
||||
|
||||
class UserAllPermedNodesApi(BaseUserPermedNodesApi):
|
||||
|
|
|
@ -3,23 +3,22 @@ from urllib.parse import parse_qsl
|
|||
|
||||
from django.conf import settings
|
||||
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.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.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.common import timeit
|
||||
from perms.hands import Node
|
||||
from perms.models import PermNode
|
||||
from perms.utils import PermAccountUtil
|
||||
from perms.utils.permission import AssetPermissionUtil
|
||||
from perms.utils.user_permission import (
|
||||
UserGrantedNodesQueryUtils, UserGrantedAssetsQueryUtils,
|
||||
)
|
||||
from perms.utils import PermAccountUtil, UserPermNodeUtil, AssetPermissionUtil
|
||||
from perms.utils import UserPermAssetUtil
|
||||
from .mixin import RebuildTreeMixin
|
||||
from ..mixin import SelfOrPKUserMixin
|
||||
|
||||
|
@ -52,13 +51,12 @@ class BaseUserNodeWithAssetAsTreeApi(
|
|||
|
||||
|
||||
class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
||||
query_node_util: UserGrantedNodesQueryUtils
|
||||
query_asset_util: UserGrantedAssetsQueryUtils
|
||||
query_node_util: UserPermNodeUtil
|
||||
query_asset_util: UserPermAssetUtil
|
||||
|
||||
def get_nodes_assets(self):
|
||||
perm_ids = AssetPermissionUtil().get_permissions_for_user(self.request.user, flat=True)
|
||||
self.query_node_util = UserGrantedNodesQueryUtils(self.request.user, perm_ids)
|
||||
self.query_asset_util = UserGrantedAssetsQueryUtils(self.request.user, perm_ids)
|
||||
self.query_node_util = UserPermNodeUtil(self.request.user)
|
||||
self.query_asset_util = UserPermAssetUtil(self.request.user)
|
||||
ung_nodes, ung_assets = self._get_nodes_assets_for_ungrouped()
|
||||
fav_nodes, fav_assets = self._get_nodes_assets_for_favorite()
|
||||
all_nodes, all_assets = self._get_nodes_assets_for_all()
|
||||
|
@ -87,9 +85,9 @@ class UserPermedNodesWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
|||
def _get_nodes_assets_for_all(self):
|
||||
nodes = self.query_node_util.get_whole_tree_nodes(with_special=False)
|
||||
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:
|
||||
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')
|
||||
return nodes, assets
|
||||
|
||||
|
@ -98,20 +96,21 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
|||
""" 用户授权的节点的子节点与资产树 """
|
||||
|
||||
def get_nodes_assets(self):
|
||||
nodes = PermNode.objects.none()
|
||||
assets = Asset.objects.none()
|
||||
query_node_util = UserGrantedNodesQueryUtils(self.user)
|
||||
query_asset_util = UserGrantedAssetsQueryUtils(self.user)
|
||||
query_node_util = UserPermNodeUtil(self.user)
|
||||
query_asset_util = UserPermAssetUtil(self.user)
|
||||
node_key = self.query_node_key
|
||||
if not node_key:
|
||||
nodes = query_node_util.get_top_level_nodes()
|
||||
assets = Asset.objects.none()
|
||||
elif node_key == PermNode.UNGROUPED_NODE_KEY:
|
||||
nodes = PermNode.objects.none()
|
||||
assets = query_asset_util.get_ungroup_assets()
|
||||
elif node_key == PermNode.FAVORITE_NODE_KEY:
|
||||
nodes = PermNode.objects.none()
|
||||
assets = query_asset_util.get_favorite_assets()
|
||||
else:
|
||||
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')
|
||||
return nodes, assets
|
||||
|
||||
|
@ -133,41 +132,48 @@ class UserPermedNodeChildrenWithAssetsAsTreeApi(BaseUserNodeWithAssetAsTreeApi):
|
|||
class UserGrantedK8sAsTreeApi(SelfOrPKUserMixin, ListAPIView):
|
||||
""" 用户授权的K8s树 """
|
||||
|
||||
@staticmethod
|
||||
def asset(asset_id):
|
||||
kwargs = {'id': asset_id, 'is_active': True}
|
||||
asset = get_object_or_404(Asset, **kwargs)
|
||||
return asset
|
||||
def get_token(self):
|
||||
token_id = self.request.query_params.get('token')
|
||||
token = get_object_or_404(ConnectionToken, pk=token_id)
|
||||
if token.is_expired:
|
||||
raise PermissionDenied('Token is expired')
|
||||
token.renewal()
|
||||
return token
|
||||
|
||||
def get_accounts(self, asset):
|
||||
def get_account_secret(self, token: ConnectionToken):
|
||||
util = PermAccountUtil()
|
||||
accounts = util.get_permed_accounts_for_user(self.user, asset)
|
||||
ignore_username = [Account.AliasAccount.INPUT, Account.AliasAccount.USER]
|
||||
accounts = filter(lambda x: x.username not in ignore_username, accounts)
|
||||
accounts = util.get_permed_accounts_for_user(self.user, token.asset)
|
||||
account_username = token.account
|
||||
accounts = filter(lambda x: x.username == account_username, 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):
|
||||
tree_id = request.query_params.get('tree_id')
|
||||
key = request.query_params.get('key', {})
|
||||
token = self.get_token()
|
||||
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 = []
|
||||
parent_info = dict(parse_qsl(key))
|
||||
account_username = parent_info.get('account')
|
||||
|
||||
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)
|
||||
k8s_tree_instance = KubernetesTree(asset, secret)
|
||||
if not any([namespace, pod]) and not key:
|
||||
asset_node = k8s_tree_instance.as_asset_tree_node()
|
||||
tree.append(asset_node)
|
||||
for account in accounts:
|
||||
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)
|
||||
tree.extend(k8s_tree_instance.async_tree_node(namespace, pod))
|
||||
return Response(data=tree)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from django_filters import rest_framework as filters
|
||||
from django.db.models import QuerySet, Q
|
||||
|
||||
from common.db.models import UnionQuerySet
|
||||
from common.drf.filters import BaseFilterSet
|
||||
from common.utils import get_object_or_none
|
||||
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 = list(inherit_all_node_ids)
|
||||
|
||||
qs1 = queryset.filter(assets__in=asset_ids).distinct()
|
||||
qs2 = queryset.filter(nodes__in=inherit_all_node_ids).distinct()
|
||||
|
||||
qs = UnionQuerySet(qs1, qs2)
|
||||
qs1_ids = queryset.filter(assets__in=asset_ids).distinct().values_list('id', flat=True)
|
||||
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 = queryset.filter(id__in=qs_ids)
|
||||
return qs
|
||||
|
||||
def filter_effective(self, queryset):
|
||||
|
|
|
@ -41,11 +41,6 @@ class Migration(migrations.Migration):
|
|||
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(
|
||||
model_name='userassetgrantedtreenoderelation',
|
||||
name='updated_by',
|
||||
|
|
|
@ -5,14 +5,14 @@ from django.db.models import Q
|
|||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from users.models import User
|
||||
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 OrgManager
|
||||
from common.utils import date_expired_default
|
||||
from common.utils.timezone import local_now
|
||||
|
||||
from perms.const import ActionChoices
|
||||
from users.models import User
|
||||
|
||||
__all__ = ['AssetPermission', 'ActionChoices']
|
||||
|
||||
|
@ -104,9 +104,10 @@ class AssetPermission(JMSOrgBaseModel):
|
|||
group_ids = self.user_groups.all().values_list('id', flat=True)
|
||||
user_ids = list(user_ids)
|
||||
group_ids = list(group_ids)
|
||||
qs1 = User.objects.filter(id__in=user_ids).distinct()
|
||||
qs2 = User.objects.filter(groups__id__in=group_ids).distinct()
|
||||
qs = UnionQuerySet(qs1, qs2)
|
||||
qs1_ids = User.objects.filter(id__in=user_ids).distinct().values_list('id', flat=True)
|
||||
qs2_ids = User.objects.filter(groups__id__in=group_ids).distinct().values_list('id', flat=True)
|
||||
qs_ids = list(qs1_ids) + list(qs2_ids)
|
||||
qs = User.objects.filter(id__in=qs_ids)
|
||||
return qs
|
||||
|
||||
def get_all_assets(self, flat=False):
|
||||
|
|
|
@ -16,12 +16,18 @@ class NodeFrom(TextChoices):
|
|||
class UserAssetGrantedTreeNodeRelation(FamilyMixin, JMSOrgBaseModel):
|
||||
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)
|
||||
node = models.ForeignKey('assets.Node', default=None, on_delete=models.CASCADE,
|
||||
db_constraint=False, null=False, related_name='granted_node_rels')
|
||||
node = models.ForeignKey(
|
||||
'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_parent_key = models.CharField(max_length=64, default='', verbose_name=_('Parent key'),
|
||||
db_index=True)
|
||||
node_parent_key = models.CharField(
|
||||
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_assets_amount = models.IntegerField(default=0)
|
||||
comment = ''
|
||||
|
@ -35,15 +41,14 @@ class UserAssetGrantedTreeNodeRelation(FamilyMixin, JMSOrgBaseModel):
|
|||
return self.node_parent_key
|
||||
|
||||
@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_rel_nodes = cls.objects.filter(user=user, node_key__in=ancestor_keys)
|
||||
|
||||
for rel_node in ancestor_rel_nodes:
|
||||
if rel_node.key == key:
|
||||
return rel_node.node_from, rel_node
|
||||
if rel_node.node_from == cls.NodeFrom.granted:
|
||||
return cls.NodeFrom.granted, None
|
||||
ancestor_nodes = cls.objects.filter(user=user, node_key__in=ancestor_keys)
|
||||
for node in ancestor_nodes:
|
||||
if node.key == key:
|
||||
return node.node_from, node
|
||||
if node.node_from == cls.NodeFrom.granted:
|
||||
return node.node_from, None
|
||||
return '', None
|
||||
|
||||
|
||||
|
@ -68,6 +73,9 @@ class PermNode(Node):
|
|||
'node_from': F('granted_node_rels__node_from')
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.name}'
|
||||
|
||||
def use_granted_assets_amount(self):
|
||||
self.assets_amount = self.granted_assets_amount
|
||||
|
||||
|
@ -90,15 +98,16 @@ class PermNode(Node):
|
|||
node.assets_amount = assets_amount
|
||||
return node
|
||||
|
||||
def get_granted_status(self, user):
|
||||
status, rel_node = UserAssetGrantedTreeNodeRelation.get_node_granted_status(user, self.key)
|
||||
self.node_from = status
|
||||
if rel_node:
|
||||
self.granted_assets_amount = rel_node.node_assets_amount
|
||||
return status
|
||||
def compute_node_from_and_assets_amount(self, user):
|
||||
node_from, node = UserAssetGrantedTreeNodeRelation.get_node_from_with_node(
|
||||
user, self.key
|
||||
)
|
||||
self.node_from = node_from
|
||||
if node:
|
||||
self.granted_assets_amount = node.node_assets_amount
|
||||
|
||||
def save(self):
|
||||
# 这是个只读 Model
|
||||
""" 这是个只读 Model """
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from .permission import *
|
||||
from .user_permission import *
|
||||
from .account import *
|
||||
from .user_perm_tree import *
|
||||
from .user_perm import *
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -153,7 +153,7 @@ class ConnectMethodUtil:
|
|||
protocols = {
|
||||
TerminalType.koko: {
|
||||
'web_methods': [WebMethod.web_cli, WebMethod.web_sftp],
|
||||
'listen': [Protocol.ssh, Protocol.http],
|
||||
'listen': [Protocol.http],
|
||||
'support': [
|
||||
Protocol.ssh, Protocol.telnet,
|
||||
Protocol.mysql, Protocol.postgresql,
|
||||
|
|
Loading…
Reference in New Issue