perf: 干掉 applications

pull/8873/head
ibuler 2022-08-09 15:42:06 +08:00
parent 05e2f8aaf6
commit 3011b18eaa
59 changed files with 2 additions and 1873 deletions

View File

@ -1,3 +0,0 @@
from django.contrib import admin
# Register your models here.

View File

@ -1,3 +0,0 @@
from .application import *
from .account import *
from .remote_app import *

View File

@ -1,66 +0,0 @@
# coding: utf-8
#
from django_filters import rest_framework as filters
from django.db.models import Q
from common.drf.filters import BaseFilterSet
from common.drf.api import JMSBulkModelViewSet
from common.mixins import RecordViewLogMixin
from common.permissions import UserConfirmation
from authentication.const import ConfirmType
from rbac.permissions import RBACPermission
from assets.models import SystemUser
from ..models import Account
from .. import serializers
class AccountFilterSet(BaseFilterSet):
username = filters.CharFilter(method='do_nothing')
type = filters.CharFilter(field_name='type', lookup_expr='exact')
category = filters.CharFilter(field_name='category', lookup_expr='exact')
app_display = filters.CharFilter(field_name='app_display', lookup_expr='exact')
class Meta:
model = Account
fields = ['app', 'systemuser']
@property
def qs(self):
qs = super().qs
qs = self.filter_username(qs)
return qs
def filter_username(self, qs):
username = self.get_query_param('username')
if not username:
return qs
q = Q(username=username) | Q(systemuser__username=username)
qs = qs.filter(q).distinct()
return qs
class ApplicationAccountViewSet(JMSBulkModelViewSet):
model = Account
search_fields = ['username', 'app_display']
filterset_class = AccountFilterSet
filterset_fields = ['username', 'app_display', 'type', 'category', 'app']
serializer_class = serializers.AppAccountSerializer
def get_queryset(self):
queryset = Account.get_queryset()
return queryset
class SystemUserAppRelationViewSet(ApplicationAccountViewSet):
perm_model = SystemUser
class ApplicationAccountSecretViewSet(RecordViewLogMixin, ApplicationAccountViewSet):
serializer_class = serializers.AppAccountSecretSerializer
permission_classes = [RBACPermission, UserConfirmation.require(ConfirmType.MFA)]
http_method_names = ['get', 'options']
rbac_perms = {
'retrieve': 'applications.view_applicationaccountsecret',
'list': 'applications.view_applicationaccountsecret',
}

View File

@ -1,39 +0,0 @@
# coding: utf-8
#
from orgs.mixins.api import OrgBulkModelViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from common.tree import TreeNodeSerializer
from common.mixins.api import SuggestionMixin
from .. import serializers
from ..models import Application
__all__ = ['ApplicationViewSet']
class ApplicationViewSet(SuggestionMixin, OrgBulkModelViewSet):
model = Application
filterset_fields = {
'name': ['exact'],
'category': ['exact', 'in'],
'type': ['exact', 'in'],
}
search_fields = ('name', 'type', 'category')
serializer_classes = {
'default': serializers.AppSerializer,
'get_tree': TreeNodeSerializer,
'suggestion': serializers.MiniAppSerializer
}
rbac_perms = {
'get_tree': 'applications.view_application',
'match': 'applications.match_application'
}
@action(methods=['GET'], detail=False, url_path='tree')
def get_tree(self, request, *args, **kwargs):
show_count = request.query_params.get('show_count', '1') == '1'
queryset = self.filter_queryset(self.get_queryset())
tree_nodes = Application.create_tree_nodes(queryset, show_count=show_count)
serializer = self.get_serializer(tree_nodes, many=True)
return Response(serializer.data)

View File

@ -1,16 +0,0 @@
# coding: utf-8
#
from orgs.mixins import generics
from .. import models
from ..serializers import RemoteAppConnectionInfoSerializer
__all__ = [
'RemoteAppConnectionInfoApi',
]
class RemoteAppConnectionInfoApi(generics.RetrieveAPIView):
model = models.Application
serializer_class = RemoteAppConnectionInfoSerializer

View File

@ -1,14 +0,0 @@
"""
jumpserver.__app__.hands.py
~~~~~~~~~~~~~~~~~
This app depends other apps api, function .. should be import or write mack here.
Other module of this app shouldn't connect with other app.
:copyright: (c) 2014-2018 by JumpServer Team.
:license: GPL v2, see LICENSE for more details.
"""
from users.models import User, UserGroup

View File

@ -5,7 +5,6 @@ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from common.tree import TreeNode from common.tree import TreeNode
from ..utils import KubernetesTree
from .. import const from .. import const

View File

