diff --git a/apps/perms/api/__init__.py b/apps/perms/api/__init__.py index dc0282725..d5155aeef 100644 --- a/apps/perms/api/__init__.py +++ b/apps/perms/api/__init__.py @@ -2,8 +2,10 @@ # from .asset_permission import * +from .application_permission import * from .user_permission import * from .asset_permission_relation import * +from .application_permission_relation import * from .user_group_permission import * from .remote_app_permission import * from .remote_app_permission_relation import * diff --git a/apps/perms/api/application_permission.py b/apps/perms/api/application_permission.py new file mode 100644 index 000000000..4421c48cd --- /dev/null +++ b/apps/perms/api/application_permission.py @@ -0,0 +1,29 @@ +# -*- 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 ..models import ApplicationPermission +from ..hands import ( + User, UserGroup, Asset, Node, SystemUser, +) +from .. import serializers + + +class ApplicationPermissionViewSet(OrgModelViewSet): + """ + 应用授权列表的增删改查API + """ + model = ApplicationPermission + serializer_class = serializers.ApplicationPermissionSerializer + filter_fields = ['name'] + permission_classes = (IsOrgAdmin,) + + def get_queryset(self): + queryset = super().get_queryset().prefetch_related( + "applications", "users", "user_groups", "system_users" + ) + return queryset + diff --git a/apps/perms/api/application_permission_relation.py b/apps/perms/api/application_permission_relation.py new file mode 100644 index 000000000..ef1f9fd08 --- /dev/null +++ b/apps/perms/api/application_permission_relation.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# +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 orgs.mixins.api import OrgRelationMixin +from orgs.mixins.api import OrgBulkModelViewSet +from orgs.utils import current_org +from common.permissions import IsOrgAdmin +from .. import serializers +from .. import models + +__all__ = [ + 'ApplicationPermissionUserRelationViewSet', + 'ApplicationPermissionUserGroupRelationViewSet', + 'ApplicationPermissionApplicationRelationViewSet', + 'ApplicationPermissionSystemUserRelationViewSet' +] + + +class RelationMixin(OrgRelationMixin, OrgBulkModelViewSet): + 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 + permission_classes = (IsOrgAdmin,) + filter_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 + permission_classes = (IsOrgAdmin,) + filter_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 + permission_classes = (IsOrgAdmin,) + filter_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 + permission_classes = (IsOrgAdmin,) + filter_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 diff --git a/apps/perms/migrations/0016_applicationpermission.py b/apps/perms/migrations/0016_applicationpermission.py new file mode 100644 index 000000000..c7d922696 --- /dev/null +++ b/apps/perms/migrations/0016_applicationpermission.py @@ -0,0 +1,44 @@ +# Generated by Django 2.2.13 on 2020-10-21 07:14 + +import common.utils.django +from django.conf import settings +from django.db import migrations, models +import django.utils.timezone +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0030_auto_20200819_2041'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('applications', '0006_application'), + ('assets', '0057_fill_node_value_assets_amount_and_parent_key'), + ('perms', '0015_auto_20200929_1728'), + ] + + operations = [ + migrations.CreateModel( + name='ApplicationPermission', + fields=[ + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, verbose_name='Name')), + ('is_active', models.BooleanField(default=True, verbose_name='Active')), + ('date_start', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='Date start')), + ('date_expired', models.DateTimeField(db_index=True, default=common.utils.django.date_expired_default, verbose_name='Date expired')), + ('created_by', models.CharField(blank=True, max_length=128, verbose_name='Created by')), + ('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')), + ('comment', models.TextField(blank=True, verbose_name='Comment')), + ('applications', models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='applications.Application', verbose_name='Application')), + ('system_users', models.ManyToManyField(related_name='granted_by_application_permissions', to='assets.SystemUser', verbose_name='System user')), + ('user_groups', models.ManyToManyField(blank=True, related_name='applicationpermissions', to='users.UserGroup', verbose_name='User group')), + ('users', models.ManyToManyField(blank=True, related_name='applicationpermissions', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'Application permission', + 'ordering': ('name',), + 'unique_together': {('org_id', 'name')}, + }, + ), + ] diff --git a/apps/perms/models/__init__.py b/apps/perms/models/__init__.py index 264c14787..e7b0b0ffb 100644 --- a/apps/perms/models/__init__.py +++ b/apps/perms/models/__init__.py @@ -2,6 +2,7 @@ # from .asset_permission import * +from .application_permission import * from .remote_app_permission import * from .database_app_permission import * from .k8s_app_permission import * diff --git a/apps/perms/models/application_permission.py b/apps/perms/models/application_permission.py new file mode 100644 index 000000000..db586c35f --- /dev/null +++ b/apps/perms/models/application_permission.py @@ -0,0 +1,38 @@ +# coding: utf-8 +# + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from common.utils import lazyproperty +from .base import BasePermission + +__all__ = [ + 'ApplicationPermission', +] + + +class ApplicationPermission(BasePermission): + applications = models.ManyToManyField('applications.Application', related_name='granted_by_permissions', blank=True, verbose_name=_("Application")) + system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_application_permissions', verbose_name=_("System user")) + + class Meta: + unique_together = [('org_id', 'name')] + verbose_name = _('Application permission') + ordering = ('name',) + + @lazyproperty + def users_amount(self): + return self.users.count() + + @lazyproperty + def user_groups_amount(self): + return self.user_groups.count() + + @lazyproperty + def applications_amount(self): + return self.applications.count() + + @lazyproperty + def system_users_amount(self): + return self.system_users.count() diff --git a/apps/perms/serializers/__init__.py b/apps/perms/serializers/__init__.py index e233a4d5b..0c3cf741d 100644 --- a/apps/perms/serializers/__init__.py +++ b/apps/perms/serializers/__init__.py @@ -2,10 +2,12 @@ # from .system_user_permission import * from .asset_permission import * +from .application_permission import * from .user_permission import * from .remote_app_permission import * from .remote_app_permission_relation import * from .asset_permission_relation import * +from .application_permission_relation import * from .database_app_permission import * from .database_app_permission_relation import * from .base import * diff --git a/apps/perms/serializers/application_permission.py b/apps/perms/serializers/application_permission.py new file mode 100644 index 000000000..916937f05 --- /dev/null +++ b/apps/perms/serializers/application_permission.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# + +from rest_framework import serializers + +from django.db.models import Count +from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from perms.models import ApplicationPermission + +__all__ = [ + 'ApplicationPermissionSerializer' +] + + +class ApplicationPermissionSerializer(BulkOrgResourceModelSerializer): + is_valid = serializers.BooleanField(read_only=True) + is_expired = serializers.BooleanField(read_only=True) + + class Meta: + model = ApplicationPermission + mini_fields = ['id', 'name'] + small_fields = mini_fields + [ + 'is_active', 'is_expired', 'is_valid', 'created_by', 'date_created', + 'date_expired', 'date_start', 'comment' + ] + m2m_fields = [ + 'users', 'user_groups', 'applications', 'system_users', + 'users_amount', 'user_groups_amount', 'applications_amount', 'system_users_amount', + ] + fields = small_fields + m2m_fields + read_only_fields = ['created_by', 'date_created'] + + @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 + diff --git a/apps/perms/serializers/application_permission_relation.py b/apps/perms/serializers/application_permission_relation.py new file mode 100644 index 000000000..4d9abd982 --- /dev/null +++ b/apps/perms/serializers/application_permission_relation.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# +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' +] + + +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 Meta: + list_serializer_class = AdaptedBulkListSerializer + + +class ApplicationPermissionUserRelationSerializer(RelationMixin, serializers.ModelSerializer): + user_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = ApplicationPermission.users.through + fields = [ + 'id', 'user', 'user_display', + ] + + +class ApplicationPermissionUserGroupRelationSerializer(RelationMixin, serializers.ModelSerializer): + usergroup_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = ApplicationPermission.user_groups.through + fields = [ + 'id', 'usergroup', "usergroup_display", + ] + + +class ApplicationPermissionApplicationRelationSerializer(RelationMixin, serializers.ModelSerializer): + application_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = ApplicationPermission.applications.through + fields = [ + 'id', "application", "application_display", + ] + + +class ApplicationPermissionSystemUserRelationSerializer(RelationMixin, serializers.ModelSerializer): + systemuser_display = serializers.ReadOnlyField() + + class Meta(RelationMixin.Meta): + model = ApplicationPermission.system_users.through + fields = [ + 'id', 'systemuser', 'systemuser_display' + ] + diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index f2b87ca72..3559703a9 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -3,6 +3,7 @@ from django.urls import re_path from common import api as capi from .asset_permission import asset_permission_urlpatterns +from .application_permission import application_permission_urlpatterns from .remote_app_permission import remote_app_permission_urlpatterns from .database_app_permission import database_app_permission_urlpatterns from .system_user_permission import system_users_permission_urlpatterns @@ -15,6 +16,7 @@ old_version_urlpatterns = [ ] urlpatterns = asset_permission_urlpatterns + \ + application_permission_urlpatterns + \ remote_app_permission_urlpatterns + \ database_app_permission_urlpatterns + \ k8s_app_permission_urlpatterns + \ diff --git a/apps/perms/urls/application_permission.py b/apps/perms/urls/application_permission.py new file mode 100644 index 000000000..bd37501c9 --- /dev/null +++ b/apps/perms/urls/application_permission.py @@ -0,0 +1,50 @@ +# 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('/applications/', api.UserGrantedApplicationsApi.as_view(), name='user-applications'), + path('applications/', api.UserGrantedApplicationsApi.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'), + + 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'), +] + +user_group_permission_urlpatterns = [ + path('/applications/', api.UserGroupGrantedApplicationsApi.as_view(), name='user-group-applications'), +] + +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'), +] + +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 +""" +application_permission_urlpatterns = router.urls