diff --git a/apps/applications/api/__init__.py b/apps/applications/api/__init__.py index b232a1ece..7220c0c12 100644 --- a/apps/applications/api/__init__.py +++ b/apps/applications/api/__init__.py @@ -1,4 +1,5 @@ from .application import * +from .mixin import * from .remote_app import * from .database_app import * from .k8s_app import * diff --git a/apps/applications/api/mixin.py b/apps/applications/api/mixin.py new file mode 100644 index 000000000..eb4fc9486 --- /dev/null +++ b/apps/applications/api/mixin.py @@ -0,0 +1,49 @@ + +class SerializeApplicationToTreeNodeMixin: + + @staticmethod + def _serialize_db(db): + return { + 'id': db.id, + 'name': db.name, + 'title': db.name, + 'pId': '', + 'open': False, + 'iconSkin': 'database', + 'meta': {'type': 'database_app'} + } + + @staticmethod + def _serialize_remote_app(remote_app): + return { + 'id': remote_app.id, + 'name': remote_app.name, + 'title': remote_app.name, + 'pId': '', + 'open': False, + 'isParent': False, + 'iconSkin': 'chrome', + 'meta': {'type': 'remote_app'} + } + + @staticmethod + def _serialize_cloud(cloud): + return { + 'id': cloud.id, + 'name': cloud.name, + 'title': cloud.name, + 'pId': '', + 'open': False, + 'isParent': False, + 'iconSkin': 'k8s', + 'meta': {'type': 'k8s_app'} + } + + def dispatch_serialize(self, application): + method_name = f'_serialize_{application.category}' + data = getattr(self, method_name)(application) + return data + + def serialize_applications(self, applications): + data = [self.dispatch_serialize(application) for application in applications] + return data diff --git a/apps/applications/models/application.py b/apps/applications/models/application.py index a4c7845eb..4c8e16a93 100644 --- a/apps/applications/models/application.py +++ b/apps/applications/models/application.py @@ -113,3 +113,6 @@ class Application(CommonModelMixin, OrgModelMixin): class Meta: unique_together = [('org_id', 'name')] ordering = ('name',) + + def __str__(self): + return '{}({})'.format(self.name, self.get_category_display()) diff --git a/apps/perms/api/__init__.py b/apps/perms/api/__init__.py index d5155aeef..514010b31 100644 --- a/apps/perms/api/__init__.py +++ b/apps/perms/api/__init__.py @@ -4,9 +4,11 @@ from .asset_permission import * from .application_permission import * from .user_permission import * +from .user_permission_application import * from .asset_permission_relation import * from .application_permission_relation import * from .user_group_permission import * +from .user_group_permission_application import * from .remote_app_permission import * from .remote_app_permission_relation import * from .user_remote_app_permission import * diff --git a/apps/perms/api/application_permission.py b/apps/perms/api/application_permission.py index 4421c48cd..24a43d7e9 100644 --- a/apps/perms/api/application_permission.py +++ b/apps/perms/api/application_permission.py @@ -1,18 +1,12 @@ # -*- coding: utf-8 -*- # -from django.db.models import Q - from common.permissions import IsOrgAdmin -from orgs.mixins.api import OrgModelViewSet -from common.utils import get_object_or_none +from orgs.mixins.api import OrgBulkModelViewSet from ..models import ApplicationPermission -from ..hands import ( - User, UserGroup, Asset, Node, SystemUser, -) from .. import serializers -class ApplicationPermissionViewSet(OrgModelViewSet): +class ApplicationPermissionViewSet(OrgBulkModelViewSet): """ 应用授权列表的增删改查API """ diff --git a/apps/perms/api/application_permission_relation.py b/apps/perms/api/application_permission_relation.py index ef1f9fd08..7e05cc6ce 100644 --- a/apps/perms/api/application_permission_relation.py +++ b/apps/perms/api/application_permission_relation.py @@ -2,11 +2,10 @@ # from rest_framework import generics from django.db.models import F, Value -from django.db.models import Q from django.db.models.functions import Concat from django.shortcuts import get_object_or_404 -from assets.models import Node, Asset +from applications.models import Application from orgs.mixins.api import OrgRelationMixin from orgs.mixins.api import OrgBulkModelViewSet from orgs.utils import current_org @@ -18,7 +17,9 @@ __all__ = [ 'ApplicationPermissionUserRelationViewSet', 'ApplicationPermissionUserGroupRelationViewSet', 'ApplicationPermissionApplicationRelationViewSet', - 'ApplicationPermissionSystemUserRelationViewSet' + 'ApplicationPermissionSystemUserRelationViewSet', + 'ApplicationPermissionAllApplicationListApi', + 'ApplicationPermissionAllUserListApi', ] @@ -96,3 +97,32 @@ class ApplicationPermissionSystemUserRelationViewSet(RelationMixin): Value(')') )) return queryset + + +class ApplicationPermissionAllApplicationListApi(generics.ListAPIView): + permission_classes = (IsOrgAdmin,) + serializer_class = serializers.ApplicationPermissionAllApplicationSerializer + only_fields = serializers.ApplicationPermissionAllApplicationSerializer.Meta.only_fields + filter_fields = ('name',) + search_fields = filter_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): + permission_classes = (IsOrgAdmin,) + serializer_class = serializers.ApplicationPermissionAllUserSerializer + only_fields = serializers.ApplicationPermissionAllUserSerializer.Meta.only_fields + filter_fields = ('username', 'name') + search_fields = filter_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 diff --git a/apps/perms/api/user_group_permission_application.py b/apps/perms/api/user_group_permission_application.py new file mode 100644 index 000000000..86d5e2060 --- /dev/null +++ b/apps/perms/api/user_group_permission_application.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# + +from django.db.models import Q +from rest_framework.generics import ListAPIView + +from common.permissions import IsOrgAdminOrAppUser +from applications.models import Application +from perms import serializers + +__all__ = [ + 'UserGroupGrantedApplicationsApi' +] + + +class UserGroupGrantedApplicationsApi(ListAPIView): + """ + 获取用户组直接授权的资产 + """ + permission_classes = (IsOrgAdminOrAppUser,) + serializer_class = serializers.ApplicationGrantedSerializer + only_fields = serializers.ApplicationGrantedSerializer.Meta.only_fields + filter_fields = ['id', 'name', 'comment'] + search_fields = ['name', 'comment'] + + def get_queryset(self): + user_group_id = self.kwargs.get('pk', '') + queryset = Application.objects\ + .filter(Q(granted_by_permissions__user_groups__id=user_group_id))\ + .distinct().only(*self.only_fields) + return queryset diff --git a/apps/perms/api/user_permission_application/__init__.py b/apps/perms/api/user_permission_application/__init__.py new file mode 100644 index 000000000..50aed175b --- /dev/null +++ b/apps/perms/api/user_permission_application/__init__.py @@ -0,0 +1,2 @@ +from .user_permission_applications import * +from .common import * diff --git a/apps/perms/api/user_permission_application/common.py b/apps/perms/api/user_permission_application/common.py new file mode 100644 index 000000000..4ac6ba728 --- /dev/null +++ b/apps/perms/api/user_permission_application/common.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# +import uuid +from django.shortcuts import get_object_or_404 +from rest_framework.views import APIView, Response +from rest_framework.generics import ( + ListAPIView, get_object_or_404 +) + +from applications.models import Application +from perms.utils.application_permission import ( + get_application_system_users_id +) +from perms.api.user_permission.mixin import ForAdminMixin, ForUserMixin +from common.permissions import IsOrgAdminOrAppUser +from ...hands import User, SystemUser +from ... import serializers + + +__all__ = [ + 'UserGrantedApplicationSystemUsersApi', + 'MyGrantedApplicationSystemUsersApi', + 'ValidateUserApplicationPermissionApi' +] + + +class GrantedApplicationSystemUsersMixin(ListAPIView): + serializer_class = serializers.ApplicationSystemUserSerializer + only_fields = serializers.ApplicationSystemUserSerializer.Meta.only_fields + user: None + + def get_application_system_users_id(self, application): + return get_application_system_users_id(self.user, application) + + def get_queryset(self): + application_id = self.kwargs.get('application_id') + application = get_object_or_404(Application, id=application_id) + system_users_id = self.get_application_system_users_id(application) + system_users = SystemUser.objects.filter(id__in=system_users_id)\ + .only(*self.only_fields).order_by('priority') + return system_users + + +class UserGrantedApplicationSystemUsersApi(ForAdminMixin, GrantedApplicationSystemUsersMixin): + pass + + +class MyGrantedApplicationSystemUsersApi(ForUserMixin, GrantedApplicationSystemUsersMixin): + pass + + +class ValidateUserApplicationPermissionApi(APIView): + permission_classes = (IsOrgAdminOrAppUser,) + + def get(self, request, *args, **kwargs): + user_id = request.query_params.get('user_id', '') + application_id = request.query_params.get('application_id', '') + system_user_id = request.query_params.get('system_user_id', '') + + try: + user_id = uuid.UUID(user_id) + application_id = uuid.UUID(application_id) + system_user_id = uuid.UUID(system_user_id) + except ValueError: + return Response({'msg': False}, status=403) + + user = get_object_or_404(User, id=user_id) + application = get_object_or_404(Application, id=application_id) + system_user = get_object_or_404(SystemUser, id=system_user_id) + + system_users_id = get_application_system_users_id(user, application) + if system_user.id in system_users_id: + return Response({'msg': True}, status=200) + + return Response({'msg': False}, status=403) diff --git a/apps/perms/api/user_permission_application/user_permission_applications.py b/apps/perms/api/user_permission_application/user_permission_applications.py new file mode 100644 index 000000000..aa8a54b1a --- /dev/null +++ b/apps/perms/api/user_permission_application/user_permission_applications.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# +from rest_framework.generics import ListAPIView +from rest_framework.response import Response + +from applications.api.mixin import SerializeApplicationToTreeNodeMixin +from perms import serializers +from perms.api.user_permission.mixin import ForAdminMixin, ForUserMixin +from perms.utils.user_application_permission import ( + get_user_granted_all_applications +) + + +__all__ = [ + 'UserAllGrantedApplicationsApi', + 'MyAllGrantedApplicationsApi', + 'UserAllGrantedApplicationsAsTreeApi', + 'MyAllGrantedApplicationsAsTreeApi', +] + + +class AllGrantedApplicationsMixin(ListAPIView): + only_fields = serializers.ApplicationGrantedSerializer.Meta.only_fields + serializer_class = serializers.ApplicationGrantedSerializer + filter_fields = ['id', 'name', 'comment'] + 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(ForAdminMixin, AllGrantedApplicationsMixin): + only_fields = serializers.ApplicationGrantedSerializer.Meta.only_fields + serializer_class = serializers.ApplicationGrantedSerializer + filter_fields = ['id', 'name', 'comment'] + search_fields = ['name', 'comment'] + + def get_queryset(self): + queryset = get_user_granted_all_applications(self.user) + return queryset.only(*self.only_fields) + + +class MyAllGrantedApplicationsApi(ForUserMixin, AllGrantedApplicationsMixin): + pass + + +class ApplicationsAsTreeMixin(SerializeApplicationToTreeNodeMixin): + """ + 将应用序列化成树的结构返回 + """ + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + data = self.serialize_applications(queryset) + return Response(data=data) + + +class UserAllGrantedApplicationsAsTreeApi(ApplicationsAsTreeMixin, UserAllGrantedApplicationsApi): + pass + + +class MyAllGrantedApplicationsAsTreeApi(ApplicationsAsTreeMixin, MyAllGrantedApplicationsApi): + pass diff --git a/apps/perms/models/application_permission.py b/apps/perms/models/application_permission.py index db586c35f..db35192af 100644 --- a/apps/perms/models/application_permission.py +++ b/apps/perms/models/application_permission.py @@ -2,10 +2,12 @@ # from django.db import models +from django.db.models import Q from django.utils.translation import ugettext_lazy as _ from common.utils import lazyproperty from .base import BasePermission +from users.models import User __all__ = [ 'ApplicationPermission', @@ -36,3 +38,11 @@ class ApplicationPermission(BasePermission): @lazyproperty def system_users_amount(self): return self.system_users.count() + + def get_all_users(self): + users_id = self.users.all().values_list('id', flat=True) + user_groups_id = self.user_groups.all().values_list('id', flat=True) + users = User.objects.filter( + Q(id__in=users_id) | Q(groups__id__in=user_groups_id) + ) + return users diff --git a/apps/perms/serializers/application_permission.py b/apps/perms/serializers/application_permission.py index 916937f05..95c4c47c1 100644 --- a/apps/perms/serializers/application_permission.py +++ b/apps/perms/serializers/application_permission.py @@ -3,7 +3,6 @@ from rest_framework import serializers -from django.db.models import Count from orgs.mixins.serializers import BulkOrgResourceModelSerializer from perms.models import ApplicationPermission diff --git a/apps/perms/serializers/application_permission_relation.py b/apps/perms/serializers/application_permission_relation.py index 4d9abd982..fd3f12f57 100644 --- a/apps/perms/serializers/application_permission_relation.py +++ b/apps/perms/serializers/application_permission_relation.py @@ -4,15 +4,15 @@ from rest_framework import serializers from common.mixins import BulkSerializerMixin from common.serializers import AdaptedBulkListSerializer -from assets.models import Asset, Node from ..models import ApplicationPermission -from users.models import User __all__ = [ 'ApplicationPermissionUserRelationSerializer', 'ApplicationPermissionUserGroupRelationSerializer', 'ApplicationPermissionApplicationRelationSerializer', - 'ApplicationPermissionSystemUserRelationSerializer' + 'ApplicationPermissionSystemUserRelationSerializer', + 'ApplicationPermissionAllApplicationSerializer', + 'ApplicationPermissionAllUserSerializer' ] @@ -67,3 +67,26 @@ class ApplicationPermissionSystemUserRelationSerializer(RelationMixin, serialize '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) diff --git a/apps/perms/serializers/user_permission.py b/apps/perms/serializers/user_permission.py index bfc273775..380389bcc 100644 --- a/apps/perms/serializers/user_permission.py +++ b/apps/perms/serializers/user_permission.py @@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _ from assets.models import Node, SystemUser, Asset from assets.serializers import ProtocolsField from .asset_permission import ActionsField +from applications.models import Application __all__ = [ 'NodeGrantedSerializer', @@ -15,6 +16,8 @@ __all__ = [ 'RemoteAppSystemUserSerializer', 'DatabaseAppSystemUserSerializer', 'K8sAppSystemUserSerializer', + 'ApplicationGrantedSerializer', + 'ApplicationSystemUserSerializer' ] @@ -34,6 +37,19 @@ class AssetSystemUserSerializer(serializers.ModelSerializer): read_only_fields = fields +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 RemoteAppSystemUserSerializer(serializers.ModelSerializer): class Meta: model = SystemUser @@ -92,3 +108,16 @@ class NodeGrantedSerializer(serializers.ModelSerializer): class ActionsSerializer(serializers.Serializer): actions = ActionsField(read_only=True) + + +class ApplicationGrantedSerializer(serializers.ModelSerializer): + """ + 被授权应用的数据结构 + """ + class Meta: + model = Application + only_fields = [ + 'id', 'name', 'domain', 'category', 'type', 'comment', 'org_id' + ] + fields = only_fields + ['org_name'] + read_only_fields = fields diff --git a/apps/perms/urls/application_permission.py b/apps/perms/urls/application_permission.py index bd37501c9..4ed9e6d37 100644 --- a/apps/perms/urls/application_permission.py +++ b/apps/perms/urls/application_permission.py @@ -13,17 +13,17 @@ router.register('application-permissions-user-groups-relations', api.Application 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('/applications/', api.UserGrantedApplicationsApi.as_view(), name='user-applications'), - path('applications/', api.UserGrantedApplicationsApi.as_view(), name='my-applications'), + path('/applications/', api.UserAllGrantedApplicationsApi.as_view(), name='user-applications'), + path('applications/', api.MyAllGrantedApplicationsApi.as_view(), name='my-applications'), - # Application as tree - path('/applications/tree/', api.UserGrantedApplicationsAsTreeApi.as_view(), name='user-applications-as-tree'), - path('applications/tree/', api.UserGrantedApplicationsAsTreeApi.as_view(), name='my-applications-as-tree'), + # Application As Tree + path('/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('/applications//system-users/', api.UserGrantedApplicationSystemUsersApi.as_view(), name='user-application-system-users'), - path('applications//system-users/', api.UserGrantedApplicationSystemUsersApi.as_view(), name='user-application-system-users'), + path('applications//system-users/', api.MyGrantedApplicationSystemUsersApi.as_view(), name='my-application-system-users'), ] user_group_permission_urlpatterns = [ @@ -31,11 +31,11 @@ user_group_permission_urlpatterns = [ ] permission_urlpatterns = [ - # 授权规则中授权的用户和数据库应用 + # 授权规则中授权的用户和应用 path('/applications/all/', api.ApplicationPermissionAllApplicationListApi.as_view(), name='application-permission-all-applications'), path('/users/all/', api.ApplicationPermissionAllUserListApi.as_view(), name='application-permission-all-users'), - # 验证用户是否有某个数据库应用的权限 + # 验证用户是否有某个应用的权限 path('user/validate/', api.ValidateUserApplicationPermissionApi.as_view(), name='validate-user-application-permission'), ] @@ -46,5 +46,3 @@ application_permission_urlpatterns = [ ] application_permission_urlpatterns += router.urls -""" -application_permission_urlpatterns = router.urls diff --git a/apps/perms/utils/__init__.py b/apps/perms/utils/__init__.py index 5ef34b6d9..c556d2f89 100644 --- a/apps/perms/utils/__init__.py +++ b/apps/perms/utils/__init__.py @@ -2,7 +2,9 @@ # from .asset_permission import * +from .application_permission import * from .remote_app_permission import * from .database_app_permission import * from .k8s_app_permission import * from .user_asset_permission import * +from .user_application_permission import * diff --git a/apps/perms/utils/application_permission.py b/apps/perms/utils/application_permission.py new file mode 100644 index 000000000..83f246ab3 --- /dev/null +++ b/apps/perms/utils/application_permission.py @@ -0,0 +1,14 @@ +from django.db.models import Q + +from common.utils import get_logger +from ..models import ApplicationPermission + +logger = get_logger(__file__) + + +def get_application_system_users_id(user, application): + queryset = ApplicationPermission.objects\ + .filter(Q(users=user) | Q(user_groups__users=user), Q(applications=application))\ + .valid()\ + .values_list('system_users', flat=True) + return queryset diff --git a/apps/perms/utils/user_application_permission.py b/apps/perms/utils/user_application_permission.py new file mode 100644 index 000000000..5745dd4ef --- /dev/null +++ b/apps/perms/utils/user_application_permission.py @@ -0,0 +1,21 @@ +from perms.models import ApplicationPermission +from applications.models import Application + + +def get_user_all_applicationpermission_ids(user): + application_perm_ids = set() + application_perm_ids.update( + ApplicationPermission.objects.valid().filter(users=user).distinct().values_list('id', flat=True) + ) + application_perm_ids.update( + ApplicationPermission.objects.valid().filter(user_groups__users=user).distinct().values_list('id', flat=True) + ) + return application_perm_ids + + +def get_user_granted_all_applications(user): + application_perm_ids = get_user_all_applicationpermission_ids(user) + applications = Application.objects.filter( + granted_by_permissions__id__in=application_perm_ids + ).distinct() + return applications