@ -1,9 +0,0 @@
from rest_framework import permissions
__all__ = ['IsRemoteApp']
class IsRemoteApp(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj.category_remote_app

View File

@ -1,2 +0,0 @@
from .application import *
from .remote_app import *

View File

@ -1,168 +0,0 @@
# coding: utf-8
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from assets.serializers.base import AuthSerializerMixin
from common.drf.serializers import MethodSerializer, SecretReadableMixin
from .attrs import (
category_serializer_classes_mapping,
type_serializer_classes_mapping,
type_secret_serializer_classes_mapping
)
from .. import models
from .. import const
__all__ = [
'AppSerializer', 'MiniAppSerializer', 'AppSerializerMixin',
'AppAccountSerializer', 'AppAccountSecretSerializer'
]
class AppSerializerMixin(serializers.Serializer):
attrs = MethodSerializer()
@property
def app(self):
if isinstance(self.instance, models.Application):
instance = self.instance
else:
instance = None
return instance
def get_attrs_serializer(self):
default_serializer = serializers.Serializer(read_only=True)
instance = self.app
if instance:
_type = instance.type
_category = instance.category
else:
_type = self.context['request'].query_params.get('type')
_category = self.context['request'].query_params.get('category')
if _type:
if isinstance(self, AppAccountSecretSerializer):
serializer_class = type_secret_serializer_classes_mapping.get(_type)
else:
serializer_class = type_serializer_classes_mapping.get(_type)
elif _category:
serializer_class = category_serializer_classes_mapping.get(_category)
else:
serializer_class = default_serializer
if not serializer_class:
serializer_class = default_serializer
if isinstance(serializer_class, type):
serializer = serializer_class()
else:
serializer = serializer_class
return serializer
def create(self, validated_data):
return super().create(validated_data)
def update(self, instance, validated_data):
return super().update(instance, validated_data)
class AppSerializer(AppSerializerMixin, BulkOrgResourceModelSerializer):
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category display'))
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
class Meta:
model = models.Application
fields_mini = ['id', 'name']
fields_small = fields_mini + [
'category', 'category_display', 'type', 'type_display',
'attrs', 'date_created', 'date_updated', 'created_by', 'comment'
]
fields_fk = ['domain']
fields = fields_small + fields_fk
read_only_fields = [
'created_by', 'date_created', 'date_updated', 'get_type_display',
]
def validate_attrs(self, attrs):
_attrs = self.instance.attrs if self.instance else {}
_attrs.update(attrs)
return _attrs
class MiniAppSerializer(serializers.ModelSerializer):
class Meta:
model = models.Application
fields = AppSerializer.Meta.fields_mini
class AppAccountSerializer(AppSerializerMixin, AuthSerializerMixin, BulkOrgResourceModelSerializer):
category = serializers.ChoiceField(label=_('Category'), choices=const.AppCategory.choices, read_only=True)
category_display = serializers.SerializerMethodField(label=_('Category display'))
type = serializers.ChoiceField(label=_('Type'), choices=const.AppType.choices, read_only=True)
type_display = serializers.SerializerMethodField(label=_('Type display'))
date_created = serializers.DateTimeField(label=_('Date created'), format="%Y/%m/%d %H:%M:%S", read_only=True)
date_updated = serializers.DateTimeField(label=_('Date updated'), format="%Y/%m/%d %H:%M:%S", read_only=True)
category_mapper = dict(const.AppCategory.choices)
type_mapper = dict(const.AppType.choices)
class Meta:
model = models.Account
fields_mini = ['id', 'username', 'version']
fields_write_only = ['password', 'private_key', 'public_key', 'passphrase']
fields_other = ['date_created', 'date_updated']
fields_fk = ['systemuser', 'systemuser_display', 'app', 'app_display']
fields = fields_mini + fields_fk + fields_write_only + fields_other + [
'type', 'type_display', 'category', 'category_display', 'attrs'
]
extra_kwargs = {
'username': {'default': '', 'required': False},
'password': {'write_only': True},
'app_display': {'label': _('Application display')},
'systemuser_display': {'label': _('System User')},
'account': {'label': _('account')}
}
use_model_bulk_create = True
model_bulk_create_kwargs = {
'ignore_conflicts': True
}
@property
def app(self):
if isinstance(self.instance, models.Account):
instance = self.instance.app
else:
instance = None
return instance
def get_category_display(self, obj):
return self.category_mapper.get(obj.category)
def get_type_display(self, obj):
return self.type_mapper.get(obj.type)
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related('systemuser', 'app')
return queryset
def to_representation(self, instance):
instance.load_auth()
return super().to_representation(instance)
class AppAccountSecretSerializer(SecretReadableMixin, AppAccountSerializer):
class Meta(AppAccountSerializer.Meta):
fields_backup = [
'id', 'app_display', 'attrs', 'username', 'password', 'private_key',
'public_key', 'date_created', 'date_updated', 'version'
]
extra_kwargs = {
'password': {'write_only': False},
'private_key': {'write_only': False},
'public_key': {'write_only': False},
'app_display': {'label': _('Application display')},
'systemuser_display': {'label': _('System User')}
}

View File

@ -1 +0,0 @@
from .attrs import *

View File

@ -1,3 +0,0 @@
from .remote_app import *
from .db import *
from .cloud import *

View File

@ -1,8 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
__all__ = ['CloudSerializer']
class CloudSerializer(serializers.Serializer):
cluster = serializers.CharField(max_length=1024, label=_('Cluster'), allow_null=True)

View File

@ -1,15 +0,0 @@
# coding: utf-8
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
__all__ = ['DBSerializer']
class DBSerializer(serializers.Serializer):
host = serializers.CharField(max_length=128, label=_('Host'), allow_null=True)
port = serializers.IntegerField(label=_('Port'), allow_null=True)
database = serializers.CharField(
max_length=128, required=True, allow_null=True, label=_('Database')
)

View File

@ -1,60 +0,0 @@
# coding: utf-8
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
from common.utils import get_logger, is_uuid, get_object_or_none
from assets.models import Asset
logger = get_logger(__file__)
__all__ = ['RemoteAppSerializer']
class ExistAssetPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def to_internal_value(self, data):
instance = super().to_internal_value(data)
return str(instance.id)
def to_representation(self, _id):
# _id 是 instance.id
if self.pk_field is not None:
return self.pk_field.to_representation(_id)
# 解决删除资产后远程应用更新页面会显示资产ID的问题
asset = get_object_or_none(Asset, id=_id)
if not asset:
return None
return _id
class RemoteAppSerializer(serializers.Serializer):
asset_info = serializers.SerializerMethodField()
asset = ExistAssetPrimaryKeyRelatedField(
queryset=Asset.objects, required=True, label=_("Asset"), allow_null=True
)
path = serializers.CharField(
max_length=128, label=_('Application path'), allow_null=True
)
def validate_asset(self, asset):
if not asset:
raise serializers.ValidationError(_('This field is required.'))
return asset
@staticmethod
def get_asset_info(obj):
asset_id = obj.get('asset')
if not asset_id or not is_uuid(asset_id):
return {}
try:
asset = Asset.objects.get(id=str(asset_id))
except ObjectDoesNotExist as e:
logger.error(e)
return {}
if not asset:
return {}
asset_info = {'id': str(asset.id), 'hostname': asset.hostname}
return asset_info

View File

@ -1,15 +0,0 @@
from .mysql import *
from .mariadb import *
from .oracle import *
from .pgsql import *
from .sqlserver import *
from .redis import *
from .mongodb import *
from .chrome import *
from .mysql_workbench import *
from .vmware_client import *
from .custom import *
from .k8s import *

View File

@ -1,34 +0,0 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['ChromeSerializer', 'ChromeSecretSerializer']
class ChromeSerializer(RemoteAppSerializer):
CHROME_PATH = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'
path = serializers.CharField(
max_length=128, label=_('Application path'), default=CHROME_PATH, allow_null=True,
)
chrome_target = serializers.CharField(
max_length=128, allow_blank=True, required=False,
label=_('Target URL'), allow_null=True,
)
chrome_username = serializers.CharField(
max_length=128, allow_blank=True, required=False,
label=_('Chrome username'), allow_null=True,
)
chrome_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Chrome password'), allow_null=True
)
class ChromeSecretSerializer(ChromeSerializer):
chrome_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Chrome password'), allow_null=True, write_only=False
)

View File

@ -1,33 +0,0 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['CustomSerializer', 'CustomSecretSerializer']
class CustomSerializer(RemoteAppSerializer):
custom_cmdline = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Operating parameter'),
allow_null=True,
)
custom_target = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Target url'),
allow_null=True,
)
custom_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Custom Username'),
allow_null=True,
)
custom_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Custom password'), allow_null=True,
)
class CustomSecretSerializer(RemoteAppSerializer):
custom_password = EncryptedField(
max_length=128, allow_blank=True, required=False, write_only=False,
label=_('Custom password'), allow_null=True,
)

View File

@ -1,7 +0,0 @@
from ..application_category import CloudSerializer
__all__ = ['K8SSerializer']
class K8SSerializer(CloudSerializer):
pass

View File

@ -1,7 +0,0 @@
from .mysql import MySQLSerializer
__all__ = ['MariaDBSerializer']
class MariaDBSerializer(MySQLSerializer):
pass

View File

@ -1,11 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['MongoDBSerializer']
class MongoDBSerializer(DBSerializer):
port = serializers.IntegerField(default=27017, label=_('Port'), allow_null=True)

View File

@ -1,11 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['MySQLSerializer']
class MySQLSerializer(DBSerializer):
port = serializers.IntegerField(default=3306, label=_('Port'), allow_null=True)

View File

@ -1,43 +0,0 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['MySQLWorkbenchSerializer', 'MySQLWorkbenchSecretSerializer']
class MySQLWorkbenchSerializer(RemoteAppSerializer):
MYSQL_WORKBENCH_PATH = 'C:\Program Files\MySQL\MySQL Workbench 8.0 CE\MySQLWorkbench.exe'
path = serializers.CharField(
max_length=128, label=_('Application path'), default=MYSQL_WORKBENCH_PATH,
allow_null=True,
)
mysql_workbench_ip = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('IP'),
allow_null=True,
)
mysql_workbench_port = serializers.IntegerField(
required=False, label=_('Port'),
allow_null=True,
)
mysql_workbench_name = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Database'),
allow_null=True,
)
mysql_workbench_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Mysql workbench username'),
allow_null=True,
)
mysql_workbench_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Mysql workbench password'), allow_null=True,
)
class MySQLWorkbenchSecretSerializer(RemoteAppSerializer):
mysql_workbench_password = EncryptedField(
max_length=128, allow_blank=True, required=False, write_only=False,
label=_('Mysql workbench password'), allow_null=True,
)

View File

@ -1,10 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['OracleSerializer']
class OracleSerializer(DBSerializer):
port = serializers.IntegerField(default=1521, label=_('Port'), allow_null=True)

View File

@ -1,10 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['PostgreSerializer']
class PostgreSerializer(DBSerializer):
port = serializers.IntegerField(default=5432, label=_('Port'), allow_null=True)

View File

@ -1,11 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['RedisSerializer']
class RedisSerializer(DBSerializer):
port = serializers.IntegerField(default=6379, label=_('Port'), allow_null=True)

View File

@ -1,11 +0,0 @@
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from ..application_category import DBSerializer
__all__ = ['SQLServerSerializer']
class SQLServerSerializer(DBSerializer):
port = serializers.IntegerField(default=1433, label=_('Port'), allow_null=True)

View File

@ -1,39 +0,0 @@
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
from common.drf.fields import EncryptedField
from ..application_category import RemoteAppSerializer
__all__ = ['VMwareClientSerializer', 'VMwareClientSecretSerializer']
class VMwareClientSerializer(RemoteAppSerializer):
PATH = r'''
C:\Program Files (x86)\VMware\Infrastructure\Virtual Infrastructure Client\Launcher\VpxClient
.exe
'''
VMWARE_CLIENT_PATH = ''.join(PATH.split())
path = serializers.CharField(
max_length=128, label=_('Application path'), default=VMWARE_CLIENT_PATH,
allow_null=True
)
vmware_target = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Target URL'),
allow_null=True
)
vmware_username = serializers.CharField(
max_length=128, allow_blank=True, required=False, label=_('Vmware username'),
allow_null=True
)
vmware_password = EncryptedField(
max_length=128, allow_blank=True, required=False,
label=_('Vmware password'), allow_null=True
)
class VMwareClientSecretSerializer(RemoteAppSerializer):
vmware_password = EncryptedField(
max_length=128, allow_blank=True, required=False, write_only=False,
label=_('Vmware password'), allow_null=True
)

View File

@ -1,62 +0,0 @@
import copy
from applications import const
from . import application_category, application_type
__all__ = [
'category_serializer_classes_mapping',
'type_serializer_classes_mapping',
'get_serializer_class_by_application_type',
'type_secret_serializer_classes_mapping'
]
# define `attrs` field `category serializers mapping`
# ---------------------------------------------------
category_serializer_classes_mapping = {
const.AppCategory.db.value: application_category.DBSerializer,
const.AppCategory.remote_app.value: application_category.RemoteAppSerializer,
const.AppCategory.cloud.value: application_category.CloudSerializer,
}
# define `attrs` field `type serializers mapping`
# -----------------------------------------------
type_serializer_classes_mapping = {
# db
const.AppType.mysql.value: application_type.MySQLSerializer,
const.AppType.mariadb.value: application_type.MariaDBSerializer,
const.AppType.oracle.value: application_type.OracleSerializer,
const.AppType.pgsql.value: application_type.PostgreSerializer,
const.AppType.sqlserver.value: application_type.SQLServerSerializer,
const.AppType.redis.value: application_type.RedisSerializer,
const.AppType.mongodb.value: application_type.MongoDBSerializer,
# cloud
const.AppType.k8s.value: application_type.K8SSerializer
}
remote_app_serializer_classes_mapping = {
# remote-app
const.AppType.chrome.value: application_type.ChromeSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSerializer,
const.AppType.custom.value: application_type.CustomSerializer
}
type_serializer_classes_mapping.update(remote_app_serializer_classes_mapping)
remote_app_secret_serializer_classes_mapping = {
# remote-app
const.AppType.chrome.value: application_type.ChromeSecretSerializer,
const.AppType.mysql_workbench.value: application_type.MySQLWorkbenchSecretSerializer,
const.AppType.vmware_client.value: application_type.VMwareClientSecretSerializer,
const.AppType.custom.value: application_type.CustomSecretSerializer
}
type_secret_serializer_classes_mapping = copy.deepcopy(type_serializer_classes_mapping)
type_secret_serializer_classes_mapping.update(remote_app_secret_serializer_classes_mapping)
def get_serializer_class_by_application_type(_application_type):
return type_serializer_classes_mapping.get(_application_type)

View File

@ -1,31 +0,0 @@
# coding: utf-8
#
from rest_framework import serializers
from common.utils import get_logger
from ..models import Application
logger = get_logger(__file__)
__all__ = ['RemoteAppConnectionInfoSerializer']
class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer):
parameter_remote_app = serializers.SerializerMethodField()
asset = serializers.SerializerMethodField()
class Meta:
model = Application
fields = [
'id', 'name', 'asset', 'parameter_remote_app',
]
read_only_fields = ['parameter_remote_app']
@staticmethod
def get_asset(obj):
return obj.attrs.get('asset')
@staticmethod
def get_parameter_remote_app(obj):
return obj.get_rdp_remote_app_setting()

View File

@ -1,3 +0,0 @@
from django.test import TestCase
# Create your tests here.

View File

@ -1,7 +0,0 @@
# coding: utf-8
#
__all__ = [
]

View File

@ -1,25 +0,0 @@
# coding:utf-8
#
from django.urls import path
from rest_framework_bulk.routes import BulkRouter
from .. import api
app_name = 'applications'
router = BulkRouter()
router.register(r'applications', api.ApplicationViewSet, 'application')
router.register(r'accounts', api.ApplicationAccountViewSet, 'application-account')
router.register(r'system-users-apps-relations', api.SystemUserAppRelationViewSet, 'system-users-apps-relation')
router.register(r'account-secrets', api.ApplicationAccountSecretViewSet, 'application-account-secret')
urlpatterns = [
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
# path('accounts/', api.ApplicationAccountViewSet.as_view(), name='application-account'),
# path('account-secrets/', api.ApplicationAccountSecretViewSet.as_view(), name='application-account-secret')
]
urlpatterns += router.urls

View File

@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
#
from .kubernetes_util import *

View File

@ -1,186 +0,0 @@
# -*- coding: utf-8 -*-
from urllib3.exceptions import MaxRetryError
from urllib.parse import urlencode
from kubernetes.client import api_client
from kubernetes.client.api import core_v1_api
from kubernetes import client
from kubernetes.client.exceptions import ApiException
from rest_framework.generics import get_object_or_404
from common.utils import get_logger
from common.tree import TreeNode
from assets.models import SystemUser
from .. import const
logger = get_logger(__file__)
class KubernetesClient:
def __init__(self, url, token):
self.url = url
self.token = token
def get_api(self):
configuration = client.Configuration()
configuration.host = self.url
configuration.verify_ssl = False
configuration.api_key = {"authorization": "Bearer " + self.token}
c = api_client.ApiClient(configuration=configuration)
api = core_v1_api.CoreV1Api(c)
return api
def get_namespace_list(self):
api = self.get_api()
namespace_list = []
for ns in api.list_namespace().items:
namespace_list.append(ns.metadata.name)
return namespace_list
def get_services(self):
api = self.get_api()
ret = api.list_service_for_all_namespaces(watch=False)
for i in ret.items:
print("%s \t%s \t%s \t%s \t%s \n" % (
i.kind, i.metadata.namespace, i.metadata.name, i.spec.cluster_ip, i.spec.ports))
def get_pod_info(self, namespace, pod):
api = self.get_api()
resp = api.read_namespaced_pod(namespace=namespace, name=pod)
return resp
def get_pod_logs(self, namespace, pod):
api = self.get_api()
log_content = api.read_namespaced_pod_log(pod, namespace, pretty=True, tail_lines=200)
return log_content
def get_pods(self):
api = self.get_api()
try:
ret = api.list_pod_for_all_namespaces(watch=False, _request_timeout=(3, 3))
except MaxRetryError:
logger.warning('Kubernetes connection timed out')
return
except ApiException as e:
if e.status == 401:
logger.warning('Kubernetes User not authenticated')
else:
logger.warning(e)
return
data = {}
for i in ret.items:
namespace = i.metadata.namespace
pod_info = {
'pod_name': i.metadata.name,
'containers': [j.name for j in i.spec.containers]
}
if namespace in data:
data[namespace].append(pod_info)
else:
data[namespace] = [pod_info, ]
return data
@staticmethod
def get_kubernetes_data(app_id, system_user_id):
from ..models import Application
app = get_object_or_404(Application, id=app_id)
system_user = get_object_or_404(SystemUser, id=system_user_id)
k8s = KubernetesClient(app.attrs['cluster'], system_user.token)
return k8s.get_pods()
class KubernetesTree:
def __init__(self, tree_id):
self.tree_id = tree_id
def as_tree_node(self, app):
pid = app.create_app_tree_pid(self.tree_id)
app_id = str(app.id)
parent_info = {'app_id': app_id}
node = self.create_tree_node(
app_id, pid, app.name, 'k8s', parent_info
)
return node
def as_system_user_tree_node(self, system_user, parent_info):
from ..models import ApplicationTreeNodeMixin
system_user_id = str(system_user.id)
username = system_user.username
username = username if username else '*'
name = f'{system_user.name}({username})'
pid = urlencode({'app_id': self.tree_id})
i = ApplicationTreeNodeMixin.create_tree_id(pid, 'system_user_id', system_user_id)
parent_info.update({'system_user_id': system_user_id})
node = self.create_tree_node(
i, pid, name, 'system_user', parent_info, icon='user-tie'
)
return node
def as_namespace_pod_tree_node(self, name, meta, type, counts=0, is_container=False):
from ..models import ApplicationTreeNodeMixin
i = ApplicationTreeNodeMixin.create_tree_id(self.tree_id, type, name)
meta.update({type: name})
name = name if is_container else f'{name}({counts})'
node = self.create_tree_node(
i, self.tree_id, name, type, meta, icon='cloud', is_container=is_container
)
return node
@staticmethod
def create_tree_node(id_, pid, name, identity, parent_info, icon='', is_container=False):
node = TreeNode(**{
'id': id_,
'name': name,
'title': name,
'pId': pid,
'isParent': not is_container,
'open': False,
'iconSkin': icon,
'parentInfo': urlencode(parent_info),
'meta': {
'type': 'application',
'data': {
'category': const.AppCategory.cloud,
'type': const.AppType.k8s,
'identity': identity
}
}
})
return node
def async_tree_node(self, parent_info):
pod_name = parent_info.get('pod')
app_id = parent_info.get('app_id')
namespace = parent_info.get('namespace')
system_user_id = parent_info.get('system_user_id')
tree_nodes = []
data = KubernetesClient.get_kubernetes_data(app_id, system_user_id)
if not data:
return tree_nodes
if pod_name:
for container in next(
filter(
lambda x: x['pod_name'] == pod_name, data[namespace]
)
)['containers']:
container_node = self.as_namespace_pod_tree_node(
container, parent_info, 'container', is_container=True
)
tree_nodes.append(container_node)
elif namespace:
for pod in data[namespace]:
pod_nodes = self.as_namespace_pod_tree_node(
pod['pod_name'], parent_info, 'pod', len(pod['containers'])
)
tree_nodes.append(pod_nodes)
elif system_user_id:
for namespace, pods in data.items():
namespace_node = self.as_namespace_pod_tree_node(
namespace, parent_info, 'namespace', len(pods)
)
tree_nodes.append(namespace_node)
return tree_nodes

View File

@ -5,7 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from common.drf.serializers import JMSWritableNestedModelSerializer from common.drf.serializers import JMSWritableNestedModelSerializer
from orgs.mixins.serializers import OrgResourceModelSerializerMixin from orgs.mixins.serializers import OrgResourceModelSerializerMixin
from ...models import Asset, Node, Platform, Protocol, Label, Domain from ...models import Asset, Node, Platform, Protocol, Label, Domain, Account
from ..mixin import CategoryDisplayMixin from ..mixin import CategoryDisplayMixin
from ..account import AccountSerializer from ..account import AccountSerializer
@ -59,7 +59,7 @@ class AssetSerializer(CategoryDisplayMixin,
OrgResourceModelSerializerMixin): OrgResourceModelSerializerMixin):
domain = AssetDomainSerializer(required=False) domain = AssetDomainSerializer(required=False)
nodes_display = serializers.ListField( nodes_display = serializers.ListField(
child=serializers.CharField(), label=_('Nodes name'), required=False child=serializers.CharField(), label=_('Nodes name'), required=False,
) )
labels = AssetLabelSerializer(many=True, required=False) labels = AssetLabelSerializer(many=True, required=False)
nodes = AssetNodesSerializer(many=True, required=False) nodes = AssetNodesSerializer(many=True, required=False)
@ -105,7 +105,6 @@ class AssetSerializer(CategoryDisplayMixin,
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def validate_type(self, value): def validate_type(self, value):
print(self.initial_data)
return value return value
def validate_category(self, value): def validate_category(self, value):

View File

@ -21,7 +21,6 @@ api_v1 = [
path('settings/', include('settings.urls.api_urls', namespace='api-settings')), path('settings/', include('settings.urls.api_urls', namespace='api-settings')),
path('authentication/', include('authentication.urls.api_urls', namespace='api-auth')), path('authentication/', include('authentication.urls.api_urls', namespace='api-auth')),
path('common/', include('common.urls.api_urls', namespace='api-common')), path('common/', include('common.urls.api_urls', namespace='api-common')),
path('applications/', include('applications.urls.api_urls', namespace='api-applications')),
path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')), path('tickets/', include('tickets.urls.api_urls', namespace='api-tickets')),
path('acls/', include('acls.urls.api_urls', namespace='api-acls')), path('acls/', include('acls.urls.api_urls', namespace='api-acls')),
path('notifications/', include('notifications.urls.api_urls', namespace='api-notifications')), path('notifications/', include('notifications.urls.api_urls', namespace='api-notifications')),

View File

@ -2,5 +2,4 @@
# #
from .asset import * from .asset import *
from .application import *
from .system_user_permission import * from .system_user_permission import *

View File

@ -1,4 +0,0 @@
from .user_permission import *
from .application_permission import *
from .application_permission_relation import *
from .user_group_permission import *

View File

@ -1,55 +0,0 @@
# -*- coding: utf-8 -*-
#
from applications.models import Application
from perms.models import ApplicationPermission
from perms import serializers
from ..base import BasePermissionViewSet
class ApplicationPermissionViewSet(BasePermissionViewSet):
"""
应用授权列表的增删改查API
"""
model = ApplicationPermission
serializer_class = serializers.ApplicationPermissionSerializer
filterset_fields = {
'name': ['exact'],
'category': ['exact'],
'type': ['exact', 'in'],
'from_ticket': ['exact']
}
search_fields = ['name', 'category', 'type']
custom_filter_fields = BasePermissionViewSet.custom_filter_fields + [
'application_id', 'application', 'app', 'app_name'
]
ordering_fields = ('name',)
ordering = ('name', )
def get_queryset(self):
queryset = super().get_queryset().prefetch_related(
"applications", "users", "user_groups", "system_users"
)
return queryset
def filter_application(self, queryset):
app_id = self.request.query_params.get('application_id') or \
self.request.query_params.get('app')
app_name = self.request.query_params.get('application') or \
self.request.query_params.get('app_name')
if app_id:
applications = Application.objects.filter(pk=app_id)
elif app_name:
applications = Application.objects.filter(name=app_name)
else:
return queryset
if not applications:
return queryset.none()
queryset = queryset.filter(applications__in=applications)
return queryset
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = self.filter_application(queryset)
return queryset

View File

@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
#
from rest_framework import generics
from django.db.models import F, Value
from django.db.models.functions import Concat
from django.shortcuts import get_object_or_404
from applications.models import Application
from orgs.mixins.api import OrgRelationMixin
from orgs.mixins.api import OrgBulkModelViewSet
from orgs.utils import current_org
from perms import serializers
from perms import models
__all__ = [
'ApplicationPermissionUserRelationViewSet',
'ApplicationPermissionUserGroupRelationViewSet',
'ApplicationPermissionApplicationRelationViewSet',
'ApplicationPermissionSystemUserRelationViewSet',
'ApplicationPermissionAllApplicationListApi',
'ApplicationPermissionAllUserListApi',
]
class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet):
perm_model = models.ApplicationPermission
def get_queryset(self):
queryset = super().get_queryset()
org_id = current_org.org_id()
if org_id is not None:
queryset = queryset.filter(applicationpermission__org_id=org_id)
queryset = queryset.annotate(applicationpermission_display=F('applicationpermission__name'))
return queryset
class ApplicationPermissionUserRelationViewSet(RelationMixin):
serializer_class = serializers.ApplicationPermissionUserRelationSerializer
m2m_field = models.ApplicationPermission.users.field
filterset_fields = [
'id', "user", "applicationpermission",
]
search_fields = ("user__name", "user__username", "applicationpermission__name")
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(user_display=F('user__name'))
return queryset
class ApplicationPermissionUserGroupRelationViewSet(RelationMixin):
serializer_class = serializers.ApplicationPermissionUserGroupRelationSerializer
m2m_field = models.ApplicationPermission.user_groups.field
filterset_fields = [
'id', "usergroup", "applicationpermission"
]
search_fields = ["usergroup__name", "applicationpermission__name"]
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(usergroup_display=F('usergroup__name'))
return queryset
class ApplicationPermissionApplicationRelationViewSet(RelationMixin):
serializer_class = serializers.ApplicationPermissionApplicationRelationSerializer
m2m_field = models.ApplicationPermission.applications.field
filterset_fields = [
'id', 'application', 'applicationpermission',
]
search_fields = ["id", "application__name", "applicationpermission__name"]
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(application_display=F('application__name'))
return queryset
class ApplicationPermissionSystemUserRelationViewSet(RelationMixin):
serializer_class = serializers.ApplicationPermissionSystemUserRelationSerializer
m2m_field = models.ApplicationPermission.system_users.field
filterset_fields = [
'id', 'systemuser', 'applicationpermission',
]
search_fields = [
"applicactionpermission__name", "systemuser__name", "systemuser__username"
]
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.annotate(
systemuser_display=Concat(
F('systemuser__name'), Value('('), F('systemuser__username'),
Value(')')
))
return queryset
class ApplicationPermissionAllApplicationListApi(generics.ListAPIView):
serializer_class = serializers.ApplicationPermissionAllApplicationSerializer
only_fields = serializers.ApplicationPermissionAllApplicationSerializer.Meta.only_fields
filterset_fields = ('name',)
search_fields = filterset_fields
def get_queryset(self):
pk = self.kwargs.get('pk')
perm = get_object_or_404(models.ApplicationPermission, pk=pk)
applications = Application.objects.filter(granted_by_permissions=perm) \
.only(*self.only_fields).distinct()
return applications
class ApplicationPermissionAllUserListApi(generics.ListAPIView):
serializer_class = serializers.ApplicationPermissionAllUserSerializer
only_fields = serializers.ApplicationPermissionAllUserSerializer.Meta.only_fields
filterset_fields = ('username', 'name')
search_fields = filterset_fields
def get_queryset(self):
pk = self.kwargs.get('pk')
perm = get_object_or_404(models.ApplicationPermission, pk=pk)
users = perm.get_all_users().only(*self.only_fields).distinct()
return users

View File

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
#
from django.db.models import Q
from rest_framework.generics import ListAPIView
from common.mixins.api import CommonApiMixin
from applications.models import Application
from perms import serializers
__all__ = [
'UserGroupGrantedApplicationsApi'
]
class UserGroupGrantedApplicationsApi(CommonApiMixin, ListAPIView):
"""
获取用户组直接授权的应用
"""
serializer_class = serializers.AppGrantedSerializer
only_fields = serializers.AppGrantedSerializer.Meta.only_fields
filterset_fields = ['id', 'name', 'category', 'type', 'comment']
search_fields = ['name', 'comment']
rbac_perms = {
'list': 'perms.view_applicationpermission'
}
def get_queryset(self):
user_group_id = self.kwargs.get('pk')
if not user_group_id:
return Application.objects.none()
queryset = Application.objects\
.filter(Q(granted_by_permissions__user_groups__id=user_group_id))\
.distinct().only(*self.only_fields)
return queryset

View File

@ -1,2 +0,0 @@
from .user_permission_applications import *
from .common import *

View File

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
#
import time
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from rest_framework.views import APIView, Response
from rest_framework import status
from rest_framework.generics import (
ListAPIView, get_object_or_404
)
from orgs.utils import tmp_to_root_org
from applications.models import Application
from perms.utils.application.permission import (
get_application_system_user_ids,
validate_permission,
)
from .mixin import AppRoleAdminMixin, AppRoleUserMixin
from perms.hands import User, SystemUser
from perms import serializers
__all__ = [
'UserGrantedApplicationSystemUsersApi',
'MyGrantedApplicationSystemUsersApi',
'ValidateUserApplicationPermissionApi'
]
class BaseGrantedApplicationSystemUsersApi(ListAPIView):
serializer_class = serializers.ApplicationSystemUserSerializer
only_fields = serializers.ApplicationSystemUserSerializer.Meta.only_fields
user: None
def get_application_system_user_ids(self, application):
return get_application_system_user_ids(self.user, application)
def get_queryset(self):
application_id = self.kwargs.get('application_id')
application = get_object_or_404(Application, id=application_id)
system_user_ids = self.get_application_system_user_ids(application)
system_users = SystemUser.objects.filter(id__in=system_user_ids) \
.only(*self.only_fields).order_by('priority')
return system_users
class UserGrantedApplicationSystemUsersApi(AppRoleAdminMixin, BaseGrantedApplicationSystemUsersApi):
pass
class MyGrantedApplicationSystemUsersApi(AppRoleUserMixin, BaseGrantedApplicationSystemUsersApi):
pass
@method_decorator(tmp_to_root_org(), name='get')
class ValidateUserApplicationPermissionApi(APIView):
rbac_perms = {
'GET': 'perms.view_applicationpermission'
}
def get(self, request, *args, **kwargs):
user_id = request.query_params.get('user_id', '')
application_id = request.query_params.get('application_id', '')
account = system_user_id = request.query_params.get('account', '')
data = {
'has_permission': False,
'expire_at': int(time.time()),
'actions': []
}
if not all((user_id, application_id, account)):
return Response(data)
user = User.objects.get(id=user_id)
application = Application.objects.get(id=application_id)
has_perm, actions, expire_at = validate_permission(user, application, account)
status_code = status.HTTP_200_OK if has_perm else status.HTTP_403_FORBIDDEN
data = {
'has_permission': has_perm,
'expire_at': int(expire_at),
'actions': actions
}
return Response(data, status=status_code)

View File

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
#
from common.mixins.api import RoleAdminMixin as _RoleAdminMixin
from common.mixins.api import RoleUserMixin as _RoleUserMixin
from orgs.utils import tmp_to_root_org
class AppRoleAdminMixin(_RoleAdminMixin):
rbac_perms = (
('list', 'perms.view_userapp'),
('retrieve', 'perms.view_userapps'),
('get_tree', 'perms.view_userapps'),
('GET', 'perms.view_userapps'),
)
class AppRoleUserMixin(_RoleUserMixin):
rbac_perms = (
('list', 'perms.view_myapps'),
('retrieve', 'perms.view_myapps'),
('get_tree', 'perms.view_myapps'),
('GET', 'perms.view_myapps'),
)

View File

@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
#
from typing import Callable
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from common.mixins.api import CommonApiMixin
from common.tree import TreeNodeSerializer
from perms import serializers
from perms.tree.app import GrantedAppTreeUtil
from perms.utils.application.user_permission import (
get_user_granted_all_applications
)
from .mixin import AppRoleAdminMixin, AppRoleUserMixin
__all__ = [
'UserAllGrantedApplicationsApi',
'MyAllGrantedApplicationsApi',
'UserAllGrantedApplicationsAsTreeApi',
'MyAllGrantedApplicationsAsTreeApi',
]
class AllGrantedApplicationsApi(CommonApiMixin, ListAPIView):
only_fields = serializers.AppGrantedSerializer.Meta.only_fields
serializer_class = serializers.AppGrantedSerializer
filterset_fields = {
'id': ['exact'],
'name': ['exact'],
'category': ['exact'],
'type': ['exact', 'in'],
'comment': ['exact'],
}
search_fields = ['name', 'comment']
user: None
def get_queryset(self):
queryset = get_user_granted_all_applications(self.user)
return queryset.only(*self.only_fields)
class UserAllGrantedApplicationsApi(AppRoleAdminMixin, AllGrantedApplicationsApi):
pass
class MyAllGrantedApplicationsApi(AppRoleUserMixin, AllGrantedApplicationsApi):
pass
class ApplicationsAsTreeMixin:
"""
将应用序列化成树的结构返回
"""
serializer_class = TreeNodeSerializer
user: None
filter_queryset: Callable
get_queryset: Callable
get_serializer: Callable
def list(self, request, *args, **kwargs):
tree_id = request.query_params.get('tree_id', None)
parent_info = request.query_params.get('parentInfo', None)
queryset = self.filter_queryset(self.get_queryset())
util = GrantedAppTreeUtil()
if not tree_id:
tree_nodes = util.create_tree_nodes(queryset)
else:
tree_nodes = util.get_children_nodes(tree_id, parent_info, self.user)
serializer = self.get_serializer(tree_nodes, many=True)
return Response(data=serializer.data)
class UserAllGrantedApplicationsAsTreeApi(ApplicationsAsTreeMixin, UserAllGrantedApplicationsApi):
pass
class MyAllGrantedApplicationsAsTreeApi(ApplicationsAsTreeMixin, MyAllGrantedApplicationsApi):
pass

View File

@ -2,5 +2,4 @@
# #
from .base import * from .base import *
from .asset import * from .asset import *
from .application import *
from .system_user_permission import * from .system_user_permission import *

View File

@ -1,3 +0,0 @@
from .permission import *
from .permission_relation import *
from .user_permission import *

View File

@ -1,83 +0,0 @@
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from perms.models import ApplicationPermission, Action
from ..base import ActionsField, BasePermissionSerializer
__all__ = [
'ApplicationPermissionSerializer'
]
class ApplicationPermissionSerializer(BasePermissionSerializer):
actions = ActionsField(required=False, allow_null=True, label=_("Actions"))
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category display'))
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
is_valid = serializers.BooleanField(read_only=True, label=_('Is valid'))
is_expired = serializers.BooleanField(read_only=True, label=_("Is expired"))
class Meta:
model = ApplicationPermission
fields_mini = ['id', 'name']
fields_small = fields_mini + [
'category', 'category_display', 'type', 'type_display',
'actions',
'is_active', 'is_expired', 'is_valid',
'created_by', 'date_created', 'date_expired', 'date_start', 'comment', 'from_ticket'
]
fields_m2m = [
'users', 'user_groups', 'applications', 'system_users',
'users_amount', 'user_groups_amount', 'applications_amount',
'system_users_amount',
]
fields = fields_small + fields_m2m
read_only_fields = ['created_by', 'date_created', 'from_ticket']
extra_kwargs = {
'is_expired': {'label': _('Is expired')},
'is_valid': {'label': _('Is valid')},
'actions': {'label': _('Actions')},
'users_amount': {'label': _('Users amount')},
'user_groups_amount': {'label': _('User groups amount')},
'system_users_amount': {'label': _('System users amount')},
'applications_amount': {'label': _('Apps amount')},
}
def _filter_actions_choices(self, choices):
if request := self.context.get('request'):
category = request.query_params.get('category')
else:
category = None
exclude_choices = ApplicationPermission.get_exclude_actions_choices(category=category)
for choice in exclude_choices:
choices.pop(choice, None)
return choices
@classmethod
def setup_eager_loading(cls, queryset):
""" Perform necessary eager loading of data. """
queryset = queryset.prefetch_related(
'users', 'user_groups', 'applications', 'system_users'
)
return queryset
def validate_applications(self, applications):
if self.instance:
permission_type = self.instance.type
else:
permission_type = self.initial_data['type']
other_type_applications = [
application for application in applications
if application.type != permission_type
]
if len(other_type_applications) > 0:
error = _(
'The application list contains applications '
'that are different from the permission type. ({})'
).format(', '.join([application.name for application in other_type_applications]))
raise serializers.ValidationError(error)
return applications

View File

@ -1,88 +0,0 @@
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from common.mixins import BulkSerializerMixin
from perms.models import ApplicationPermission
__all__ = [
'ApplicationPermissionUserRelationSerializer',
'ApplicationPermissionUserGroupRelationSerializer',
'ApplicationPermissionApplicationRelationSerializer',
'ApplicationPermissionSystemUserRelationSerializer',
'ApplicationPermissionAllApplicationSerializer',
'ApplicationPermissionAllUserSerializer'
]
class RelationMixin(BulkSerializerMixin, serializers.Serializer):
applicationpermission_display = serializers.ReadOnlyField()
def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info)
fields.extend(['applicationpermission', "applicationpermission_display"])
return fields
class ApplicationPermissionUserRelationSerializer(RelationMixin, serializers.ModelSerializer):
user_display = serializers.ReadOnlyField()
class Meta:
model = ApplicationPermission.users.through
fields = [
'id', 'user', 'user_display',
]
class ApplicationPermissionUserGroupRelationSerializer(RelationMixin, serializers.ModelSerializer):
usergroup_display = serializers.ReadOnlyField()
class Meta:
model = ApplicationPermission.user_groups.through
fields = [
'id', 'usergroup', "usergroup_display",
]
class ApplicationPermissionApplicationRelationSerializer(RelationMixin, serializers.ModelSerializer):
application_display = serializers.ReadOnlyField()
class Meta:
model = ApplicationPermission.applications.through
fields = [
'id', "application", "application_display",
]
class ApplicationPermissionSystemUserRelationSerializer(RelationMixin, serializers.ModelSerializer):
systemuser_display = serializers.ReadOnlyField()
class Meta:
model = ApplicationPermission.system_users.through
fields = [
'id', 'systemuser', 'systemuser_display'
]
class ApplicationPermissionAllApplicationSerializer(serializers.Serializer):
application = serializers.UUIDField(read_only=True, source='id')
application_display = serializers.SerializerMethodField()
class Meta:
only_fields = ['id', 'name']
@staticmethod
def get_application_display(obj):
return str(obj)
class ApplicationPermissionAllUserSerializer(serializers.Serializer):
user = serializers.UUIDField(read_only=True, source='id')
user_display = serializers.SerializerMethodField()
class Meta:
only_fields = ['id', 'username', 'name']
@staticmethod
def get_user_display(obj):
return str(obj)

View File

@ -1,42 +0,0 @@
# -*- coding: utf-8 -*-
#
from rest_framework import serializers
from django.utils.translation import ugettext_lazy as _
from assets.models import SystemUser
from applications.models import Application
from applications.serializers import AppSerializerMixin
__all__ = [
'AppGrantedSerializer', 'ApplicationSystemUserSerializer'
]
class ApplicationSystemUserSerializer(serializers.ModelSerializer):
"""
查看授权的应用系统用户的数据结构这个和SystemUserSerializer不同字段少
"""
class Meta:
model = SystemUser
only_fields = (
'id', 'name', 'username', 'priority', 'protocol', 'login_mode'
)
fields = list(only_fields)
read_only_fields = fields
class AppGrantedSerializer(AppSerializerMixin, serializers.ModelSerializer):
"""
被授权应用的数据结构
"""
category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category'))
type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type'))
class Meta:
model = Application
only_fields = [
'id', 'name', 'domain', 'category', 'type', 'attrs', 'comment', 'org_id'
]
fields = only_fields + ['category_display', 'type_display', 'org_name']
read_only_fields = fields

View File

@ -1,3 +1,2 @@
from . import asset_permission from . import asset_permission
from . import app_permission
from . import refresh_perms from . import refresh_perms

View File

@ -1,106 +0,0 @@
import itertools
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from users.models import User, UserGroup
from assets.models import Asset, SystemUser
from applications.models import Application
from common.utils import get_logger
from common.exceptions import M2MReverseNotAllowed
from common.decorator import on_transaction_commit
from common.const.signals import POST_ADD
from perms.models import ApplicationPermission
from applications.models import Account as AppAccount
logger = get_logger(__file__)
@receiver(m2m_changed, sender=ApplicationPermission.applications.through)
@on_transaction_commit
def on_app_permission_applications_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission applications change signal received")
system_users = instance.system_users.all()
set_remote_app_asset_system_users_if_need(instance, system_users=system_users)
apps = Application.objects.filter(pk__in=pk_set)
set_app_accounts(apps, system_users)
def set_app_accounts(apps, system_users):
for app, system_user in itertools.product(apps, system_users):
AppAccount.objects.get_or_create(
defaults={'app': app, 'systemuser': system_user},
app=app, systemuser=system_user
)
def set_remote_app_asset_system_users_if_need(instance: ApplicationPermission, system_users=None,
users=None, groups=None):
if not instance.category_remote_app:
return
attrs = instance.applications.all().values_list('attrs', flat=True)
asset_ids = [attr['asset'] for attr in attrs if attr.get('asset')]
# 远程应用中资产可能在资产表里不存在
asset_ids = Asset.objects.filter(id__in=asset_ids).values_list('id', flat=True)
if not asset_ids:
return
system_users = system_users or instance.system_users.all()
for system_user in system_users:
system_user.add_related_assets(asset_ids)
if system_user.username_same_with_user:
users = users or instance.users.all()
groups = groups or instance.user_groups.all()
system_user.groups.add(*groups)
system_user.users.add(*users)
@receiver(m2m_changed, sender=ApplicationPermission.system_users.through)
@on_transaction_commit
def on_app_permission_system_users_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission system_users change signal received")
system_users = SystemUser.objects.filter(pk__in=pk_set)
set_remote_app_asset_system_users_if_need(instance, system_users=system_users)
apps = instance.applications.all()
set_app_accounts(apps, system_users)
@receiver(m2m_changed, sender=ApplicationPermission.users.through)
@on_transaction_commit
def on_app_permission_users_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission users change signal received")
users = User.objects.filter(pk__in=pk_set)
set_remote_app_asset_system_users_if_need(instance, users=users)
@receiver(m2m_changed, sender=ApplicationPermission.user_groups.through)
@on_transaction_commit
def on_app_permission_user_groups_changed(sender, instance, action, reverse, pk_set, **kwargs):
if reverse:
raise M2MReverseNotAllowed
if action != POST_ADD:
return
logger.debug("Application permission user groups change signal received")
groups = UserGroup.objects.filter(pk__in=pk_set)
set_remote_app_asset_system_users_if_need(instance, groups=groups)

View File

@ -1,103 +0,0 @@
from urllib.parse import urlencode, parse_qsl
from django.utils.translation import ugettext as _
from rest_framework.generics import get_object_or_404
from common.tree import TreeNode
from orgs.models import Organization
from assets.models import SystemUser
from applications.utils import KubernetesTree
from applications.models import Application
from perms.utils.application.permission import get_application_system_user_ids
class GrantedAppTreeUtil:
@staticmethod
def filter_organizations(applications):
organization_ids = set(applications.values_list('org_id', flat=True))
organizations = [Organization.get_instance(org_id) for org_id in organization_ids]
organizations.sort(key=lambda x: x.name)
return organizations
@staticmethod
def create_root_node():
name = _('My applications')
node = TreeNode(**{
'id': 'applications',
'name': name,
'title': name,
'pId': '',
'open': True,
'iconSkin': 'applications',
'isParent': True,
'meta': {
'type': 'root'
}
})
return node
@staticmethod
def create_empty_node():
name = _("Empty")
node = TreeNode(**{
'id': 'empty',
'name': name,
'title': name,
'pId': '',
'isParent': True,
'children': [],
'meta': {
'type': 'application'
}
})
return node
@staticmethod
def get_children_nodes(tree_id, parent_info, user):
tree_nodes = []
parent_info = dict(parse_qsl(parent_info))
pod_name = parent_info.get('pod')
app_id = parent_info.get('app_id')
namespace = parent_info.get('namespace')
system_user_id = parent_info.get('system_user_id')
if app_id and not any([pod_name, namespace, system_user_id]):
app = get_object_or_404(Application, id=app_id)
system_user_ids = get_application_system_user_ids(user, app)
system_users = SystemUser.objects.filter(id__in=system_user_ids).order_by('priority')
for system_user in system_users:
system_user_node = KubernetesTree(tree_id).as_system_user_tree_node(
system_user, parent_info
)
tree_nodes.append(system_user_node)
return tree_nodes
tree_nodes = KubernetesTree(tree_id).async_tree_node(parent_info)
return tree_nodes
def create_tree_nodes(self, applications):
tree_nodes = []
if not applications:
return [self.create_empty_node()]
root_node = self.create_root_node()
organizations = self.filter_organizations(applications)
for i, org in enumerate(organizations):
tree_id = urlencode({'org_id': str(org.id)})
apps = applications.filter(org_id=org.id)
# 组织节点
org_node = org.as_tree_node(oid=tree_id, pid=root_node.id)
org_node.name += '({})'.format(apps.count())
tree_nodes.append(org_node)
# 类别节点
category_type_nodes = Application.create_category_type_tree_nodes(
apps, tree_id, show_empty=False
)
tree_nodes += category_type_nodes
for app in apps:
app_node = app.as_tree_node(tree_id, k8s_as_tree=True)
tree_nodes.append(app_node)
return tree_nodes

View File

@ -1,19 +1,8 @@
# coding:utf-8 # coding:utf-8
from django.urls import re_path
from common import api as capi
from .asset_permission import asset_permission_urlpatterns from .asset_permission import asset_permission_urlpatterns
from .application_permission import application_permission_urlpatterns
from .system_user_permission import system_users_permission_urlpatterns
app_name = 'perms' app_name = 'perms'
old_version_urlpatterns = [
re_path('(?P<resource>user|user-group|asset-permission|remote-app-permission)/.*', capi.redirect_plural_name_api)
]
urlpatterns = [] urlpatterns = []
urlpatterns += asset_permission_urlpatterns urlpatterns += asset_permission_urlpatterns
urlpatterns += application_permission_urlpatterns
urlpatterns += system_users_permission_urlpatterns
urlpatterns += old_version_urlpatterns

View File

@ -1,48 +0,0 @@
# coding: utf-8
#
from django.urls import path, include
from rest_framework_bulk.routes import BulkRouter
from .. import api
router = BulkRouter()
router.register('application-permissions', api.ApplicationPermissionViewSet, 'application-permission')
router.register('application-permissions-users-relations', api.ApplicationPermissionUserRelationViewSet, 'application-permissions-users-relation')
router.register('application-permissions-user-groups-relations', api.ApplicationPermissionUserGroupRelationViewSet, 'application-permissions-user-groups-relation')
router.register('application-permissions-applications-relations', api.ApplicationPermissionApplicationRelationViewSet, 'application-permissions-application-relation')
router.register('application-permissions-system-users-relations', api.ApplicationPermissionSystemUserRelationViewSet, 'application-permissions-system-users-relation')
user_permission_urlpatterns = [
path('<uuid:pk>/applications/', api.UserAllGrantedApplicationsApi.as_view(), name='user-applications'),
path('applications/', api.MyAllGrantedApplicationsApi.as_view(), name='my-applications'),
# Application As Tree
path('<uuid:pk>/applications/tree/', api.UserAllGrantedApplicationsAsTreeApi.as_view(), name='user-applications-as-tree'),
path('applications/tree/', api.MyAllGrantedApplicationsAsTreeApi.as_view(), name='my-applications-as-tree'),
# Application System Users
path('<uuid:pk>/applications/<uuid:application_id>/system-users/', api.UserGrantedApplicationSystemUsersApi.as_view(), name='user-application-system-users'),
path('applications/<uuid:application_id>/system-users/', api.MyGrantedApplicationSystemUsersApi.as_view(), name='my-application-system-users'),
]
user_group_permission_urlpatterns = [
path('<uuid:pk>/applications/', api.UserGroupGrantedApplicationsApi.as_view(), name='user-group-applications'),
]
permission_urlpatterns = [
# 授权规则中授权的用户和应用
path('<uuid:pk>/applications/all/', api.ApplicationPermissionAllApplicationListApi.as_view(), name='application-permission-all-applications'),
path('<uuid:pk>/users/all/', api.ApplicationPermissionAllUserListApi.as_view(), name='application-permission-all-users'),
# 验证用户是否有某个应用的权限
path('user/validate/', api.ValidateUserApplicationPermissionApi.as_view(), name='validate-user-application-permission'),
]
application_permission_urlpatterns = [
path('users/', include(user_permission_urlpatterns)),
path('user-groups/', include(user_group_permission_urlpatterns)),
path('application-permissions/', include(permission_urlpatterns))
]
application_permission_urlpatterns += router.urls

View File

@ -1,6 +0,0 @@
from django.urls import path
from .. import api
system_users_permission_urlpatterns = [
path('system-users-permission/', api.SystemUserPermission.as_view(), name='system-users-permission'),
]