diff --git a/apps/applications/api/__init__.py b/apps/applications/api/__init__.py index 7220c0c12..68f0d1803 100644 --- a/apps/applications/api/__init__.py +++ b/apps/applications/api/__init__.py @@ -1,5 +1,3 @@ from .application import * from .mixin import * from .remote_app import * -from .database_app import * -from .k8s_app import * diff --git a/apps/applications/api/application.py b/apps/applications/api/application.py index ad447798c..bf32cf679 100644 --- a/apps/applications/api/application.py +++ b/apps/applications/api/application.py @@ -3,18 +3,18 @@ from orgs.mixins.api import OrgBulkModelViewSet -from .mixin import ApplicationAttrsSerializerViewMixin from ..hands import IsOrgAdminOrAppUser from .. import models, serializers - -__all__ = [ - 'ApplicationViewSet', -] +from .mixin import ApplicationViewMixin -class ApplicationViewSet(ApplicationAttrsSerializerViewMixin, OrgBulkModelViewSet): +__all__ = ['ApplicationViewSet'] + + +class ApplicationViewSet(ApplicationViewMixin, OrgBulkModelViewSet): model = models.Application filter_fields = ('name', 'type', 'category') search_fields = filter_fields permission_classes = (IsOrgAdminOrAppUser,) serializer_class = serializers.ApplicationSerializer + diff --git a/apps/applications/api/database_app.py b/apps/applications/api/database_app.py deleted file mode 100644 index af5810e0f..000000000 --- a/apps/applications/api/database_app.py +++ /dev/null @@ -1,20 +0,0 @@ -# coding: utf-8 -# - -from orgs.mixins.api import OrgBulkModelViewSet - -from .. import models -from .. import serializers -from ..hands import IsOrgAdminOrAppUser - -__all__ = [ - 'DatabaseAppViewSet', -] - - -class DatabaseAppViewSet(OrgBulkModelViewSet): - model = models.DatabaseApp - filter_fields = ('name',) - search_fields = filter_fields - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = serializers.DatabaseAppSerializer diff --git a/apps/applications/api/k8s_app.py b/apps/applications/api/k8s_app.py deleted file mode 100644 index 5cc63b546..000000000 --- a/apps/applications/api/k8s_app.py +++ /dev/null @@ -1,20 +0,0 @@ -# coding: utf-8 -# - -from orgs.mixins.api import OrgBulkModelViewSet - -from .. import models -from .. import serializers -from ..hands import IsOrgAdminOrAppUser - -__all__ = [ - 'K8sAppViewSet', -] - - -class K8sAppViewSet(OrgBulkModelViewSet): - model = models.K8sApp - filter_fields = ('name',) - search_fields = filter_fields - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = serializers.K8sAppSerializer diff --git a/apps/applications/api/mixin.py b/apps/applications/api/mixin.py index 6bab179a0..a07761a22 100644 --- a/apps/applications/api/mixin.py +++ b/apps/applications/api/mixin.py @@ -1,57 +1,16 @@ -import uuid - -from common.exceptions import JMSException from orgs.models import Organization -from .. import models +from ..serializers.utils import get_dynamic_mapping_fields_mapping_rule_by_view -class ApplicationAttrsSerializerViewMixin: +__all__ = ['ApplicationViewMixin', 'SerializeApplicationToTreeNodeMixin'] - def get_serializer_class(self): - serializer_class = super().get_serializer_class() - if getattr(self, 'swagger_fake_view', False): - return serializer_class - app_type = self.request.query_params.get('type') - app_category = self.request.query_params.get('category') - type_options = list(dict(models.Category.get_all_type_serializer_mapper()).keys()) - category_options = list(dict(models.Category.get_category_serializer_mapper()).keys()) - # ListAPIView 没有 action 属性 - # 不使用method属性,因为options请求时为method为post - action = getattr(self, 'action', 'list') +class ApplicationViewMixin: + """ 实现 `get_dynamic_mapping_fields_mapping_rule` 方法, 供其他和 Application 相关的 View 继承使用""" - if app_type and app_type not in type_options: - raise JMSException( - 'Invalid query parameter `type`, select from the following options: {}' - ''.format(type_options) - ) - if app_category and app_category not in category_options: - raise JMSException( - 'Invalid query parameter `category`, select from the following options: {}' - ''.format(category_options) - ) - - if action in [ - 'create', 'update', 'partial_update', 'bulk_update', 'partial_bulk_update' - ] and not app_type: - # action: create / update - raise JMSException( - 'The `{}` action must take the `type` query parameter'.format(action) - ) - - if app_type: - # action: create / update / list / retrieve / metadata - attrs_cls = models.Category.get_type_serializer_cls(app_type) - class_name = 'ApplicationDynamicSerializer{}'.format(app_type.title()) - elif app_category: - # action: list / retrieve / metadata - attrs_cls = models.Category.get_category_serializer_cls(app_category) - class_name = 'ApplicationDynamicSerializer{}'.format(app_category.title()) - else: - attrs_cls = models.Category.get_no_password_serializer_cls() - class_name = 'ApplicationDynamicSerializer' - cls = type(class_name, (serializer_class,), {'attrs': attrs_cls()}) - return cls + def get_dynamic_mapping_fields_mapping_rule(self): + fields_mapping_rule = get_dynamic_mapping_fields_mapping_rule_by_view(view=self) + return fields_mapping_rule class SerializeApplicationToTreeNodeMixin: diff --git a/apps/applications/api/remote_app.py b/apps/applications/api/remote_app.py index 9b983449b..c68679bab 100644 --- a/apps/applications/api/remote_app.py +++ b/apps/applications/api/remote_app.py @@ -1,40 +1,19 @@ # coding: utf-8 # -from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins import generics -from common.exceptions import JMSException -from ..hands import IsOrgAdmin, IsAppUser +from ..hands import IsAppUser from .. import models -from ..serializers import RemoteAppSerializer, RemoteAppConnectionInfoSerializer +from ..serializers import RemoteAppConnectionInfoSerializer +from ..permissions import IsRemoteApp __all__ = [ - 'RemoteAppViewSet', 'RemoteAppConnectionInfoApi', + 'RemoteAppConnectionInfoApi', ] -class RemoteAppViewSet(OrgBulkModelViewSet): - model = models.RemoteApp - filter_fields = ('name', 'type', 'comment') - search_fields = filter_fields - permission_classes = (IsOrgAdmin,) - serializer_class = RemoteAppSerializer - - class RemoteAppConnectionInfoApi(generics.RetrieveAPIView): model = models.Application - permission_classes = (IsAppUser, ) + permission_classes = (IsAppUser, IsRemoteApp) serializer_class = RemoteAppConnectionInfoSerializer - - @staticmethod - def check_category_allowed(obj): - if not obj.category_is_remote_app: - raise JMSException( - 'The request instance(`{}`) is not of category `remote_app`'.format(obj.category) - ) - - def get_object(self): - obj = super().get_object() - self.check_category_allowed(obj) - return obj diff --git a/apps/applications/const.py b/apps/applications/const.py index 773f30b9f..151a65b28 100644 --- a/apps/applications/const.py +++ b/apps/applications/const.py @@ -1,64 +1,49 @@ # coding: utf-8 # +from django.db.models import TextChoices from django.utils.translation import ugettext_lazy as _ -# RemoteApp +class ApplicationCategoryChoices(TextChoices): + db = 'db', _('Database') + remote_app = 'remote_app', _('Remote app') + cloud = 'cloud', 'Cloud' -REMOTE_APP_BOOT_PROGRAM_NAME = '||jmservisor' - -REMOTE_APP_TYPE_CHROME = 'chrome' -REMOTE_APP_TYPE_MYSQL_WORKBENCH = 'mysql_workbench' -REMOTE_APP_TYPE_VMWARE_CLIENT = 'vmware_client' -REMOTE_APP_TYPE_CUSTOM = 'custom' - -# Fields attribute write_only default => False - -REMOTE_APP_TYPE_CHROME_FIELDS = [ - {'name': 'chrome_target'}, - {'name': 'chrome_username'}, - {'name': 'chrome_password', 'write_only': True} -] -REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS = [ - {'name': 'mysql_workbench_ip'}, - {'name': 'mysql_workbench_name'}, - {'name': 'mysql_workbench_port'}, - {'name': 'mysql_workbench_username'}, - {'name': 'mysql_workbench_password', 'write_only': True} -] -REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS = [ - {'name': 'vmware_target'}, - {'name': 'vmware_username'}, - {'name': 'vmware_password', 'write_only': True} -] -REMOTE_APP_TYPE_CUSTOM_FIELDS = [ - {'name': 'custom_cmdline'}, - {'name': 'custom_target'}, - {'name': 'custom_username'}, - {'name': 'custom_password', 'write_only': True} -] - -REMOTE_APP_TYPE_FIELDS_MAP = { - REMOTE_APP_TYPE_CHROME: REMOTE_APP_TYPE_CHROME_FIELDS, - REMOTE_APP_TYPE_MYSQL_WORKBENCH: REMOTE_APP_TYPE_MYSQL_WORKBENCH_FIELDS, - REMOTE_APP_TYPE_VMWARE_CLIENT: REMOTE_APP_TYPE_VMWARE_CLIENT_FIELDS, - REMOTE_APP_TYPE_CUSTOM: REMOTE_APP_TYPE_CUSTOM_FIELDS -} - -REMOTE_APP_TYPE_CHOICES = ( - (REMOTE_APP_TYPE_CHROME, 'Chrome'), - (REMOTE_APP_TYPE_MYSQL_WORKBENCH, 'MySQL Workbench'), - (REMOTE_APP_TYPE_VMWARE_CLIENT, 'vSphere Client'), - (REMOTE_APP_TYPE_CUSTOM, _('Custom')), -) + @classmethod + def get_label(cls, category): + return dict(cls.choices).get(category, '') -# DatabaseApp +class ApplicationTypeChoices(TextChoices): + # db category + mysql = 'mysql', 'MySQL' + oracle = 'oracle', 'Oracle' + pgsql = 'postgresql', 'PostgreSQL' + mariadb = 'mariadb', 'MariaDB' + # remote-app category + chrome = 'chrome', 'Chrome' + mysql_workbench = 'mysql_workbench', 'MySQL Workbench' + vmware_client = 'vmware_client', 'vSphere Client' + custom = 'custom', _('Custom') -DATABASE_APP_TYPE_MYSQL = 'mysql' + # cloud category + k8s = 'k8s', 'Kubernetes' + + @classmethod + def get_label(cls, tp): + return dict(cls.choices).get(tp, '') + + @classmethod + def db_types(cls): + return [cls.mysql.value, cls.oracle.value, cls.pgsql.value, cls.mariadb.value] + + @classmethod + def remote_app_types(cls): + return [cls.chrome.value, cls.mysql_workbench.value, cls.vmware_client.value, cls.custom.value] + + @classmethod + def cloud_types(cls): + return [cls.k8s.value] -DATABASE_APP_TYPE_CHOICES = ( - (DATABASE_APP_TYPE_MYSQL, 'MySQL'), -) diff --git a/apps/applications/migrations/0008_auto_20210104_0435.py b/apps/applications/migrations/0008_auto_20210104_0435.py new file mode 100644 index 000000000..2942ecf6e --- /dev/null +++ b/apps/applications/migrations/0008_auto_20210104_0435.py @@ -0,0 +1,28 @@ +# Generated by Django 3.1 on 2021-01-03 20:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('perms', '0017_auto_20210104_0435'), + ('applications', '0007_auto_20201119_1110'), + ] + + operations = [ + migrations.DeleteModel( + name='DatabaseApp', + ), + migrations.DeleteModel( + name='K8sApp', + ), + migrations.AlterField( + model_name='application', + name='attrs', + field=models.JSONField(default=dict, verbose_name='Attrs'), + ), + migrations.DeleteModel( + name='RemoteApp', + ), + ] diff --git a/apps/applications/models/__init__.py b/apps/applications/models/__init__.py index b232a1ece..a12310aa4 100644 --- a/apps/applications/models/__init__.py +++ b/apps/applications/models/__init__.py @@ -1,4 +1 @@ from .application import * -from .remote_app import * -from .database_app import * -from .k8s_app import * diff --git a/apps/applications/models/application.py b/apps/applications/models/application.py index 1c8ae98f0..12bc4ff0a 100644 --- a/apps/applications/models/application.py +++ b/apps/applications/models/application.py @@ -1,128 +1,24 @@ -from itertools import chain - from django.db import models from django.utils.translation import ugettext_lazy as _ from orgs.mixins.models import OrgModelMixin from common.mixins import CommonModelMixin -from common.db.models import ChoiceSet - - -class DBType(ChoiceSet): - mysql = 'mysql', 'MySQL' - oracle = 'oracle', 'Oracle' - pgsql = 'postgresql', 'PostgreSQL' - mariadb = 'mariadb', 'MariaDB' - - @classmethod - def get_type_serializer_cls_mapper(cls): - from ..serializers import database_app - mapper = { - cls.mysql: database_app.MySQLAttrsSerializer, - cls.oracle: database_app.OracleAttrsSerializer, - cls.pgsql: database_app.PostgreAttrsSerializer, - cls.mariadb: database_app.MariaDBAttrsSerializer, - } - return mapper - - -class RemoteAppType(ChoiceSet): - chrome = 'chrome', 'Chrome' - mysql_workbench = 'mysql_workbench', 'MySQL Workbench' - vmware_client = 'vmware_client', 'vSphere Client' - custom = 'custom', _('Custom') - - @classmethod - def get_type_serializer_cls_mapper(cls): - from ..serializers import remote_app - mapper = { - cls.chrome: remote_app.ChromeAttrsSerializer, - cls.mysql_workbench: remote_app.MySQLWorkbenchAttrsSerializer, - cls.vmware_client: remote_app.VMwareClientAttrsSerializer, - cls.custom: remote_app.CustomRemoteAppAttrsSeralizers, - } - return mapper - - -class CloudType(ChoiceSet): - k8s = 'k8s', 'Kubernetes' - - @classmethod - def get_type_serializer_cls_mapper(cls): - from ..serializers import k8s_app - mapper = { - cls.k8s: k8s_app.K8sAttrsSerializer, - } - return mapper - - -class Category(ChoiceSet): - db = 'db', _('Database') - remote_app = 'remote_app', _('Remote app') - cloud = 'cloud', 'Cloud' - - @classmethod - def get_category_type_mapper(cls): - return { - cls.db: DBType, - cls.remote_app: RemoteAppType, - cls.cloud: CloudType - } - - @classmethod - def get_category_type_choices_mapper(cls): - return { - name: tp.choices - for name, tp in cls.get_category_type_mapper().items() - } - - @classmethod - def get_type_choices(cls, category): - return cls.get_category_type_choices_mapper().get(category, []) - - @classmethod - def get_all_type_choices(cls): - all_grouped_choices = tuple(cls.get_category_type_choices_mapper().values()) - return tuple(chain(*all_grouped_choices)) - - @classmethod - def get_all_type_serializer_mapper(cls): - mapper = {} - for tp in cls.get_category_type_mapper().values(): - mapper.update(tp.get_type_serializer_cls_mapper()) - return mapper - - @classmethod - def get_type_serializer_cls(cls, tp): - mapper = cls.get_all_type_serializer_mapper() - return mapper.get(tp, None) - - @classmethod - def get_category_serializer_mapper(cls): - from ..serializers import remote_app, database_app, k8s_app - return { - cls.db: database_app.DBAttrsSerializer, - cls.remote_app: remote_app.RemoteAppAttrsSerializer, - cls.cloud: k8s_app.CloudAttrsSerializer, - } - - @classmethod - def get_category_serializer_cls(cls, cg): - mapper = cls.get_category_serializer_mapper() - return mapper.get(cg, None) - - @classmethod - def get_no_password_serializer_cls(cls): - from ..serializers import common - return common.NoPasswordSerializer +from .. import const class Application(CommonModelMixin, OrgModelMixin): name = models.CharField(max_length=128, verbose_name=_('Name')) - domain = models.ForeignKey('assets.Domain', null=True, blank=True, related_name='applications', verbose_name=_("Domain"), on_delete=models.SET_NULL) - category = models.CharField(max_length=16, choices=Category.choices, verbose_name=_('Category')) - type = models.CharField(max_length=16, choices=Category.get_all_type_choices(), verbose_name=_('Type')) - attrs = models.JSONField() + category = models.CharField( + max_length=16, choices=const.ApplicationCategoryChoices.choices, verbose_name=_('Category') + ) + type = models.CharField( + max_length=16, choices=const.ApplicationTypeChoices.choices, verbose_name=_('Type') + ) + domain = models.ForeignKey( + 'assets.Domain', null=True, blank=True, related_name='applications', + on_delete=models.SET_NULL, verbose_name=_("Domain"), + ) + attrs = models.JSONField(default=dict, verbose_name=_('Attrs')) comment = models.TextField( max_length=128, default='', blank=True, verbose_name=_('Comment') ) @@ -136,5 +32,6 @@ class Application(CommonModelMixin, OrgModelMixin): type_display = self.get_type_display() return f'{self.name}({type_display})[{category_display}]' - def category_is_remote_app(self): - return self.category == Category.remote_app + @property + def category_remote_app(self): + return self.category == const.ApplicationCategoryChoices.remote_app.value diff --git a/apps/applications/models/database_app.py b/apps/applications/models/database_app.py deleted file mode 100644 index 3317f06a4..000000000 --- a/apps/applications/models/database_app.py +++ /dev/null @@ -1,42 +0,0 @@ -# coding: utf-8 -# - -import uuid -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -from orgs.mixins.models import OrgModelMixin -from common.mixins import CommonModelMixin -from .. import const - - -__all__ = ['DatabaseApp'] - - -class DatabaseApp(CommonModelMixin, OrgModelMixin): - id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=128, verbose_name=_('Name')) - type = models.CharField( - default=const.DATABASE_APP_TYPE_MYSQL, - choices=const.DATABASE_APP_TYPE_CHOICES, - max_length=128, verbose_name=_('Type') - ) - host = models.CharField( - max_length=128, verbose_name=_('Host'), db_index=True - ) - port = models.IntegerField(default=3306, verbose_name=_('Port')) - database = models.CharField( - max_length=128, blank=True, null=True, verbose_name=_('Database'), - db_index=True - ) - comment = models.TextField( - max_length=128, default='', blank=True, verbose_name=_('Comment') - ) - - def __str__(self): - return self.name - - class Meta: - unique_together = [('org_id', 'name'), ] - verbose_name = _("DatabaseApp") - ordering = ('name', ) diff --git a/apps/applications/models/k8s_app.py b/apps/applications/models/k8s_app.py deleted file mode 100644 index c4f0591ca..000000000 --- a/apps/applications/models/k8s_app.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.utils.translation import gettext_lazy as _ - -from common.db import models -from orgs.mixins.models import OrgModelMixin - - -class K8sApp(OrgModelMixin, models.JMSModel): - class TYPE(models.ChoiceSet): - K8S = 'k8s', _('Kubernetes') - - name = models.CharField(max_length=128, verbose_name=_('Name')) - type = models.CharField( - default=TYPE.K8S, choices=TYPE.choices, - max_length=128, verbose_name=_('Type') - ) - cluster = models.CharField(max_length=1024, verbose_name=_('Cluster')) - comment = models.TextField( - max_length=128, default='', blank=True, verbose_name=_('Comment') - ) - - def __str__(self): - return self.name - - class Meta: - unique_together = [('org_id', 'name'), ] - verbose_name = _('KubernetesApp') - ordering = ('name', ) diff --git a/apps/applications/models/remote_app.py b/apps/applications/models/remote_app.py deleted file mode 100644 index b9aee0ade..000000000 --- a/apps/applications/models/remote_app.py +++ /dev/null @@ -1,78 +0,0 @@ -# coding: utf-8 -# - -import uuid -from django.db import models -from django.utils.translation import ugettext_lazy as _ - -from orgs.mixins.models import OrgModelMixin -from common.fields.model import EncryptJsonDictTextField - -from .. import const - - -__all__ = [ - 'RemoteApp', -] - - -class RemoteApp(OrgModelMixin): - id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=128, verbose_name=_('Name')) - asset = models.ForeignKey( - 'assets.Asset', on_delete=models.CASCADE, verbose_name=_('Asset') - ) - type = models.CharField( - default=const.REMOTE_APP_TYPE_CHROME, - choices=const.REMOTE_APP_TYPE_CHOICES, - max_length=128, verbose_name=_('App type') - ) - path = models.CharField( - max_length=128, blank=False, null=False, - verbose_name=_('App path') - ) - params = EncryptJsonDictTextField( - max_length=4096, default={}, blank=True, null=True, - verbose_name=_('Parameters') - ) - created_by = models.CharField( - max_length=32, null=True, blank=True, verbose_name=_('Created by') - ) - date_created = models.DateTimeField( - auto_now_add=True, null=True, blank=True, verbose_name=_('Date created') - ) - comment = models.TextField( - max_length=128, default='', blank=True, verbose_name=_('Comment') - ) - - class Meta: - verbose_name = _("RemoteApp") - unique_together = [('org_id', 'name')] - ordering = ('name', ) - - def __str__(self): - return self.name - - @property - def parameters(self): - """ - 返回Guacamole需要的RemoteApp配置参数信息中的parameters参数 - """ - _parameters = list() - _parameters.append(self.type) - path = '\"%s\"' % self.path - _parameters.append(path) - for field in const.REMOTE_APP_TYPE_FIELDS_MAP[self.type]: - value = self.params.get(field['name']) - if value is None: - continue - _parameters.append(value) - _parameters = ' '.join(_parameters) - return _parameters - - @property - def asset_info(self): - return { - 'id': self.asset.id, - 'hostname': self.asset.hostname - } diff --git a/apps/applications/permissions.py b/apps/applications/permissions.py new file mode 100644 index 000000000..e56fd3652 --- /dev/null +++ b/apps/applications/permissions.py @@ -0,0 +1,9 @@ +from rest_framework import permissions + + +__all__ = ['IsRemoteApp'] + + +class IsRemoteApp(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + return obj.category_remote_app diff --git a/apps/applications/serializers/__init__.py b/apps/applications/serializers/__init__.py index 272b91982..3785f035d 100644 --- a/apps/applications/serializers/__init__.py +++ b/apps/applications/serializers/__init__.py @@ -1,5 +1,2 @@ from .application import * from .remote_app import * -from .database_app import * -from .k8s_app import * -from .common import * diff --git a/apps/applications/serializers/application.py b/apps/applications/serializers/application.py index 5ffcc65fb..644e3c791 100644 --- a/apps/applications/serializers/application.py +++ b/apps/applications/serializers/application.py @@ -4,6 +4,8 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from common.drf.fields import DynamicMappingField +from .attrs import get_attrs_field_dynamic_mapping_rules from .. import models @@ -15,6 +17,7 @@ __all__ = [ class ApplicationSerializer(BulkOrgResourceModelSerializer): category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category')) type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type')) + attrs = DynamicMappingField(mapping_rules=get_attrs_field_dynamic_mapping_rules()) class Meta: model = models.Application @@ -26,17 +29,7 @@ class ApplicationSerializer(BulkOrgResourceModelSerializer): 'created_by', 'date_created', 'date_updated', 'get_type_display', ] - def create(self, validated_data): - validated_data['attrs'] = validated_data.pop('attrs', {}) - instance = super().create(validated_data) - return instance - - def update(self, instance, validated_data): - new_attrs = validated_data.pop('attrs', {}) - instance = super().update(instance, validated_data) - attrs = instance.attrs - attrs.update(new_attrs) - instance.attrs = attrs - instance.save() - return instance - + def validate_attrs(self, attrs): + _attrs = self.instance.attrs if self.instance else {} + _attrs.update(attrs) + return _attrs diff --git a/apps/applications/serializers/attrs/__init__.py b/apps/applications/serializers/attrs/__init__.py new file mode 100644 index 000000000..bbbffd064 --- /dev/null +++ b/apps/applications/serializers/attrs/__init__.py @@ -0,0 +1 @@ +from .attrs import * diff --git a/apps/applications/serializers/attrs/attrs.py b/apps/applications/serializers/attrs/attrs.py new file mode 100644 index 000000000..adbe51820 --- /dev/null +++ b/apps/applications/serializers/attrs/attrs.py @@ -0,0 +1,94 @@ +import copy +from applications import const +from common.drf.fields import IgnoreSensitiveInfoReadOnlyJSONField +from . import category, type as application_type + + +__all__ = [ + 'get_attrs_field_dynamic_mapping_rules', 'get_attrs_field_mapping_rule_by_view', + 'get_serializer_by_application_type', +] + + +# application category +# -------------------- + +category_db = const.ApplicationCategoryChoices.db.value +category_remote_app = const.ApplicationCategoryChoices.remote_app.value +category_cloud = const.ApplicationCategoryChoices.cloud.value + + +# application type +# ---------------- + +# db +type_mysql = const.ApplicationTypeChoices.mysql.value +type_mariadb = const.ApplicationTypeChoices.mariadb.value +type_oracle = const.ApplicationTypeChoices.oracle.value +type_pgsql = const.ApplicationTypeChoices.pgsql.value +# remote-app +type_chrome = const.ApplicationTypeChoices.chrome.value +type_mysql_workbench = const.ApplicationTypeChoices.mysql_workbench.value +type_vmware_client = const.ApplicationTypeChoices.vmware_client.value +type_custom = const.ApplicationTypeChoices.custom.value +# cloud +type_k8s = const.ApplicationTypeChoices.k8s.value + + +# define `attrs` field `DynamicMappingField` mapping_rules +# ----------------------------------------------------- + + +__ATTRS_FIELD_DYNAMIC_MAPPING_RULES = { + 'default': IgnoreSensitiveInfoReadOnlyJSONField, + 'category': { + category_db: category.DBSerializer, + category_remote_app: category.RemoteAppSerializer, + category_cloud: category.CloudSerializer, + }, + 'type': { + # db + type_mysql: application_type.MySQLSerializer, + type_mariadb: application_type.MariaDBSerializer, + type_oracle: application_type.OracleSerializer, + type_pgsql: application_type.PostgreSerializer, + # remote-app + type_chrome: application_type.ChromeSerializer, + type_mysql_workbench: application_type.MySQLWorkbenchSerializer, + type_vmware_client: application_type.VMwareClientSerializer, + type_custom: application_type.CustomSerializer, + # cloud + type_k8s: application_type.K8SSerializer + } +} + + +# Note: +# The dynamic mapping rules of `attrs` field is obtained +# through call method `get_attrs_field_dynamic_mapping_rules` + +def get_attrs_field_dynamic_mapping_rules(): + return copy.deepcopy(__ATTRS_FIELD_DYNAMIC_MAPPING_RULES) + + +# get `attrs dynamic field` mapping rule by `view object` +# ---------------------------------------------------- + + +def get_attrs_field_mapping_rule_by_view(view): + query_type = view.request.query_params.get('type') + query_category = view.request.query_params.get('category') + if query_type: + mapping_rule = ['type', query_type] + elif query_category: + mapping_rule = ['category', query_category] + else: + mapping_rule = ['default'] + return mapping_rule + + +# get `category` mapping `serializer` +# ----------------------------------- + +def get_serializer_by_application_type(app_tp): + return __ATTRS_FIELD_DYNAMIC_MAPPING_RULES['type'].get(app_tp) diff --git a/apps/applications/serializers/attrs/category/__init__.py b/apps/applications/serializers/attrs/category/__init__.py new file mode 100644 index 000000000..1fd0beb3d --- /dev/null +++ b/apps/applications/serializers/attrs/category/__init__.py @@ -0,0 +1,3 @@ +from .remote_app import * +from .db import * +from .cloud import * diff --git a/apps/applications/serializers/attrs/category/cloud.py b/apps/applications/serializers/attrs/category/cloud.py new file mode 100644 index 000000000..60739d508 --- /dev/null +++ b/apps/applications/serializers/attrs/category/cloud.py @@ -0,0 +1,9 @@ +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')) diff --git a/apps/applications/serializers/attrs/category/db.py b/apps/applications/serializers/attrs/category/db.py new file mode 100644 index 000000000..cbe00570c --- /dev/null +++ b/apps/applications/serializers/attrs/category/db.py @@ -0,0 +1,16 @@ +# 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')) + port = serializers.IntegerField(label=_('Port')) + # 添加allow_null=True,兼容之前数据库中database字段为None的情况 + database = serializers.CharField( + max_length=128, required=True, allow_null=True, label=_('Database') + ) diff --git a/apps/applications/serializers/attrs/category/remote_app.py b/apps/applications/serializers/attrs/category/remote_app.py new file mode 100644 index 000000000..5520e2fc2 --- /dev/null +++ b/apps/applications/serializers/attrs/category/remote_app.py @@ -0,0 +1,48 @@ +# 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 +from assets.models import Asset + +logger = get_logger(__file__) + + +__all__ = ['RemoteAppSerializer'] + + +class CharPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): + + def to_internal_value(self, data): + instance = super().to_internal_value(data) + return str(instance.id) + + def to_representation(self, value): + # value is instance.id + if self.pk_field is not None: + return self.pk_field.to_representation(value) + return value + + +class RemoteAppSerializer(serializers.Serializer): + asset_info = serializers.SerializerMethodField() + asset = CharPrimaryKeyRelatedField(queryset=Asset.objects, required=False, label=_("Asset")) + path = serializers.CharField(max_length=128, label=_('Application path')) + + @staticmethod + def get_asset_info(obj): + asset_id = obj.get('asset') + if not asset_id or is_uuid(asset_id): + return {} + try: + asset = Asset.objects.filter(id=str(asset_id)).values_list('id', 'hostname') + except ObjectDoesNotExist as e: + logger.error(e) + return {} + if not asset: + return {} + asset_info = {'id': str(asset[0]), 'hostname': asset[1]} + return asset_info diff --git a/apps/applications/serializers/attrs/type/__init__.py b/apps/applications/serializers/attrs/type/__init__.py new file mode 100644 index 000000000..aaa597136 --- /dev/null +++ b/apps/applications/serializers/attrs/type/__init__.py @@ -0,0 +1,12 @@ + +from .mysql import * +from .mariadb import * +from .oracle import * +from .pgsql import * + +from .chrome import * +from .mysql_workbench import * +from .vmware_client import * +from .custom import * + +from .k8s import * diff --git a/apps/applications/serializers/attrs/type/chrome.py b/apps/applications/serializers/attrs/type/chrome.py new file mode 100644 index 000000000..eda68d0f0 --- /dev/null +++ b/apps/applications/serializers/attrs/type/chrome.py @@ -0,0 +1,25 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from ..category import RemoteAppSerializer + + +__all__ = ['ChromeSerializer'] + + +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 + ) + chrome_target = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Target URL') + ) + chrome_username = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Username') + ) + chrome_password = serializers.CharField( + max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password') + ) + diff --git a/apps/applications/serializers/attrs/type/custom.py b/apps/applications/serializers/attrs/type/custom.py new file mode 100644 index 000000000..927d88c15 --- /dev/null +++ b/apps/applications/serializers/attrs/type/custom.py @@ -0,0 +1,23 @@ + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from ..category import RemoteAppSerializer + + +__all__ = ['CustomSerializer'] + + +class CustomSerializer(RemoteAppSerializer): + custom_cmdline = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Operating parameter') + ) + custom_target = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Target url') + ) + custom_username = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Username') + ) + custom_password = serializers.CharField( + max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password') + ) diff --git a/apps/applications/serializers/attrs/type/k8s.py b/apps/applications/serializers/attrs/type/k8s.py new file mode 100644 index 000000000..c7caaeda0 --- /dev/null +++ b/apps/applications/serializers/attrs/type/k8s.py @@ -0,0 +1,8 @@ +from ..category import CloudSerializer + + +__all__ = ['K8SSerializer'] + + +class K8SSerializer(CloudSerializer): + pass diff --git a/apps/applications/serializers/attrs/type/mariadb.py b/apps/applications/serializers/attrs/type/mariadb.py new file mode 100644 index 000000000..d2bcb546f --- /dev/null +++ b/apps/applications/serializers/attrs/type/mariadb.py @@ -0,0 +1,8 @@ +from .mysql import MySQLSerializer + + +__all__ = ['MariaDBSerializer'] + + +class MariaDBSerializer(MySQLSerializer): + pass diff --git a/apps/applications/serializers/attrs/type/mysql.py b/apps/applications/serializers/attrs/type/mysql.py new file mode 100644 index 000000000..ec6ed1b4f --- /dev/null +++ b/apps/applications/serializers/attrs/type/mysql.py @@ -0,0 +1,15 @@ +from rest_framework import serializers +from django.utils.translation import ugettext_lazy as _ + +from ..category import DBSerializer + + +__all__ = ['MySQLSerializer'] + + +class MySQLSerializer(DBSerializer): + port = serializers.IntegerField(default=3306, label=_('Port')) + + + + diff --git a/apps/applications/serializers/attrs/type/mysql_workbench.py b/apps/applications/serializers/attrs/type/mysql_workbench.py new file mode 100644 index 000000000..a38cd34df --- /dev/null +++ b/apps/applications/serializers/attrs/type/mysql_workbench.py @@ -0,0 +1,30 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from ..category import RemoteAppSerializer + + +__all__ = ['MySQLWorkbenchSerializer'] + + +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 + ) + mysql_workbench_ip = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('IP') + ) + mysql_workbench_port = serializers.IntegerField( + required=False, label=_('Port') + ) + mysql_workbench_name = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Database') + ) + mysql_workbench_username = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Username') + ) + mysql_workbench_password = serializers.CharField( + max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password') + ) diff --git a/apps/applications/serializers/attrs/type/oracle.py b/apps/applications/serializers/attrs/type/oracle.py new file mode 100644 index 000000000..810098d21 --- /dev/null +++ b/apps/applications/serializers/attrs/type/oracle.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from django.utils.translation import ugettext_lazy as _ + +from ..category import DBSerializer + + +__all__ = ['OracleSerializer'] + + +class OracleSerializer(DBSerializer): + port = serializers.IntegerField(default=1521, label=_('Port')) + diff --git a/apps/applications/serializers/attrs/type/pgsql.py b/apps/applications/serializers/attrs/type/pgsql.py new file mode 100644 index 000000000..7166de21a --- /dev/null +++ b/apps/applications/serializers/attrs/type/pgsql.py @@ -0,0 +1,12 @@ +from rest_framework import serializers +from django.utils.translation import ugettext_lazy as _ + +from ..category import DBSerializer + + +__all__ = ['PostgreSerializer'] + + +class PostgreSerializer(DBSerializer): + port = serializers.IntegerField(default=5432, label=_('Port')) + diff --git a/apps/applications/serializers/attrs/type/vmware_client.py b/apps/applications/serializers/attrs/type/vmware_client.py new file mode 100644 index 000000000..c02b87a02 --- /dev/null +++ b/apps/applications/serializers/attrs/type/vmware_client.py @@ -0,0 +1,28 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from ..category import RemoteAppSerializer + + +__all__ = ['VMwareClientSerializer'] + + +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 + ) + vmware_target = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Target URL') + ) + vmware_username = serializers.CharField( + max_length=128, allow_blank=True, required=False, label=_('Username') + ) + vmware_password = serializers.CharField( + max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password') + ) diff --git a/apps/applications/serializers/common.py b/apps/applications/serializers/common.py deleted file mode 100644 index 31c0ba2ac..000000000 --- a/apps/applications/serializers/common.py +++ /dev/null @@ -1,11 +0,0 @@ -from rest_framework import serializers - - -class NoPasswordSerializer(serializers.JSONField): - def to_representation(self, value): - new_value = {} - for k, v in value.items(): - if 'password' not in k: - new_value[k] = v - return new_value - diff --git a/apps/applications/serializers/database_app.py b/apps/applications/serializers/database_app.py deleted file mode 100644 index ab30edba4..000000000 --- a/apps/applications/serializers/database_app.py +++ /dev/null @@ -1,50 +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 common.drf.serializers import AdaptedBulkListSerializer - -from .. import models - - -class DBAttrsSerializer(serializers.Serializer): - host = serializers.CharField(max_length=128, label=_('Host')) - port = serializers.IntegerField(label=_('Port')) - # 添加allow_null=True,兼容之前数据库中database字段为None的情况 - database = serializers.CharField(max_length=128, required=True, allow_null=True, label=_('Database')) - - -class MySQLAttrsSerializer(DBAttrsSerializer): - port = serializers.IntegerField(default=3306, label=_('Port')) - - -class PostgreAttrsSerializer(DBAttrsSerializer): - port = serializers.IntegerField(default=5432, label=_('Port')) - - -class OracleAttrsSerializer(DBAttrsSerializer): - port = serializers.IntegerField(default=1521, label=_('Port')) - - -class MariaDBAttrsSerializer(MySQLAttrsSerializer): - pass - - -class DatabaseAppSerializer(BulkOrgResourceModelSerializer): - - class Meta: - model = models.DatabaseApp - list_serializer_class = AdaptedBulkListSerializer - fields = [ - 'id', 'name', 'type', 'get_type_display', 'host', 'port', - 'database', 'comment', 'created_by', 'date_created', 'date_updated', - ] - read_only_fields = [ - 'created_by', 'date_created', 'date_updated' - 'get_type_display', - ] - extra_kwargs = { - 'get_type_display': {'label': _('Type for display')}, - } diff --git a/apps/applications/serializers/k8s_app.py b/apps/applications/serializers/k8s_app.py deleted file mode 100644 index 6bf685468..000000000 --- a/apps/applications/serializers/k8s_app.py +++ /dev/null @@ -1,27 +0,0 @@ -from rest_framework import serializers -from django.utils.translation import ugettext_lazy as _ - -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from .. import models - - -class CloudAttrsSerializer(serializers.Serializer): - cluster = serializers.CharField(max_length=1024, label=_('Cluster')) - - -class K8sAttrsSerializer(CloudAttrsSerializer): - pass - - -class K8sAppSerializer(BulkOrgResourceModelSerializer): - type_display = serializers.CharField(source='get_type_display', read_only=True, label=_('Type for display')) - - class Meta: - model = models.K8sApp - fields = [ - 'id', 'name', 'type', 'type_display', 'comment', 'created_by', - 'date_created', 'date_updated', 'cluster' - ] - read_only_fields = [ - 'id', 'created_by', 'date_created', 'date_updated', - ] diff --git a/apps/applications/serializers/remote_app.py b/apps/applications/serializers/remote_app.py index 48a784e33..773456cae 100644 --- a/apps/applications/serializers/remote_app.py +++ b/apps/applications/serializers/remote_app.py @@ -1,89 +1,14 @@ # coding: utf-8 # - -import copy -from django.utils.translation import ugettext_lazy as _ -from django.core.exceptions import ObjectDoesNotExist from rest_framework import serializers - -from common.drf.serializers import AdaptedBulkListSerializer -from common.drf.fields import CustomMetaDictField from common.utils import get_logger -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from assets.models import Asset +from ..models import Application -from .. import const -from ..models import RemoteApp, Category, Application logger = get_logger(__file__) -class CharPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): - - def to_internal_value(self, data): - instance = super().to_internal_value(data) - return str(instance.id) - - def to_representation(self, value): - # value is instance.id - if self.pk_field is not None: - return self.pk_field.to_representation(value) - return value - - -class RemoteAppAttrsSerializer(serializers.Serializer): - asset_info = serializers.SerializerMethodField() - asset = CharPrimaryKeyRelatedField(queryset=Asset.objects, required=False, label=_("Asset")) - path = serializers.CharField(max_length=128, label=_('Application path')) - - @staticmethod - def get_asset_info(obj): - asset_info = {} - asset_id = obj.get('asset') - if not asset_id: - return asset_info - try: - asset = Asset.objects.get(id=asset_id) - asset_info.update({ - 'id': str(asset.id), - 'hostname': asset.hostname - }) - except ObjectDoesNotExist as e: - logger.error(e) - return asset_info - - -class ChromeAttrsSerializer(RemoteAppAttrsSerializer): - REMOTE_APP_PATH = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe' - path = serializers.CharField(max_length=128, label=_('Application path'), default=REMOTE_APP_PATH) - chrome_target = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Target URL')) - chrome_username = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Username')) - chrome_password = serializers.CharField(max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')) - - -class MySQLWorkbenchAttrsSerializer(RemoteAppAttrsSerializer): - REMOTE_APP_PATH = 'C:\Program Files\MySQL\MySQL Workbench 8.0 CE\MySQLWorkbench.exe' - path = serializers.CharField(max_length=128, label=_('Application path'), default=REMOTE_APP_PATH) - mysql_workbench_ip = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('IP')) - mysql_workbench_port = serializers.IntegerField(required=False, label=_('Port')) - mysql_workbench_name = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Database')) - mysql_workbench_username = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Username')) - mysql_workbench_password = serializers.CharField(max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')) - - -class VMwareClientAttrsSerializer(RemoteAppAttrsSerializer): - REMOTE_APP_PATH = 'C:\Program Files (x86)\VMware\Infrastructure\Virtual Infrastructure Client\Launcher\VpxClient.exe' - path = serializers.CharField(max_length=128, label=_('Application path'), default=REMOTE_APP_PATH) - vmware_target = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Target URL')) - vmware_username = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Username')) - vmware_password = serializers.CharField(max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')) - - -class CustomRemoteAppAttrsSeralizers(RemoteAppAttrsSerializer): - custom_cmdline = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Operating parameter')) - custom_target = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Target url')) - custom_username = serializers.CharField(max_length=128, allow_blank=True, required=False, label=_('Username')) - custom_password = serializers.CharField(max_length=128, allow_blank=True, required=False, write_only=True, label=_('Password')) +__all__ = ['RemoteAppConnectionInfoSerializer'] class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer): @@ -97,94 +22,36 @@ class RemoteAppConnectionInfoSerializer(serializers.ModelSerializer): ] read_only_fields = ['parameter_remote_app'] + @staticmethod + def get_asset(obj): + return obj.attrs.get('asset') + @staticmethod def get_parameters(obj): """ 返回Guacamole需要的RemoteApp配置参数信息中的parameters参数 """ - serializer_cls = Category.get_type_serializer_cls(obj.type) - fields = serializer_cls().get_fields() - fields.pop('asset', None) - fields_name = list(fields.keys()) - attrs = obj.attrs - _parameters = list() - _parameters.append(obj.type) - for field_name in list(fields_name): - value = attrs.get(field_name, None) + from .attrs import get_serializer_by_application_type + serializer_class = get_serializer_by_application_type(obj.type) + fields = serializer_class().get_fields() + + parameters = [obj.type] + for field_name in list(fields.keys()): + if field_name in ['asset']: + continue + value = obj.attrs.get(field_name) if not value: continue if field_name == 'path': value = '\"%s\"' % value - _parameters.append(str(value)) - _parameters = ' '.join(_parameters) - return _parameters + parameters.append(str(value)) + + parameters = ' '.join(parameters) + return parameters def get_parameter_remote_app(self, obj): - parameters = self.get_parameters(obj) - parameter = { - 'program': const.REMOTE_APP_BOOT_PROGRAM_NAME, + return { + 'program': '||jmservisor', 'working_directory': '', - 'parameters': parameters, + 'parameters': self.get_parameters(obj) } - return parameter - - @staticmethod - def get_asset(obj): - return obj.attrs.get('asset') - - -# TODO: DELETE -class RemoteAppParamsDictField(CustomMetaDictField): - type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP - default_type = const.REMOTE_APP_TYPE_CHROME - convert_key_remove_type_prefix = False - convert_key_to_upper = False - - -# TODO: DELETE -class RemoteAppSerializer(BulkOrgResourceModelSerializer): - params = RemoteAppParamsDictField(label=_('Parameters')) - type_fields_map = const.REMOTE_APP_TYPE_FIELDS_MAP - - class Meta: - model = RemoteApp - list_serializer_class = AdaptedBulkListSerializer - fields = [ - 'id', 'name', 'asset', 'asset_info', 'type', 'get_type_display', - 'path', 'params', 'date_created', 'created_by', 'comment', - ] - read_only_fields = [ - 'created_by', 'date_created', 'asset_info', - 'get_type_display' - ] - extra_kwargs = { - 'asset_info': {'label': _('Asset info')}, - 'get_type_display': {'label': _('Type for display')}, - } - - def process_params(self, instance, validated_data): - new_params = copy.deepcopy(validated_data.get('params', {})) - tp = validated_data.get('type', '') - - if tp != instance.type: - return new_params - - old_params = instance.params - fields = self.type_fields_map.get(instance.type, []) - for field in fields: - if not field.get('write_only', False): - continue - field_name = field['name'] - new_value = new_params.get(field_name, '') - old_value = old_params.get(field_name, '') - field_value = new_value if new_value else old_value - new_params[field_name] = field_value - - return new_params - - def update(self, instance, validated_data): - params = self.process_params(instance, validated_data) - validated_data['params'] = params - return super().update(instance, validated_data) - - diff --git a/apps/applications/serializers/utils.py b/apps/applications/serializers/utils.py new file mode 100644 index 000000000..7de5e0c46 --- /dev/null +++ b/apps/applications/serializers/utils.py @@ -0,0 +1,16 @@ +from .attrs import get_attrs_field_mapping_rule_by_view + +__all__ = [ + 'get_dynamic_mapping_fields_mapping_rule_by_view' +] + + +# +# get `dynamic fields` mapping rule by `view object` +# ---------------------------------------------------- + + +def get_dynamic_mapping_fields_mapping_rule_by_view(view): + return { + 'attrs': get_attrs_field_mapping_rule_by_view(view=view), + } diff --git a/apps/applications/urls/api_urls.py b/apps/applications/urls/api_urls.py index bfe685d4c..ab463a401 100644 --- a/apps/applications/urls/api_urls.py +++ b/apps/applications/urls/api_urls.py @@ -1,26 +1,20 @@ # coding:utf-8 # - -from django.urls import path, re_path +from django.urls import path from rest_framework_bulk.routes import BulkRouter - -from common import api as capi from .. import api + app_name = 'applications' + router = BulkRouter() router.register(r'applications', api.ApplicationViewSet, 'application') -router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app') -router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app') -router.register(r'k8s-apps', api.K8sAppViewSet, 'k8s-app') + urlpatterns = [ path('remote-apps//connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'), ] -old_version_urlpatterns = [ - re_path('(?Premote-app)/.*', capi.redirect_plural_name_api) -] -urlpatterns += router.urls + old_version_urlpatterns +urlpatterns += router.urls diff --git a/apps/applications/urls/views_urls.py b/apps/applications/urls/views_urls.py deleted file mode 100644 index 2b09baf9f..000000000 --- a/apps/applications/urls/views_urls.py +++ /dev/null @@ -1,7 +0,0 @@ -# coding:utf-8 -from django.urls import path - -app_name = 'applications' - -urlpatterns = [ -] diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index 082aaa412..6491f003f 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -205,16 +205,15 @@ class SystemUser(BaseUser): return assets @classmethod - def get_protocol_by_application_type(cls, application_type): - from applications.models import Category - remote_app_types = list(dict(Category.get_type_choices(Category.remote_app)).keys()) - if application_type in remote_app_types: + def get_protocol_by_application_type(cls, app_type): + from applications.const import ApplicationTypeChoices + if app_type in ApplicationTypeChoices.remote_app_types(): return cls.PROTOCOL_RDP - cloud_types = list(dict(Category.get_type_choices(Category.cloud)).keys()) - db_types = list(dict(Category.get_type_choices(Category.db)).keys()) - other_types = [*cloud_types, *db_types] - if application_type in other_types: - return application_type + protocol = None + other_types = [*ApplicationTypeChoices.db_types(), *ApplicationTypeChoices.cloud_types()] + if app_type in other_types and app_type in cls.APPLICATION_CATEGORY_PROTOCOLS: + protocol = app_type + return protocol class Meta: ordering = ['name'] diff --git a/apps/common/drf/fields.py b/apps/common/drf/fields.py index 80db9cd80..b96d04bf8 100644 --- a/apps/common/drf/fields.py +++ b/apps/common/drf/fields.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- # -import data_tree +import copy from rest_framework import serializers __all__ = [ 'DynamicMappingField', 'ReadableHiddenField', - 'CustomMetaDictField', + 'CustomMetaDictField', 'IgnoreSensitiveInfoReadOnlyJSONField', ] @@ -49,18 +49,12 @@ class DynamicMappingField(serializers.Field): ''.format(type(mapping_rules)) ) - assert 'default' in mapping_rules, ( - "mapping_rules['default'] is a required, but only get `{}`" - "".format(list(mapping_rules.keys())) - ) - - self.mapping_rules = mapping_rules - self.mapping_tree = self._build_mapping_tree() + self.__mapping_rules = mapping_rules super().__init__(*args, **kwargs) - def _build_mapping_tree(self): - tree = data_tree.Data_tree_node(arg_data=self.mapping_rules) - return tree + @property + def mapping_rules(self): + return copy.deepcopy(self.__mapping_rules) def to_internal_value(self, data): """ 实际是一个虚拟字段所以不返回任何值 """ @@ -70,6 +64,29 @@ class DynamicMappingField(serializers.Field): """ 实际是一个虚拟字段所以不返回任何值 """ pass + +# A Ignore read-only fields for sensitive information +# ---------------------------------------------------------- + + +class IgnoreSensitiveInfoReadOnlyJSONField(serializers.JSONField): + """ A ignore read-only fields for sensitive information """ + + def __init__(self, **kwargs): + kwargs['read_only'] = True + super().__init__(**kwargs) + + def to_representation(self, value): + sensitive_ignored_value = {} + sensitive_names = ['password'] + for field_name, field_value in value.items(): + for sensitive_name in sensitive_names: + if sensitive_name in field_name.lower(): + continue + sensitive_ignored_value[field_name] = field_value + return super().to_representation(sensitive_ignored_value) + + # # ReadableHiddenField # ------------------- diff --git a/apps/common/drf/serializers.py b/apps/common/drf/serializers.py index 9abef8ac8..e32f70213 100644 --- a/apps/common/drf/serializers.py +++ b/apps/common/drf/serializers.py @@ -7,6 +7,7 @@ from rest_framework_bulk.serializers import BulkListSerializer from common.mixins import BulkListSerializerMixin from common.drf.fields import DynamicMappingField from common.mixins.serializers import BulkSerializerMixin +from common.utils import QuickLookupDict __all__ = [ 'IncludeDynamicMappingFieldSerializerMetaClass', @@ -69,18 +70,29 @@ class IncludeDynamicMappingFieldSerializerMetaClass(serializers.SerializerMetacl def get_dynamic_mapping_fields(mcs, bases, attrs): fields = {} - fields_mapping_rules = attrs.get('dynamic_mapping_fields_mapping_rule') + # get `fields mapping rule` from attrs `dynamic_mapping_fields_mapping_rule` + fields_mapping_rule = attrs.get('dynamic_mapping_fields_mapping_rule') - assert isinstance(fields_mapping_rules, dict), ( + # check `fields_mapping_rule` type + assert isinstance(fields_mapping_rule, dict), ( '`dynamic_mapping_fields_mapping_rule` must be `dict` type , but get `{}`' - ''.format(type(fields_mapping_rules)) + ''.format(type(fields_mapping_rule)) ) - fields_mapping_rules = copy.deepcopy(fields_mapping_rules) - + # get `serializer class` declared fields declared_fields = mcs._get_declared_fields(bases, attrs) + declared_fields_names = list(declared_fields.keys()) - for field_name, field_mapping_rule in fields_mapping_rules.items(): + fields_mapping_rule = copy.deepcopy(fields_mapping_rule) + + for field_name, field_mapping_rule in fields_mapping_rule.items(): + + if field_name not in declared_fields_names: + continue + + declared_field = declared_fields[field_name] + if not isinstance(declared_field, DynamicMappingField): + continue assert isinstance(field_mapping_rule, (list, str)), ( '`dynamic_mapping_fields_mapping_rule.field_mapping_rule` ' @@ -90,34 +102,21 @@ class IncludeDynamicMappingFieldSerializerMetaClass(serializers.SerializerMetacl ''.format(type(field_mapping_rule), field_mapping_rule) ) - if field_name not in declared_fields.keys(): - continue - - declared_field = declared_fields[field_name] - if not isinstance(declared_field, DynamicMappingField): - continue - - dynamic_field = declared_field - - mapping_tree = dynamic_field.mapping_tree.copy() - - def get_field(rule): - return mapping_tree.get(arg_path=rule) - if isinstance(field_mapping_rule, str): field_mapping_rule = field_mapping_rule.split('.') - field_mapping_rule[-1] = field_mapping_rule[-1] or 'default' + # construct `field mapping rules` sequence list + field_mapping_rules = [ + field_mapping_rule, + copy.deepcopy(field_mapping_rule)[:-1] + ['default'], + ['default'] + ] - field = get_field(rule=field_mapping_rule) + dynamic_field = declared_field - if not field: - field_mapping_rule[-1] = 'default' - field = get_field(rule=field_mapping_rule) + field_finder = QuickLookupDict(dynamic_field.mapping_rules) - if field is None: - field_mapping_rule = ['default'] - field = get_field(rule=field_mapping_rule) + field = field_finder.find_one(key_paths=field_mapping_rules) if isinstance(field, type): field = field() diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index 25b2e771b..3b0d0f9fa 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # import re +import data_tree from collections import OrderedDict from itertools import chain import logging @@ -10,6 +11,8 @@ from functools import wraps import time import ipaddress import psutil +from django.utils.translation import ugettext_lazy as _ +from ..exceptions import JMSException UUID_PATTERN = re.compile(r'\w{8}(-\w{4}){3}-\w{12}') @@ -251,3 +254,152 @@ def get_disk_usage(): mount_points = [p.mountpoint for p in partitions] usages = {p: psutil.disk_usage(p) for p in mount_points} return usages + + +# Verify that `value` is in `choices` and throw an `JMSException` +# --------------------------------------------------------------- + + +def check_value_in_choices(value, choices, **kwargs): + # get raise parameters from kwargs + raise_exception = kwargs.get('raise_exception', False) + raise_error_msg = kwargs.get('raise_error_msg', None) + raise_reverse = kwargs.get('raise_reverse', False) + + def should_raise(): + """ + Simplify the following logic: + + if raise_exception: + if raise_reverse and value_in_choices: + return True + else: + return False + + if not raise_reverse and not value_in_choices: + return True + else: + return False + else: + return False + """ + return raise_exception and raise_reverse == value_in_choices + + value_in_choices = True if value in choices else False + + if not should_raise(): + return value_in_choices + + if raise_error_msg is None: + raise_error_msg = _('Value `{}` is not in Choices: `{}`'.format(value, choices)) + + raise JMSException(raise_error_msg) + + +# Quick lookup dict +# ----------------- + + +class QuickLookupDict(object): + """ + 说明: + dict 类型数据的快速查找 + 作用: + 可根据指定 key 的深度 path 快速查找出对应的 value 值 + 依赖: + data-tree==0.0.1 + 实现: + 通过对 data-tree 库的封装来实现 + """ + + def __init__(self, data, key_delimiter='.'): + self._check_data_type(data, type_choices=(dict, ), error='Expected `data` type is dict') + self.data = data + self.key_delimiter = key_delimiter + self._data_tree = self._get_data_tree(data, key_delimiter) + + # Method encapsulated of `data-tree` + # ---------------------------------- + + @staticmethod + def _get_data_tree(data, key_delimiter): + tree = data_tree.Data_tree_node( + arg_data=data, arg_string_delimiter_for_path=key_delimiter + ) + return tree + + def _get_data_tree_node(self, path): + return self._data_tree.get(arg_path=path) + + @staticmethod + def _get_data_tree_node_original_data(tree_node): + if isinstance(tree_node, data_tree.Data_tree_node): + data = tree_node.get_data_in_format_for_export() + else: + data = tree_node + return data + + # Method called internally + # ------------------------ + + @staticmethod + def _check_data_type(data, type_choices, error=None): + error = error or '`data` type error, {} => {}'.format(type(data), type_choices) + assert isinstance(data, type_choices), error + + @staticmethod + def _check_object_callable(_object): + if _object is None: + return False + if not callable(_object): + return False + return True + + # Method called externally + # ------------------------ + + def get(self, key_path, default=None): + error = 'key_path - can be either a list of keys, or a delimited string.' + self._check_data_type(key_path, (list, str,), error=error) + + tree_node = self._get_data_tree_node(key_path) + if tree_node is None: + return default + value = self._get_data_tree_node_original_data(tree_node) + return value + + def get_many(self, key_paths, default=None): + values = [ + self.get(key_path, default=default) for key_path in key_paths + ] + return values + + def find_one(self, key_paths, default=None, callable_filter=None): + """ + 按照 key_paths 顺序查找,返回第一个满足 `callable_filter` 规则的值 + """ + + def get_data_filter(): + if self._check_object_callable(callable_filter): + return callable_filter + return self.__default_find_callable_filter + + _filter = get_data_filter() + + for key_path in key_paths: + value = self.get(key_path=key_path) + if _filter(key_path, value): + return value + + return default + + # Method default + # -------------- + + @staticmethod + def __default_find_callable_filter(key_path, value): + return value is not None + + + + diff --git a/apps/perms/api/__init__.py b/apps/perms/api/__init__.py index 50957c154..d9a7afe0f 100644 --- a/apps/perms/api/__init__.py +++ b/apps/perms/api/__init__.py @@ -3,15 +3,4 @@ from .asset import * from .application import * - -# TODO: 删除 -from .remote_app_permission import * -from .remote_app_permission_relation import * -from .user_remote_app_permission import * -from .database_app_permission import * -from .database_app_permission_relation import * -from .user_database_app_permission import * from .system_user_permission import * -from .k8s_app_permission import * -from .k8s_app_permission_relation import * -from .user_k8s_app_permission import * diff --git a/apps/perms/api/application/user_group_permission.py b/apps/perms/api/application/user_group_permission.py index 4c112c183..661995d43 100644 --- a/apps/perms/api/application/user_group_permission.py +++ b/apps/perms/api/application/user_group_permission.py @@ -5,8 +5,9 @@ from django.db.models import Q from rest_framework.generics import ListAPIView from common.permissions import IsOrgAdminOrAppUser +from common.mixins.api import CommonApiMixin from applications.models import Application -from applications.api.mixin import ApplicationAttrsSerializerViewMixin +from applications.api.mixin import ApplicationViewMixin from perms import serializers __all__ = [ @@ -14,9 +15,9 @@ __all__ = [ ] -class UserGroupGrantedApplicationsApi(ApplicationAttrsSerializerViewMixin, ListAPIView): +class UserGroupGrantedApplicationsApi(ApplicationViewMixin, CommonApiMixin, ListAPIView): """ - 获取用户组直接授权的资产 + 获取用户组直接授权的应用 """ permission_classes = (IsOrgAdminOrAppUser,) serializer_class = serializers.ApplicationGrantedSerializer diff --git a/apps/perms/api/application/user_permission/user_permission_applications.py b/apps/perms/api/application/user_permission/user_permission_applications.py index 848e22304..eab6a6b97 100644 --- a/apps/perms/api/application/user_permission/user_permission_applications.py +++ b/apps/perms/api/application/user_permission/user_permission_applications.py @@ -3,8 +3,9 @@ from rest_framework.generics import ListAPIView from rest_framework.response import Response +from common.mixins.api import CommonApiMixin from applications.api.mixin import ( - SerializeApplicationToTreeNodeMixin, ApplicationAttrsSerializerViewMixin + SerializeApplicationToTreeNodeMixin, ApplicationViewMixin ) from perms import serializers from perms.api.asset.user_permission.mixin import ForAdminMixin, ForUserMixin @@ -21,7 +22,7 @@ __all__ = [ ] -class AllGrantedApplicationsMixin(ApplicationAttrsSerializerViewMixin, ListAPIView): +class AllGrantedApplicationsMixin(ApplicationViewMixin, CommonApiMixin, ListAPIView): only_fields = serializers.ApplicationGrantedSerializer.Meta.only_fields serializer_class = serializers.ApplicationGrantedSerializer filter_fields = ['id', 'name', 'category', 'type', 'comment'] diff --git a/apps/perms/api/base.py b/apps/perms/api/base.py index b56c161d7..3d9c8672d 100644 --- a/apps/perms/api/base.py +++ b/apps/perms/api/base.py @@ -1,5 +1,3 @@ -from django.db.models import F -from orgs.mixins.api import OrgRelationMixin from django.db.models import Q from common.permissions import IsOrgAdmin from common.utils import get_object_or_none @@ -8,9 +6,7 @@ from assets.models import SystemUser from users.models import User, UserGroup -__all__ = [ - 'RelationViewSet', 'BasePermissionViewSet' -] +__all__ = ['BasePermissionViewSet'] class BasePermissionViewSet(OrgBulkModelViewSet): @@ -99,10 +95,3 @@ class BasePermissionViewSet(OrgBulkModelViewSet): queryset = self.filter_keyword(queryset) queryset = queryset.distinct() return queryset - - -class RelationViewSet(OrgRelationMixin, OrgBulkModelViewSet): - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate(**{f'{self.from_field}_display': F(f'{self.from_field}__name')}) - return queryset diff --git a/apps/perms/api/database_app_permission.py b/apps/perms/api/database_app_permission.py deleted file mode 100644 index 4b43d347f..000000000 --- a/apps/perms/api/database_app_permission.py +++ /dev/null @@ -1,21 +0,0 @@ -# coding: utf-8 -# - -from orgs.mixins.api import OrgBulkModelViewSet - -from .. import models, serializers -from common.permissions import IsOrgAdmin - - -__all__ = ['DatabaseAppPermissionViewSet'] - - -class DatabaseAppPermissionViewSet(OrgBulkModelViewSet): - model = models.DatabaseAppPermission - serializer_classes = { - 'default': serializers.DatabaseAppPermissionSerializer, - 'display': serializers.DatabaseAppPermissionListSerializer - } - filter_fields = ('name',) - search_fields = filter_fields - permission_classes = (IsOrgAdmin,) diff --git a/apps/perms/api/database_app_permission_relation.py b/apps/perms/api/database_app_permission_relation.py deleted file mode 100644 index 572896e34..000000000 --- a/apps/perms/api/database_app_permission_relation.py +++ /dev/null @@ -1,120 +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 common.permissions import IsOrgAdmin -from .base import RelationViewSet -from .. import models, serializers - -__all__ = [ - 'DatabaseAppPermissionUserRelationViewSet', - 'DatabaseAppPermissionUserGroupRelationViewSet', - 'DatabaseAppPermissionAllUserListApi', - 'DatabaseAppPermissionDatabaseAppRelationViewSet', - 'DatabaseAppPermissionAllDatabaseAppListApi', - 'DatabaseAppPermissionSystemUserRelationViewSet', -] - - -class DatabaseAppPermissionUserRelationViewSet(RelationViewSet): - serializer_class = serializers.DatabaseAppPermissionUserRelationSerializer - m2m_field = models.DatabaseAppPermission.users.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'user', 'databaseapppermission' - ] - search_fields = ('user__name', 'user__username', 'databaseapppermission__name') - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate(user_display=F('user__name')) - return queryset - - -class DatabaseAppPermissionUserGroupRelationViewSet(RelationViewSet): - serializer_class = serializers.DatabaseAppPermissionUserGroupRelationSerializer - m2m_field = models.DatabaseAppPermission.user_groups.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', "usergroup", "databaseapppermission" - ] - search_fields = ["usergroup__name", "databaseapppermission__name"] - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset \ - .annotate(usergroup_display=F('usergroup__name')) - return queryset - - -class DatabaseAppPermissionAllUserListApi(generics.ListAPIView): - permission_classes = (IsOrgAdmin,) - serializer_class = serializers.DatabaseAppPermissionAllUserSerializer - filter_fields = ("username", "name") - search_fields = filter_fields - - def get_queryset(self): - pk = self.kwargs.get("pk") - perm = get_object_or_404(models.DatabaseAppPermission, pk=pk) - users = perm.get_all_users().only( - *self.serializer_class.Meta.only_fields - ) - return users - - -class DatabaseAppPermissionDatabaseAppRelationViewSet(RelationViewSet): - serializer_class = serializers.DatabaseAppPermissionDatabaseAppRelationSerializer - m2m_field = models.DatabaseAppPermission.database_apps.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'databaseapp', 'databaseapppermission', - ] - search_fields = [ - "id", "databaseapp__name", "databaseapppermission__name" - ] - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset \ - .annotate(databaseapp_display=F('databaseapp__name')) - return queryset - - -class DatabaseAppPermissionAllDatabaseAppListApi(generics.ListAPIView): - permission_classes = (IsOrgAdmin,) - serializer_class = serializers.DatabaseAppPermissionAllDatabaseAppSerializer - filter_fields = ("name",) - search_fields = filter_fields - - def get_queryset(self): - pk = self.kwargs.get("pk") - perm = get_object_or_404(models.DatabaseAppPermission, pk=pk) - database_apps = perm.get_all_database_apps().only( - *self.serializer_class.Meta.only_fields - ) - return database_apps - - -class DatabaseAppPermissionSystemUserRelationViewSet(RelationViewSet): - serializer_class = serializers.DatabaseAppPermissionSystemUserRelationSerializer - m2m_field = models.DatabaseAppPermission.system_users.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'systemuser', 'databaseapppermission' - ] - search_fields = [ - 'databaseapppermission__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/api/k8s_app_permission.py b/apps/perms/api/k8s_app_permission.py deleted file mode 100644 index b1111bd7b..000000000 --- a/apps/perms/api/k8s_app_permission.py +++ /dev/null @@ -1,21 +0,0 @@ -# coding: utf-8 -# - -from orgs.mixins.api import OrgBulkModelViewSet - -from .. import models, serializers -from common.permissions import IsOrgAdmin - - -__all__ = ['K8sAppPermissionViewSet'] - - -class K8sAppPermissionViewSet(OrgBulkModelViewSet): - model = models.K8sAppPermission - serializer_classes = { - 'default': serializers.K8sAppPermissionSerializer, - 'display': serializers.K8sAppPermissionListSerializer - } - filter_fields = ('name',) - search_fields = filter_fields - permission_classes = (IsOrgAdmin,) diff --git a/apps/perms/api/k8s_app_permission_relation.py b/apps/perms/api/k8s_app_permission_relation.py deleted file mode 100644 index 443f32204..000000000 --- a/apps/perms/api/k8s_app_permission_relation.py +++ /dev/null @@ -1,111 +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 common.permissions import IsOrgAdmin -from .base import RelationViewSet -from .. import models, serializers - - -class K8sAppPermissionUserRelationViewSet(RelationViewSet): - serializer_class = serializers.K8sAppPermissionUserRelationSerializer - m2m_field = models.K8sAppPermission.users.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'user', 'k8sapppermission' - ] - search_fields = ('user__name', 'user__username', 'k8sapppermission__name') - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate(user_display=F('user__name')) - return queryset - - -class K8sAppPermissionUserGroupRelationViewSet(RelationViewSet): - serializer_class = serializers.K8sAppPermissionUserGroupRelationSerializer - m2m_field = models.K8sAppPermission.user_groups.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', "usergroup", "k8sapppermission" - ] - search_fields = ["usergroup__name", "k8sapppermission__name"] - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset \ - .annotate(usergroup_display=F('usergroup__name')) - return queryset - - -class K8sAppPermissionAllUserListApi(generics.ListAPIView): - permission_classes = (IsOrgAdmin,) - serializer_class = serializers.K8sAppPermissionAllUserSerializer - filter_fields = ("username", "name") - search_fields = filter_fields - - def get_queryset(self): - pk = self.kwargs.get("pk") - perm = get_object_or_404(models.K8sAppPermission, pk=pk) - users = perm.get_all_users().only( - *self.serializer_class.Meta.only_fields - ) - return users - - -class K8sAppPermissionK8sAppRelationViewSet(RelationViewSet): - serializer_class = serializers.K8sAppPermissionK8sAppRelationSerializer - m2m_field = models.K8sAppPermission.k8s_apps.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'k8sapp', 'k8sapppermission', - ] - search_fields = [ - "id", "k8sapp__name", "k8sapppermission__name" - ] - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset \ - .annotate(k8sapp_display=F('k8sapp__name')) - return queryset - - -class K8sAppPermissionAllK8sAppListApi(generics.ListAPIView): - permission_classes = (IsOrgAdmin,) - serializer_class = serializers.K8sAppPermissionAllK8sAppSerializer - filter_fields = ("name",) - search_fields = filter_fields - - def get_queryset(self): - pk = self.kwargs.get("pk") - perm = get_object_or_404(models.K8sAppPermission, pk=pk) - database_apps = perm.get_all_k8s_apps().only( - *self.serializer_class.Meta.only_fields - ) - return database_apps - - -class K8sAppPermissionSystemUserRelationViewSet(RelationViewSet): - serializer_class = serializers.K8sAppPermissionSystemUserRelationSerializer - m2m_field = models.K8sAppPermission.system_users.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'systemuser', 'k8sapppermission' - ] - search_fields = [ - 'k8sapppermission__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/api/mixin.py b/apps/perms/api/mixin.py deleted file mode 100644 index 43c96dc01..000000000 --- a/apps/perms/api/mixin.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -# - -from rest_framework.generics import get_object_or_404 -from common.permissions import IsValidUser, IsOrgAdminOrAppUser -from common.utils import get_logger -from orgs.utils import set_to_root_org, set_current_org, get_current_org -from ..hands import User, UserGroup - - -logger = get_logger(__name__) - -__all__ = [ - 'UserPermissionMixin', 'UserGroupPermissionMixin', -] - - -class UserPermissionMixin: - permission_classes = (IsOrgAdminOrAppUser,) - current_org = None - obj = None - - def initial(self, *args, **kwargs): - super().initial(*args, **kwargs) - self.obj = self.get_obj() - - def get_obj(self): - user_id = self.kwargs.get('pk', '') - if user_id: - user = get_object_or_404(User, id=user_id) - else: - self.current_org = get_current_org() - set_to_root_org() - user = self.request.user - return user - - def get_permissions(self): - if self.kwargs.get('pk') is None: - self.permission_classes = (IsValidUser,) - return super().get_permissions() - - def finalize_response(self, request, response, *args, **kwargs): - response = super().finalize_response(request, response, *args, **kwargs) - org = getattr(self, 'current_org', None) - if org: - set_current_org(org) - return response - - -class UserGroupPermissionMixin: - obj = None - - def get_obj(self): - user_group_id = self.kwargs.get('pk', '') - user_group = get_object_or_404(UserGroup, id=user_group_id) - return user_group - - diff --git a/apps/perms/api/remote_app_permission.py b/apps/perms/api/remote_app_permission.py deleted file mode 100644 index cb1998675..000000000 --- a/apps/perms/api/remote_app_permission.py +++ /dev/null @@ -1,98 +0,0 @@ -# coding: utf-8 -# - -from rest_framework.views import Response - -from common.permissions import IsOrgAdmin -from orgs.mixins.api import OrgModelViewSet -from orgs.mixins import generics -from ..models import RemoteAppPermission -from ..serializers import ( - RemoteAppPermissionSerializer, - RemoteAppPermissionUpdateUserSerializer, - RemoteAppPermissionUpdateRemoteAppSerializer, -) - -__all__ = [ - 'RemoteAppPermissionViewSet', - 'RemoteAppPermissionAddUserApi', 'RemoteAppPermissionAddRemoteAppApi', - 'RemoteAppPermissionRemoveUserApi', 'RemoteAppPermissionRemoveRemoteAppApi', -] - - -class RemoteAppPermissionViewSet(OrgModelViewSet): - model = RemoteAppPermission - filter_fields = ('name', ) - search_fields = filter_fields - serializer_class = RemoteAppPermissionSerializer - permission_classes = (IsOrgAdmin,) - - -class RemoteAppPermissionAddUserApi(generics.RetrieveUpdateAPIView): - model = RemoteAppPermission - permission_classes = (IsOrgAdmin,) - serializer_class = RemoteAppPermissionUpdateUserSerializer - - def update(self, request, *args, **kwargs): - perm = self.get_object() - serializer = self.serializer_class(data=request.data) - if serializer.is_valid(): - users = serializer.validated_data.get('users') - if users: - perm.users.add(*tuple(users)) - return Response({"msg": "ok"}) - else: - return Response({"error": serializer.errors}) - - -class RemoteAppPermissionRemoveUserApi(generics.RetrieveUpdateAPIView): - model = RemoteAppPermission - permission_classes = (IsOrgAdmin,) - serializer_class = RemoteAppPermissionUpdateUserSerializer - - def update(self, request, *args, **kwargs): - perm = self.get_object() - serializer = self.serializer_class(data=request.data) - if serializer.is_valid(): - users = serializer.validated_data.get('users') - if users: - perm.users.remove(*tuple(users)) - return Response({"msg": "ok"}) - else: - return Response({"error": serializer.errors}) - - -class RemoteAppPermissionAddRemoteAppApi(generics.RetrieveUpdateAPIView): - model = RemoteAppPermission - permission_classes = (IsOrgAdmin,) - serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer - - def update(self, request, *args, **kwargs): - perm = self.get_object() - serializer = self.serializer_class(data=request.data) - if serializer.is_valid(): - remote_apps = serializer.validated_data.get('remote_apps') - if remote_apps: - perm.remote_apps.add(*tuple(remote_apps)) - return Response({"msg": "ok"}) - else: - return Response({"error": serializer.errors}) - - -class RemoteAppPermissionRemoveRemoteAppApi(generics.RetrieveUpdateAPIView): - model = RemoteAppPermission - permission_classes = (IsOrgAdmin,) - serializer_class = RemoteAppPermissionUpdateRemoteAppSerializer - - def update(self, request, *args, **kwargs): - perm = self.get_object() - serializer = self.serializer_class(data=request.data) - if serializer.is_valid(): - remote_apps = serializer.validated_data.get('remote_apps') - if remote_apps: - perm.remote_apps.remove(*tuple(remote_apps)) - return Response({"msg": "ok"}) - else: - return Response({"error": serializer.errors}) - - diff --git a/apps/perms/api/remote_app_permission_relation.py b/apps/perms/api/remote_app_permission_relation.py deleted file mode 100644 index fe0ab7355..000000000 --- a/apps/perms/api/remote_app_permission_relation.py +++ /dev/null @@ -1,79 +0,0 @@ -# coding: utf-8 -# -from perms.api.base import RelationViewSet -from rest_framework import generics -from django.db.models import F -from django.shortcuts import get_object_or_404 - -from common.permissions import IsOrgAdmin -from .. import models, serializers - -__all__ = [ - 'RemoteAppPermissionUserRelationViewSet', - 'RemoteAppPermissionRemoteAppRelationViewSet', - 'RemoteAppPermissionAllRemoteAppListApi', - 'RemoteAppPermissionAllUserListApi', -] - - -class RemoteAppPermissionAllUserListApi(generics.ListAPIView): - permission_classes = (IsOrgAdmin,) - serializer_class = serializers.PermissionAllUserSerializer - filter_fields = ("username", "name") - search_fields = filter_fields - - def get_queryset(self): - pk = self.kwargs.get("pk") - perm = get_object_or_404(models.RemoteAppPermission, pk=pk) - users = perm.all_users.only( - *self.serializer_class.Meta.only_fields - ) - return users - - -class RemoteAppPermissionUserRelationViewSet(RelationViewSet): - serializer_class = serializers.RemoteAppPermissionUserRelationSerializer - m2m_field = models.RemoteAppPermission.users.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'user', 'remoteapppermission' - ] - search_fields = ('user__name', 'user__username', 'remoteapppermission__name') - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset.annotate(user_display=F('user__name')) - return queryset - - -class RemoteAppPermissionRemoteAppRelationViewSet(RelationViewSet): - serializer_class = serializers.RemoteAppPermissionRemoteAppRelationSerializer - m2m_field = models.RemoteAppPermission.remote_apps.field - permission_classes = (IsOrgAdmin,) - filter_fields = [ - 'id', 'remoteapp', 'remoteapppermission', - ] - search_fields = [ - "id", "remoteapp__name", "remoteapppermission__name" - ] - - def get_queryset(self): - queryset = super().get_queryset() - queryset = queryset \ - .annotate(remoteapp_display=F('remoteapp__name')) - return queryset - - -class RemoteAppPermissionAllRemoteAppListApi(generics.ListAPIView): - permission_classes = (IsOrgAdmin,) - serializer_class = serializers.RemoteAppPermissionAllRemoteAppSerializer - filter_fields = ("name",) - search_fields = filter_fields - - def get_queryset(self): - pk = self.kwargs.get("pk") - perm = get_object_or_404(models.RemoteAppPermission, pk=pk) - remote_apps = perm.all_remote_apps.only( - *self.serializer_class.Meta.only_fields - ) - return remote_apps diff --git a/apps/perms/api/user_database_app_permission.py b/apps/perms/api/user_database_app_permission.py deleted file mode 100644 index f102c3623..000000000 --- a/apps/perms/api/user_database_app_permission.py +++ /dev/null @@ -1,128 +0,0 @@ -# coding: utf-8 -# - -import uuid -from django.shortcuts import get_object_or_404 -from rest_framework.views import APIView, Response -from common.permissions import IsOrgAdminOrAppUser, IsValidUser -from common.tree import TreeNodeSerializer -from orgs.mixins import generics -from users.models import User, UserGroup -from applications.serializers import DatabaseAppSerializer -from applications.models import DatabaseApp -from assets.models import SystemUser -from .. import utils, serializers -from .mixin import UserPermissionMixin - -__all__ = [ - 'UserGrantedDatabaseAppsApi', - 'UserGrantedDatabaseAppsAsTreeApi', - 'UserGroupGrantedDatabaseAppsApi', - 'ValidateUserDatabaseAppPermissionApi', - 'UserGrantedDatabaseAppSystemUsersApi', -] - - -class UserGrantedDatabaseAppsApi(generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = DatabaseAppSerializer - filter_fields = ['id', 'name', 'type', 'comment'] - search_fields = ['name', 'comment'] - - def get_object(self): - user_id = self.kwargs.get('pk', '') - if user_id: - user = get_object_or_404(User, id=user_id) - else: - user = self.request.user - return user - - def get_queryset(self): - util = utils.DatabaseAppPermissionUtil(self.get_object()) - queryset = util.get_database_apps() - return queryset - - def get_permissions(self): - if self.kwargs.get('pk') is None: - self.permission_classes = (IsValidUser,) - return super().get_permissions() - - -class UserGrantedDatabaseAppsAsTreeApi(UserGrantedDatabaseAppsApi): - serializer_class = TreeNodeSerializer - permission_classes = (IsOrgAdminOrAppUser,) - - def get_serializer(self, database_apps, *args, **kwargs): - if database_apps is None: - database_apps = [] - only_database_app = self.request.query_params.get('only', '0') == '1' - tree_root = None - data = [] - if not only_database_app: - amount = len(database_apps) - tree_root = utils.construct_database_apps_tree_root(amount) - data.append(tree_root) - for database_app in database_apps: - node = utils.parse_database_app_to_tree_node(tree_root, database_app) - data.append(node) - data.sort() - return super().get_serializer(data, many=True) - - -class UserGrantedDatabaseAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = serializers.DatabaseAppSystemUserSerializer - only_fields = serializers.DatabaseAppSystemUserSerializer.Meta.only_fields - - def get_queryset(self): - util = utils.DatabaseAppPermissionUtil(self.obj) - database_app_id = self.kwargs.get('database_app_id') - database_app = get_object_or_404(DatabaseApp, id=database_app_id) - system_users = util.get_database_app_system_users(database_app) - return system_users - - -# Validate - -class ValidateUserDatabaseAppPermissionApi(APIView): - permission_classes = (IsOrgAdminOrAppUser,) - - def get(self, request, *args, **kwargs): - user_id = request.query_params.get('user_id', '') - database_app_id = request.query_params.get('database_app_id', '') - system_user_id = request.query_params.get('system_user_id', '') - - try: - user_id = uuid.UUID(user_id) - database_app_id = uuid.UUID(database_app_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) - database_app = get_object_or_404(DatabaseApp, id=database_app_id) - system_user = get_object_or_404(SystemUser, id=system_user_id) - - util = utils.DatabaseAppPermissionUtil(user) - system_users = util.get_database_app_system_users(database_app) - if system_user in system_users: - return Response({'msg': True}, status=200) - - return Response({'msg': False}, status=403) - - -# UserGroup - -class UserGroupGrantedDatabaseAppsApi(generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = DatabaseAppSerializer - - def get_queryset(self): - queryset = [] - user_group_id = self.kwargs.get('pk') - if not user_group_id: - return queryset - user_group = get_object_or_404(UserGroup, id=user_group_id) - util = utils.DatabaseAppPermissionUtil(user_group) - queryset = util.get_database_apps() - return queryset diff --git a/apps/perms/api/user_k8s_app_permission.py b/apps/perms/api/user_k8s_app_permission.py deleted file mode 100644 index 0aa5ac6f5..000000000 --- a/apps/perms/api/user_k8s_app_permission.py +++ /dev/null @@ -1,120 +0,0 @@ -# coding: utf-8 -# - -import uuid -from django.shortcuts import get_object_or_404 -from rest_framework.views import APIView, Response -from common.permissions import IsOrgAdminOrAppUser, IsValidUser -from common.tree import TreeNodeSerializer -from orgs.mixins import generics -from users.models import User, UserGroup -from applications.serializers import K8sAppSerializer -from applications.models import K8sApp -from assets.models import SystemUser -from .. import utils, serializers -from .mixin import UserPermissionMixin - - -class UserGrantedK8sAppsApi(generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = K8sAppSerializer - filter_fields = ['id', 'name', 'type', 'comment'] - search_fields = ['name', 'comment'] - - def get_object(self): - user_id = self.kwargs.get('pk', '') - if user_id: - user = get_object_or_404(User, id=user_id) - else: - user = self.request.user - return user - - def get_queryset(self): - util = utils.K8sAppPermissionUtil(self.get_object()) - queryset = util.get_k8s_apps() - return queryset - - def get_permissions(self): - if self.kwargs.get('pk') is None: - self.permission_classes = (IsValidUser,) - return super().get_permissions() - - -class UserGrantedK8sAppsAsTreeApi(UserGrantedK8sAppsApi): - serializer_class = TreeNodeSerializer - permission_classes = (IsOrgAdminOrAppUser,) - - def get_serializer(self, k8s_apps, *args, **kwargs): - if k8s_apps is None: - k8s_apps = [] - only_k8s_app = self.request.query_params.get('only', '0') == '1' - tree_root = None - data = [] - if not only_k8s_app: - amount = len(k8s_apps) - tree_root = utils.construct_k8s_apps_tree_root(amount) - data.append(tree_root) - for k8s_app in k8s_apps: - node = utils.parse_k8s_app_to_tree_node(tree_root, k8s_app) - data.append(node) - data.sort() - return super().get_serializer(data, many=True) - - -class UserGrantedK8sAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = serializers.K8sAppSystemUserSerializer - only_fields = serializers.K8sAppSystemUserSerializer.Meta.only_fields - - def get_queryset(self): - util = utils.K8sAppPermissionUtil(self.obj) - k8s_app_id = self.kwargs.get('k8s_app_id') - k8s_app = get_object_or_404(K8sApp, id=k8s_app_id) - system_users = util.get_k8s_app_system_users(k8s_app) - return system_users - - -# Validate - -class ValidateUserK8sAppPermissionApi(APIView): - permission_classes = (IsOrgAdminOrAppUser,) - - def get(self, request, *args, **kwargs): - user_id = request.query_params.get('user_id', '') - k8s_app_id = request.query_params.get('k8s_app_id', '') - system_user_id = request.query_params.get('system_user_id', '') - - try: - user_id = uuid.UUID(user_id) - k8s_app_id = uuid.UUID(k8s_app_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) - k8s_app = get_object_or_404(K8sApp, id=k8s_app_id) - system_user = get_object_or_404(SystemUser, id=system_user_id) - - util = utils.K8sAppPermissionUtil(user) - system_users = util.get_k8s_app_system_users(k8s_app) - if system_user in system_users: - return Response({'msg': True}, status=200) - - return Response({'msg': False}, status=403) - - -# UserGroup - -class UserGroupGrantedK8sAppsApi(generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = K8sAppSerializer - - def get_queryset(self): - queryset = [] - user_group_id = self.kwargs.get('pk') - if not user_group_id: - return queryset - user_group = get_object_or_404(UserGroup, id=user_group_id) - util = utils.K8sAppPermissionUtil(user_group) - queryset = util.get_k8s_apps() - return queryset diff --git a/apps/perms/api/user_remote_app_permission.py b/apps/perms/api/user_remote_app_permission.py deleted file mode 100644 index 8f418683e..000000000 --- a/apps/perms/api/user_remote_app_permission.py +++ /dev/null @@ -1,126 +0,0 @@ -# -*- coding: utf-8 -*- - -import uuid -from django.shortcuts import get_object_or_404 -from rest_framework.views import APIView, Response - -from common.permissions import IsValidUser, IsOrgAdminOrAppUser -from common.tree import TreeNodeSerializer -from orgs.mixins import generics -from ..utils import ( - RemoteAppPermissionUtil, construct_remote_apps_tree_root, - parse_remote_app_to_tree_node, -) -from ..hands import User, RemoteApp, RemoteAppSerializer, UserGroup, SystemUser -from .mixin import UserPermissionMixin -from .. import serializers - - -__all__ = [ - 'UserGrantedRemoteAppsApi', 'ValidateUserRemoteAppPermissionApi', - 'UserGrantedRemoteAppsAsTreeApi', 'UserGroupGrantedRemoteAppsApi', - 'UserGrantedRemoteAppSystemUsersApi', -] - - -class UserGrantedRemoteAppsApi(generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = RemoteAppSerializer - filter_fields = ['name', 'id', 'type', 'comment'] - search_fields = ['name', 'comment'] - - def get_object(self): - user_id = self.kwargs.get('pk', '') - if user_id: - user = get_object_or_404(User, id=user_id) - else: - user = self.request.user - return user - - def get_queryset(self): - util = RemoteAppPermissionUtil(self.get_object()) - queryset = util.get_remote_apps() - return queryset - - def get_permissions(self): - if self.kwargs.get('pk') is None: - self.permission_classes = (IsValidUser,) - return super().get_permissions() - - -class UserGrantedRemoteAppsAsTreeApi(UserGrantedRemoteAppsApi): - serializer_class = TreeNodeSerializer - permission_classes = (IsOrgAdminOrAppUser,) - - def get_serializer(self, remote_apps=None, *args, **kwargs): - if remote_apps is None: - remote_apps = [] - only_remote_app = self.request.query_params.get('only', '0') == '1' - tree_root = None - data = [] - if not only_remote_app: - amount = len(remote_apps) - tree_root = construct_remote_apps_tree_root(amount) - data.append(tree_root) - for remote_app in remote_apps: - node = parse_remote_app_to_tree_node(tree_root, remote_app) - data.append(node) - data.sort() - return super().get_serializer(data, many=True) - - -class UserGrantedRemoteAppSystemUsersApi(UserPermissionMixin, generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser,) - serializer_class = serializers.RemoteAppSystemUserSerializer - only_fields = serializers.RemoteAppSystemUserSerializer.Meta.only_fields - - def get_queryset(self): - util = RemoteAppPermissionUtil(self.obj) - remote_app_id = self.kwargs.get('remote_app_id') - remote_app = get_object_or_404(RemoteApp, id=remote_app_id) - system_users = util.get_remote_app_system_users(remote_app) - return system_users - - -class ValidateUserRemoteAppPermissionApi(APIView): - permission_classes = (IsOrgAdminOrAppUser,) - - def get(self, request, *args, **kwargs): - user_id = request.query_params.get('user_id', '') - remote_app_id = request.query_params.get('remote_app_id', '') - system_id = request.query_params.get('system_user_id', '') - - try: - user_id = uuid.UUID(user_id) - remote_app_id = uuid.UUID(remote_app_id) - system_id = uuid.UUID(system_id) - except ValueError: - return Response({'msg': False}, status=403) - - user = get_object_or_404(User, id=user_id) - remote_app = get_object_or_404(RemoteApp, id=remote_app_id) - system_user = get_object_or_404(SystemUser, id=system_id) - - util = RemoteAppPermissionUtil(user) - system_users = util.get_remote_app_system_users(remote_app) - if system_user in system_users: - return Response({'msg': True}, status=200) - - return Response({'msg': False}, status=403) - - -# RemoteApp permission - -class UserGroupGrantedRemoteAppsApi(generics.ListAPIView): - permission_classes = (IsOrgAdminOrAppUser, ) - serializer_class = RemoteAppSerializer - - def get_queryset(self): - queryset = [] - user_group_id = self.kwargs.get('pk') - if not user_group_id: - return queryset - user_group = get_object_or_404(UserGroup, id=user_group_id) - util = RemoteAppPermissionUtil(user_group) - queryset = util.get_remote_apps() - return queryset diff --git a/apps/perms/forms/__init__.py b/apps/perms/forms/__init__.py deleted file mode 100644 index c6581b858..000000000 --- a/apps/perms/forms/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# coding: utf-8 -# - -from .asset_permission import * -from .remote_app_permission import * -from .database_app_permission import * diff --git a/apps/perms/forms/asset_permission.py b/apps/perms/forms/asset_permission.py deleted file mode 100644 index afa1bd2db..000000000 --- a/apps/perms/forms/asset_permission.py +++ /dev/null @@ -1,119 +0,0 @@ -# ~*~ coding: utf-8 ~*~ - -from __future__ import absolute_import, unicode_literals -from django import forms -from django.utils.translation import ugettext_lazy as _ - -from orgs.mixins.forms import OrgModelForm -from assets.models import Asset, Node, SystemUser -from ..models import AssetPermission, Action - -__all__ = [ - 'AssetPermissionForm', -] - - -class ActionField(forms.MultipleChoiceField): - def __init__(self, *args, **kwargs): - kwargs['choices'] = Action.CHOICES - kwargs['initial'] = Action.ALL - kwargs['label'] = _("Action") - kwargs['widget'] = forms.CheckboxSelectMultiple() - kwargs['help_text'] = _( - 'Tips: The RDP protocol does not support separate controls ' - 'for uploading or downloading files' - ) - super().__init__(*args, **kwargs) - - def to_python(self, value): - value = super().to_python(value) - return Action.choices_to_value(value) - - def prepare_value(self, value): - if value is None: - return value - value = Action.value_to_choices(value) - return value - - -class AssetPermissionForm(OrgModelForm): - actions = ActionField() - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - if self.data: - return - # 前端渲染优化, 防止过多资产 - users_field = self.fields.get('users') - assets_field = self.fields['assets'] - nodes_field = self.fields['nodes'] - if self.instance: - assets_field.queryset = self.instance.assets.all() - nodes_field.queryset = self.instance.nodes.all() - users_field.queryset = self.instance.users.all() - else: - assets_field.queryset = Asset.objects.none() - nodes_field.queryset = Node.objects.none() - users_field.queryset = [] - - # 过滤系统用户 - system_users_field = self.fields.get('system_users') - system_users_field.queryset = SystemUser.objects.exclude( - protocol__in=SystemUser.ASSET_CATEGORY_PROTOCOLS - ) - - def set_nodes_initial(self, nodes): - field = self.fields['nodes'] - field.choices = [(n.id, n.full_value) for n in nodes] - field.initial = nodes - - def set_assets_initial(self, assets): - field = self.fields['assets'] - field.choices = [(a.id, a.hostname) for a in assets] - field.initial = assets - - class Meta: - model = AssetPermission - exclude = ( - 'id', 'date_created', 'created_by', 'org_id' - ) - widgets = { - 'users': forms.SelectMultiple( - attrs={'class': 'users-select2', 'data-placeholder': _("User")} - ), - 'user_groups': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _("User group")} - ), - 'assets': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _("Asset")} - ), - 'nodes': forms.SelectMultiple( - attrs={'class': 'nodes-select2', 'data-placeholder': _("Node")} - ), - 'system_users': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('System user')} - ), - } - labels = { - 'nodes': _("Node"), - } - - def clean_user_groups(self): - users = self.cleaned_data.get('users') - user_groups = self.cleaned_data.get('user_groups') - - if not users and not user_groups: - raise forms.ValidationError( - _("User or group at least one required")) - return self.cleaned_data["user_groups"] - - def clean_asset_groups(self): - assets = self.cleaned_data.get('assets') - asset_groups = self.cleaned_data.get('asset_groups') - - if not assets and not asset_groups: - raise forms.ValidationError( - _("Asset or group at least one required")) - - return self.cleaned_data["asset_groups"] diff --git a/apps/perms/forms/database_app_permission.py b/apps/perms/forms/database_app_permission.py deleted file mode 100644 index 0dffadf93..000000000 --- a/apps/perms/forms/database_app_permission.py +++ /dev/null @@ -1,49 +0,0 @@ -# coding: utf-8 -# - -from django.utils.translation import ugettext as _ -from django import forms -from orgs.mixins.forms import OrgModelForm -from assets.models import SystemUser - -from ..models import DatabaseAppPermission - - -__all__ = ['DatabaseAppPermissionCreateUpdateForm'] - - -class DatabaseAppPermissionCreateUpdateForm(OrgModelForm): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - users_field = self.fields.get('users') - if self.instance: - users_field.queryset = self.instance.users.all() - else: - users_field.queryset = [] - - # 过滤系统用户 - system_users_field = self.fields.get('system_users') - system_users_field.queryset = SystemUser.objects.filter( - protocol__in=SystemUser.APPLICATION_CATEGORY_DB_PROTOCOLS - ) - - class Meta: - model = DatabaseAppPermission - exclude = ( - 'id', 'date_created', 'created_by', 'org_id' - ) - widgets = { - 'users': forms.SelectMultiple( - attrs={'class': 'users-select2', 'data-placeholder': _('User')} - ), - 'user_groups': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('User group')} - ), - 'database_apps': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('DatabaseApp')} - ), - 'system_users': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('System users')} - ), - } diff --git a/apps/perms/forms/remote_app_permission.py b/apps/perms/forms/remote_app_permission.py deleted file mode 100644 index c4179297f..000000000 --- a/apps/perms/forms/remote_app_permission.py +++ /dev/null @@ -1,51 +0,0 @@ -# coding: utf-8 -# - -from django.utils.translation import ugettext as _ -from django import forms -from orgs.mixins.forms import OrgModelForm -from assets.models import SystemUser - -from ..models import RemoteAppPermission - - -__all__ = [ - 'RemoteAppPermissionCreateUpdateForm', -] - - -class RemoteAppPermissionCreateUpdateForm(OrgModelForm): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - users_field = self.fields.get('users') - if self.instance: - users_field.queryset = self.instance.users.all() - else: - users_field.queryset = [] - - # 过滤系统用户 - system_users_field = self.fields.get('system_users') - system_users_field.queryset = SystemUser.objects.filter( - protocol=SystemUser.PROTOCOL_RDP - ) - - class Meta: - model = RemoteAppPermission - exclude = ( - 'id', 'date_created', 'created_by', 'org_id' - ) - widgets = { - 'users': forms.SelectMultiple( - attrs={'class': 'users-select2', 'data-placeholder': _('User')} - ), - 'user_groups': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('User group')} - ), - 'remote_apps': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('RemoteApp')} - ), - 'system_users': forms.SelectMultiple( - attrs={'class': 'select2', 'data-placeholder': _('System user')} - ) - } diff --git a/apps/perms/hands.py b/apps/perms/hands.py index ab9e3f494..25902fddf 100644 --- a/apps/perms/hands.py +++ b/apps/perms/hands.py @@ -4,13 +4,10 @@ from users.models import User, UserGroup from assets.models import Asset, SystemUser, Node, Label, FavoriteAsset from assets.serializers import NodeSerializer -from applications.serializers import RemoteAppSerializer -from applications.models import RemoteApp __all__ = [ 'User', 'UserGroup', 'Asset', 'SystemUser', 'Node', 'Label', 'FavoriteAsset', - 'NodeSerializer', 'RemoteAppSerializer', - 'RemoteApp' + 'NodeSerializer', ] diff --git a/apps/perms/migrations/0017_auto_20210104_0435.py b/apps/perms/migrations/0017_auto_20210104_0435.py new file mode 100644 index 000000000..d27efe6ee --- /dev/null +++ b/apps/perms/migrations/0017_auto_20210104_0435.py @@ -0,0 +1,62 @@ +# Generated by Django 3.1 on 2021-01-03 20:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('perms', '0016_applicationpermission'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='k8sapppermission', + unique_together=None, + ), + migrations.RemoveField( + model_name='k8sapppermission', + name='k8s_apps', + ), + migrations.RemoveField( + model_name='k8sapppermission', + name='system_users', + ), + migrations.RemoveField( + model_name='k8sapppermission', + name='user_groups', + ), + migrations.RemoveField( + model_name='k8sapppermission', + name='users', + ), + migrations.AlterUniqueTogether( + name='remoteapppermission', + unique_together=None, + ), + migrations.RemoveField( + model_name='remoteapppermission', + name='remote_apps', + ), + migrations.RemoveField( + model_name='remoteapppermission', + name='system_users', + ), + migrations.RemoveField( + model_name='remoteapppermission', + name='user_groups', + ), + migrations.RemoveField( + model_name='remoteapppermission', + name='users', + ), + migrations.DeleteModel( + name='DatabaseAppPermission', + ), + migrations.DeleteModel( + name='K8sAppPermission', + ), + migrations.DeleteModel( + name='RemoteAppPermission', + ), + ] diff --git a/apps/perms/mixins.py b/apps/perms/mixins.py index 98ac8bf76..e69de29bb 100644 --- a/apps/perms/mixins.py +++ b/apps/perms/mixins.py @@ -1,22 +0,0 @@ -# ~*~ coding: utf-8 ~*~ -# - -from orgs.utils import set_to_root_org - -__all__ = [ - 'ChangeOrgIfNeedMixin', -] - - -class ChangeOrgIfNeedMixin(object): - - @staticmethod - def change_org_if_need(request, kwargs): - if request.user.is_authenticated and request.user.is_superuser \ - or request.user.is_app \ - or kwargs.get('pk') is None: - set_to_root_org() - - def get(self, request, *args, **kwargs): - self.change_org_if_need(request, kwargs) - return super().get(request, *args, **kwargs) diff --git a/apps/perms/models/__init__.py b/apps/perms/models/__init__.py index e7b0b0ffb..31a1344eb 100644 --- a/apps/perms/models/__init__.py +++ b/apps/perms/models/__init__.py @@ -3,6 +3,3 @@ 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 index cd9cdb001..44cb38323 100644 --- a/apps/perms/models/application_permission.py +++ b/apps/perms/models/application_permission.py @@ -8,7 +8,7 @@ from django.utils.translation import ugettext_lazy as _ from common.utils import lazyproperty from .base import BasePermission from users.models import User -from applications.models import Category +from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices __all__ = [ 'ApplicationPermission', @@ -16,16 +16,38 @@ __all__ = [ class ApplicationPermission(BasePermission): - category = models.CharField(max_length=16, choices=Category.choices, verbose_name=_('Category')) - type = models.CharField(max_length=16, choices=Category.get_all_type_choices(), verbose_name=_('Type')) - 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")) + category = models.CharField( + max_length=16, choices=ApplicationCategoryChoices.choices, verbose_name=_('Category') + ) + type = models.CharField( + max_length=16, choices=ApplicationTypeChoices.choices, verbose_name=_('Type') + ) + 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',) + @property + def category_remote_app(self): + return self.category == ApplicationCategoryChoices.remote_app.value + + @property + def category_db(self): + return self.category == ApplicationCategoryChoices.db.value + + @property + def category_cloud(self): + return self.category == ApplicationCategoryChoices.cloud.value + @lazyproperty def users_amount(self): return self.users.count() diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py index dd26a6ae5..00c0d5a97 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -1,4 +1,3 @@ -import uuid import logging from functools import reduce @@ -6,8 +5,6 @@ from django.utils.translation import ugettext_lazy as _ from common.db import models from common.utils import lazyproperty -from orgs.models import Organization -from orgs.utils import get_current_org from assets.models import Asset, SystemUser, Node, FamilyMixin from .base import BasePermission diff --git a/apps/perms/models/database_app_permission.py b/apps/perms/models/database_app_permission.py deleted file mode 100644 index 91b989128..000000000 --- a/apps/perms/models/database_app_permission.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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__ = [ - 'DatabaseAppPermission', -] - - -class DatabaseAppPermission(BasePermission): - database_apps = models.ManyToManyField( - 'applications.DatabaseApp', related_name='granted_by_permissions', - blank=True, verbose_name=_("DatabaseApp") - ) - system_users = models.ManyToManyField( - 'assets.SystemUser', related_name='granted_by_database_app_permissions', - verbose_name=_("System user") - ) - - class Meta: - unique_together = [('org_id', 'name')] - verbose_name = _('DatabaseApp permission') - ordering = ('name',) - - def get_all_database_apps(self): - return self.database_apps.all() - - @lazyproperty - def database_apps_amount(self): - return self.database_apps.count() - - @lazyproperty - def system_users_amount(self): - return self.system_users.count() diff --git a/apps/perms/models/k8s_app_permission.py b/apps/perms/models/k8s_app_permission.py deleted file mode 100644 index b6c7106bc..000000000 --- a/apps/perms/models/k8s_app_permission.py +++ /dev/null @@ -1,39 +0,0 @@ -# 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__ = [ - 'K8sAppPermission', -] - - -class K8sAppPermission(BasePermission): - k8s_apps = models.ManyToManyField( - 'applications.K8sApp', related_name='granted_by_permissions', - blank=True, verbose_name=_("KubernetesApp") - ) - system_users = models.ManyToManyField( - 'assets.SystemUser', related_name='granted_by_k8s_app_permissions', - verbose_name=_("System user") - ) - - class Meta: - unique_together = [('org_id', 'name')] - verbose_name = _('KubernetesApp permission') - ordering = ('name',) - - def get_all_k8s_apps(self): - return self.k8s_apps.all() - - @lazyproperty - def k8s_apps_amount(self): - return self.k8s_apps.count() - - @lazyproperty - def system_users_amount(self): - return self.system_users.count() diff --git a/apps/perms/models/remote_app_permission.py b/apps/perms/models/remote_app_permission.py deleted file mode 100644 index 40114875c..000000000 --- a/apps/perms/models/remote_app_permission.py +++ /dev/null @@ -1,36 +0,0 @@ -# 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__ = [ - 'RemoteAppPermission', -] - - -class RemoteAppPermission(BasePermission): - remote_apps = models.ManyToManyField('applications.RemoteApp', related_name='granted_by_permissions', blank=True, verbose_name=_("RemoteApp")) - system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_remote_app_permissions', verbose_name=_("System user")) - - class Meta: - unique_together = [('org_id', 'name')] - verbose_name = _('RemoteApp permission') - ordering = ('name',) - - def get_all_remote_apps(self): - return set(self.remote_apps.all()) - - @property - def all_remote_apps(self): - return self.remote_apps.all() - - @lazyproperty - def remote_apps_amount(self): - return self.remote_apps.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 2ecfc8172..9fb257580 100644 --- a/apps/perms/serializers/__init__.py +++ b/apps/perms/serializers/__init__.py @@ -3,12 +3,3 @@ from .asset import * from .application import * from .system_user_permission import * - -# TODO: 删除 -from .remote_app_permission import * -from .remote_app_permission_relation import * -from .database_app_permission import * -from .database_app_permission_relation import * -from .base import * -from .k8s_app_permission import * -from .k8s_app_permission_relation import * diff --git a/apps/perms/serializers/application/user_permission.py b/apps/perms/serializers/application/user_permission.py index d2a0f0c75..0010e899c 100644 --- a/apps/perms/serializers/application/user_permission.py +++ b/apps/perms/serializers/application/user_permission.py @@ -6,10 +6,11 @@ from django.utils.translation import ugettext_lazy as _ from assets.models import SystemUser from applications.models import Application +from applications.serializers.attrs import get_attrs_field_dynamic_mapping_rules +from common.drf.fields import DynamicMappingField __all__ = [ - 'ApplicationGrantedSerializer', - 'ApplicationSystemUserSerializer' + 'ApplicationGrantedSerializer', 'ApplicationSystemUserSerializer' ] @@ -32,6 +33,7 @@ class ApplicationGrantedSerializer(serializers.ModelSerializer): """ category_display = serializers.ReadOnlyField(source='get_category_display', label=_('Category')) type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type')) + attrs = DynamicMappingField(mapping_rules=get_attrs_field_dynamic_mapping_rules()) class Meta: model = Application diff --git a/apps/perms/serializers/base.py b/apps/perms/serializers/base.py deleted file mode 100644 index 33de4980b..000000000 --- a/apps/perms/serializers/base.py +++ /dev/null @@ -1,13 +0,0 @@ -from rest_framework import serializers - - -class PermissionAllUserSerializer(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/database_app_permission.py b/apps/perms/serializers/database_app_permission.py deleted file mode 100644 index 0e35aff8d..000000000 --- a/apps/perms/serializers/database_app_permission.py +++ /dev/null @@ -1,67 +0,0 @@ -# coding: utf-8 -# -from django.db.models import Count -from rest_framework import serializers -from django.utils.translation import ugettext_lazy as _ - -from common.drf.serializers import AdaptedBulkListSerializer -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from .. import models - -__all__ = [ - 'DatabaseAppPermissionSerializer', 'DatabaseAppPermissionListSerializer' -] - - -class AmountMixin: - @classmethod - def setup_eager_loading(cls, queryset): - """ Perform necessary eager loading of data. """ - queryset = queryset.annotate( - users_amount=Count('users', distinct=True), user_groups_amount=Count('user_groups', distinct=True), - database_apps_amount=Count('database_apps', distinct=True), - system_users_amount=Count('system_users', distinct=True) - ) - return queryset - - -class DatabaseAppPermissionSerializer(AmountMixin, BulkOrgResourceModelSerializer): - class Meta: - model = models.DatabaseAppPermission - list_serializer_class = AdaptedBulkListSerializer - fields = [ - 'id', 'name', 'users', 'user_groups', 'database_apps', 'system_users', - 'comment', 'is_active', 'date_start', 'date_expired', 'is_valid', - 'created_by', 'date_created', 'users_amount', 'user_groups_amount', - 'database_apps_amount', 'system_users_amount', - ] - read_only_fields = [ - 'created_by', 'date_created', 'users_amount', 'user_groups_amount', - 'database_apps_amount', 'system_users_amount', - ] - extra_kwargs = { - 'is_valid': {'label': _('Is valid')}, - 'users_amount': {'label': _('Users amount')}, - 'user_groups_amount': {'label': _('User groups amount')}, - 'system_users_amount': {'label': _('System users amount')}, - 'database_apps_amount': {'label': _('Database apps amount')}, - } - - -class DatabaseAppPermissionListSerializer(AmountMixin, BulkOrgResourceModelSerializer): - is_expired = serializers.BooleanField() - - class Meta: - model = models.DatabaseAppPermission - fields = [ - 'id', 'name', 'comment', 'is_active', 'users_amount', 'user_groups_amount', - 'date_start', 'date_expired', 'is_valid', 'database_apps_amount', 'system_users_amount', - 'created_by', 'date_created', 'is_expired' - ] - extra_kwargs = { - 'is_valid': {'label': _('Is valid')}, - 'users_amount': {'label': _('Users amount')}, - 'user_groups_amount': {'label': _('User groups amount')}, - 'system_users_amount': {'label': _('System users amount')}, - 'database_apps_amount': {'label': _('Database apps amount')}, - } diff --git a/apps/perms/serializers/database_app_permission_relation.py b/apps/perms/serializers/database_app_permission_relation.py deleted file mode 100644 index 5da6c88f0..000000000 --- a/apps/perms/serializers/database_app_permission_relation.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: utf-8 -# -from perms.serializers.base import PermissionAllUserSerializer -from rest_framework import serializers - -from common.mixins import BulkSerializerMixin -from common.drf.serializers import AdaptedBulkListSerializer - -from .. import models - -__all__ = [ - 'DatabaseAppPermissionUserRelationSerializer', - 'DatabaseAppPermissionUserGroupRelationSerializer', - 'DatabaseAppPermissionAllUserSerializer', - 'DatabaseAppPermissionDatabaseAppRelationSerializer', - 'DatabaseAppPermissionAllDatabaseAppSerializer', - 'DatabaseAppPermissionSystemUserRelationSerializer', -] - - -class RelationMixin(BulkSerializerMixin, serializers.Serializer): - databaseapppermission_display = serializers.ReadOnlyField() - - def get_field_names(self, declared_fields, info): - fields = super().get_field_names(declared_fields, info) - fields.extend(['databaseapppermission', "databaseapppermission_display"]) - return fields - - class Meta: - list_serializer_class = AdaptedBulkListSerializer - - -class DatabaseAppPermissionUserRelationSerializer(RelationMixin, serializers.ModelSerializer): - user_display = serializers.ReadOnlyField() - - class Meta(RelationMixin.Meta): - model = models.DatabaseAppPermission.users.through - fields = [ - 'id', 'user', 'user_display', - ] - - -class DatabaseAppPermissionUserGroupRelationSerializer(RelationMixin, serializers.ModelSerializer): - usergroup_display = serializers.ReadOnlyField() - - class Meta(RelationMixin.Meta): - model = models.DatabaseAppPermission.user_groups.through - fields = [ - 'id', 'usergroup', "usergroup_display", - ] - - -class DatabaseAppPermissionAllUserSerializer(PermissionAllUserSerializer): - class Meta(PermissionAllUserSerializer.Meta): - pass - - -class DatabaseAppPermissionDatabaseAppRelationSerializer(RelationMixin, serializers.ModelSerializer): - databaseapp_display = serializers.ReadOnlyField() - - class Meta(RelationMixin.Meta): - model = models.DatabaseAppPermission.database_apps.through - fields = [ - 'id', "databaseapp", "databaseapp_display", - ] - - -class DatabaseAppPermissionAllDatabaseAppSerializer(serializers.Serializer): - databaseapp = serializers.UUIDField(read_only=True, source='id') - databaseapp_display = serializers.SerializerMethodField() - - class Meta: - only_fields = ['id', 'name'] - - @staticmethod - def get_databaseapp_display(obj): - return str(obj) - - -class DatabaseAppPermissionSystemUserRelationSerializer(RelationMixin, serializers.ModelSerializer): - systemuser_display = serializers.ReadOnlyField() - - class Meta(RelationMixin.Meta): - model = models.DatabaseAppPermission.system_users.through - fields = [ - 'id', 'systemuser', 'systemuser_display' - ] diff --git a/apps/perms/serializers/k8s_app_permission.py b/apps/perms/serializers/k8s_app_permission.py deleted file mode 100644 index 96f23ace6..000000000 --- a/apps/perms/serializers/k8s_app_permission.py +++ /dev/null @@ -1,65 +0,0 @@ -# coding: utf-8 -# -from django.db.models import Count -from rest_framework import serializers -from django.utils.translation import ugettext_lazy as _ - -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from .. import models - -__all__ = [ - 'K8sAppPermissionSerializer', 'K8sAppPermissionListSerializer' -] - - -class AmountMixin: - @classmethod - def setup_eager_loading(cls, queryset): - """ Perform necessary eager loading of data. """ - queryset = queryset.annotate( - users_amount=Count('users', distinct=True), user_groups_amount=Count('user_groups', distinct=True), - k8s_apps_amount=Count('k8s_apps', distinct=True), - system_users_amount=Count('system_users', distinct=True) - ) - return queryset - - -class K8sAppPermissionSerializer(AmountMixin, BulkOrgResourceModelSerializer): - class Meta: - model = models.K8sAppPermission - fields = [ - 'id', 'name', 'users', 'user_groups', 'k8s_apps', 'system_users', - 'comment', 'is_active', 'date_start', 'date_expired', 'is_valid', - 'created_by', 'date_created', 'users_amount', 'user_groups_amount', - 'k8s_apps_amount', 'system_users_amount', - ] - read_only_fields = [ - 'created_by', 'date_created', 'users_amount', 'user_groups_amount', - 'k8s_apps_amount', 'system_users_amount', 'id' - ] - extra_kwargs = { - 'is_valid': {'label': _('Is valid')}, - 'users_amount': {'label': _('Users amount')}, - 'user_groups_amount': {'label': _('User groups amount')}, - 'system_users_amount': {'label': _('System users amount')}, - 'database_apps_amount': {'label': _('Database apps amount')}, - } - - -class K8sAppPermissionListSerializer(AmountMixin, BulkOrgResourceModelSerializer): - is_expired = serializers.BooleanField() - - class Meta: - model = models.K8sAppPermission - fields = [ - 'id', 'name', 'comment', 'is_active', 'users_amount', 'user_groups_amount', - 'date_start', 'date_expired', 'is_valid', 'k8s_apps_amount', 'system_users_amount', - 'created_by', 'date_created', 'is_expired' - ] - extra_kwargs = { - 'is_valid': {'label': _('Is valid')}, - 'users_amount': {'label': _('Users amount')}, - 'user_groups_amount': {'label': _('User groups amount')}, - 'system_users_amount': {'label': _('System users amount')}, - 'k8s_apps_amount': {'label': _('K8s apps amount')}, - } diff --git a/apps/perms/serializers/k8s_app_permission_relation.py b/apps/perms/serializers/k8s_app_permission_relation.py deleted file mode 100644 index e5786ca50..000000000 --- a/apps/perms/serializers/k8s_app_permission_relation.py +++ /dev/null @@ -1,73 +0,0 @@ -# coding: utf-8 -# -from perms.serializers.base import PermissionAllUserSerializer -from rest_framework import serializers - -from common.drf.serializers import BulkModelSerializer - -from .. import models - - -class K8sAppPermissionUserRelationSerializer(BulkModelSerializer): - user_display = serializers.ReadOnlyField() - k8sapppermission_display = serializers.ReadOnlyField() - - class Meta: - model = models.K8sAppPermission.users.through - fields = [ - 'id', 'user', 'user_display', 'k8sapppermission', - 'k8sapppermission_display' - ] - - -class K8sAppPermissionUserGroupRelationSerializer(BulkModelSerializer): - usergroup_display = serializers.ReadOnlyField() - k8sapppermission_display = serializers.ReadOnlyField() - - class Meta: - model = models.K8sAppPermission.user_groups.through - fields = [ - 'id', 'usergroup', 'usergroup_display', 'k8sapppermission', - 'k8sapppermission_display' - ] - - -class K8sAppPermissionAllUserSerializer(PermissionAllUserSerializer): - class Meta(PermissionAllUserSerializer.Meta): - pass - - -class K8sAppPermissionK8sAppRelationSerializer(BulkModelSerializer): - k8sapp_display = serializers.ReadOnlyField() - k8sapppermission_display = serializers.ReadOnlyField() - - class Meta: - model = models.K8sAppPermission.k8s_apps.through - fields = [ - 'id', "k8sapp", "k8sapp_display", 'k8sapppermission', - 'k8sapppermission_display' - ] - - -class K8sAppPermissionAllK8sAppSerializer(serializers.Serializer): - k8sapp = serializers.UUIDField(read_only=True, source='id') - k8sapp_display = serializers.SerializerMethodField() - - class Meta: - only_fields = ['id', 'name'] - - @staticmethod - def get_k8sapp_display(obj): - return str(obj) - - -class K8sAppPermissionSystemUserRelationSerializer(BulkModelSerializer): - systemuser_display = serializers.ReadOnlyField() - k8sapppermission_display = serializers.ReadOnlyField() - - class Meta: - model = models.K8sAppPermission.system_users.through - fields = [ - 'id', 'systemuser', 'systemuser_display', 'k8sapppermission', - 'k8sapppermission_display' - ] diff --git a/apps/perms/serializers/remote_app_permission.py b/apps/perms/serializers/remote_app_permission.py deleted file mode 100644 index a676f8028..000000000 --- a/apps/perms/serializers/remote_app_permission.py +++ /dev/null @@ -1,62 +0,0 @@ -# coding: utf-8 -# -from rest_framework import serializers -from django.db.models import Count -from django.utils.translation import ugettext_lazy as _ - -from common.drf.serializers import AdaptedBulkListSerializer -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from ..models import RemoteAppPermission - - -__all__ = [ - 'RemoteAppPermissionSerializer', - 'RemoteAppPermissionUpdateUserSerializer', - 'RemoteAppPermissionUpdateRemoteAppSerializer', -] - - -class RemoteAppPermissionSerializer(BulkOrgResourceModelSerializer): - class Meta: - model = RemoteAppPermission - list_serializer_class = AdaptedBulkListSerializer - mini_fields = ['id', 'name'] - small_fields = mini_fields + [ - 'comment', 'is_active', 'date_start', 'date_expired', 'is_valid', - 'created_by', 'date_created' - ] - m2m_fields = [ - 'users', 'user_groups', 'remote_apps', 'system_users', - 'users_amount', 'user_groups_amount', 'remote_apps_amount', - 'system_users_amount' - ] - fields = small_fields + m2m_fields - read_only_fields = ['created_by', 'date_created'] - extra_kwargs = { - 'is_valid': {'label': _('Is valid')}, - 'users_amount': {'label': _('Users amount')}, - 'user_groups_amount': {'label': _('User groups amount')}, - 'system_users_amount': {'label': _('System users amount')}, - 'remote_apps_amount': {'label': _('Remote apps amount')}, - } - - @classmethod - def setup_eager_loading(cls, queryset): - """ Perform necessary eager loading of data. """ - queryset = queryset.annotate( - users_amount=Count('users', distinct=True), user_groups_amount=Count('user_groups', distinct=True), - remote_apps_amount=Count('remote_apps', distinct=True), system_users_amount=Count('system_users', distinct=True) - ) - return queryset - - -class RemoteAppPermissionUpdateUserSerializer(serializers.ModelSerializer): - class Meta: - model = RemoteAppPermission - fields = ['id', 'users'] - - -class RemoteAppPermissionUpdateRemoteAppSerializer(serializers.ModelSerializer): - class Meta: - model = RemoteAppPermission - fields = ['id', 'remote_apps'] diff --git a/apps/perms/serializers/remote_app_permission_relation.py b/apps/perms/serializers/remote_app_permission_relation.py deleted file mode 100644 index 1e5a30a2e..000000000 --- a/apps/perms/serializers/remote_app_permission_relation.py +++ /dev/null @@ -1,49 +0,0 @@ -# coding: utf-8 -# -from rest_framework import serializers - -from common.drf.serializers import AdaptedBulkListSerializer -from ..models import RemoteAppPermission - - -__all__ = [ - 'RemoteAppPermissionRemoteAppRelationSerializer', - 'RemoteAppPermissionAllRemoteAppSerializer', - 'RemoteAppPermissionUserRelationSerializer', -] - - -class RemoteAppPermissionRemoteAppRelationSerializer(serializers.ModelSerializer): - remoteapp_display = serializers.ReadOnlyField() - remoteapppermission_display = serializers.ReadOnlyField() - - class Meta: - model = RemoteAppPermission.remote_apps.through - list_serializer_class = AdaptedBulkListSerializer - fields = [ - 'id', 'remoteapp', 'remoteapp_display', 'remoteapppermission', 'remoteapppermission_display' - ] - - -class RemoteAppPermissionAllRemoteAppSerializer(serializers.Serializer): - remoteapp = serializers.UUIDField(read_only=True, source='id') - remoteapp_display = serializers.SerializerMethodField() - - class Meta: - only_fields = ['id', 'name'] - - @staticmethod - def get_remoteapp_display(obj): - return str(obj) - - -class RemoteAppPermissionUserRelationSerializer(serializers.ModelSerializer): - user_display = serializers.ReadOnlyField() - remoteapppermission_display = serializers.ReadOnlyField() - - class Meta: - model = RemoteAppPermission.users.through - list_serializer_class = AdaptedBulkListSerializer - fields = [ - 'id', 'user', 'user_display', 'remoteapppermission', 'remoteapppermission_display' - ] diff --git a/apps/perms/signals_handler.py b/apps/perms/signals_handler.py index 95df4c4f2..9e0bfbaeb 100644 --- a/apps/perms/signals_handler.py +++ b/apps/perms/signals_handler.py @@ -7,11 +7,11 @@ from perms.tasks import create_rebuild_user_tree_task, \ create_rebuild_user_tree_task_by_related_nodes_or_assets from users.models import User, UserGroup from assets.models import Asset, SystemUser -from applications.models import Application, Category +from applications.models import Application from common.utils import get_logger from common.exceptions import M2MReverseNotAllowed from common.const.signals import POST_ADD, POST_REMOVE, POST_CLEAR -from .models import AssetPermission, RemoteAppPermission, ApplicationPermission +from .models import AssetPermission, ApplicationPermission logger = get_logger(__file__) @@ -187,51 +187,6 @@ def on_asset_permission_user_groups_changed(instance, action, pk_set, model, system_user.groups.add(*tuple(groups)) -@receiver(m2m_changed, sender=RemoteAppPermission.system_users.through) -def on_remote_app_permission_system_users_changed(sender, instance=None, - action='', reverse=False, **kwargs): - if action != POST_ADD or reverse: - return - system_users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) - logger.debug("Remote app permission system_users change signal received") - assets = instance.remote_apps.all().values_list('asset__id', flat=True) - users = instance.users.all().values_list('id', flat=True) - groups = instance.user_groups.all().values_list('id', flat=True) - for system_user in system_users: - system_user.assets.add(*tuple(assets)) - if system_user.username_same_with_user: - system_user.groups.add(*tuple(groups)) - system_user.users.add(*tuple(users)) - - -@receiver(m2m_changed, sender=RemoteAppPermission.users.through) -def on_remoteapps_permission_users_changed(sender, instance=None, action='', - reverse=False, **kwargs): - if action != POST_ADD and reverse: - return - logger.debug("Asset permission users change signal received") - users = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) - system_users = instance.system_users.all() - - for system_user in system_users: - if system_user.username_same_with_user: - system_user.users.add(*tuple(users)) - - -@receiver(m2m_changed, sender=RemoteAppPermission.user_groups.through) -def on_remoteapps_permission_user_groups_changed(sender, instance=None, action='', - reverse=False, **kwargs): - if action != POST_ADD and reverse: - return - logger.debug("Asset permission user groups change signal received") - groups = kwargs['model'].objects.filter(pk__in=kwargs['pk_set']) - system_users = instance.system_users.all() - - for system_user in system_users: - if system_user.username_same_with_user: - system_user.groups.add(*tuple(groups)) - - @receiver(m2m_changed, sender=Asset.nodes.through) def on_node_asset_change(action, instance, reverse, pk_set, **kwargs): if not need_rebuild_mapping_node(action): @@ -249,7 +204,7 @@ def on_node_asset_change(action, instance, reverse, pk_set, **kwargs): @receiver(m2m_changed, sender=ApplicationPermission.system_users.through) def on_application_permission_system_users_changed(sender, instance: ApplicationPermission, action, reverse, pk_set, **kwargs): - if instance.category != Category.remote_app: + if not instance.category_remote_app: return if reverse: @@ -277,7 +232,7 @@ def on_application_permission_system_users_changed(sender, instance: Application @receiver(m2m_changed, sender=ApplicationPermission.users.through) def on_application_permission_users_changed(sender, instance, action, reverse, pk_set, **kwargs): - if instance.category != Category.remote_app: + if not instance.category_remote_app: return if reverse: @@ -297,7 +252,7 @@ def on_application_permission_users_changed(sender, instance, action, reverse, p @receiver(m2m_changed, sender=ApplicationPermission.user_groups.through) def on_application_permission_user_groups_changed(sender, instance, action, reverse, pk_set, **kwargs): - if instance.category != Category.remote_app: + if not instance.category_remote_app: return if reverse: @@ -317,7 +272,7 @@ def on_application_permission_user_groups_changed(sender, instance, action, reve @receiver(m2m_changed, sender=ApplicationPermission.applications.through) def on_application_permission_applications_changed(sender, instance, action, reverse, pk_set, **kwargs): - if instance.category != Category.remote_app: + if not instance.category_remote_app: return if reverse: diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 97deef6bd..c2dfe2380 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -4,11 +4,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 -from .k8s_app_permission import k8s_app_permission_urlpatterns app_name = 'perms' @@ -16,10 +12,8 @@ old_version_urlpatterns = [ re_path('(?Puser|user-group|asset-permission|remote-app-permission)/.*', capi.redirect_plural_name_api) ] -urlpatterns = asset_permission_urlpatterns + \ - application_permission_urlpatterns + \ - remote_app_permission_urlpatterns + \ - database_app_permission_urlpatterns + \ - k8s_app_permission_urlpatterns + \ - old_version_urlpatterns + \ - system_users_permission_urlpatterns +urlpatterns = [] +urlpatterns += asset_permission_urlpatterns +urlpatterns += application_permission_urlpatterns +urlpatterns += system_users_permission_urlpatterns +urlpatterns += old_version_urlpatterns diff --git a/apps/perms/urls/database_app_permission.py b/apps/perms/urls/database_app_permission.py deleted file mode 100644 index a793f980e..000000000 --- a/apps/perms/urls/database_app_permission.py +++ /dev/null @@ -1,47 +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('database-app-permissions', api.DatabaseAppPermissionViewSet, 'database-app-permission') -router.register('database-app-permissions-users-relations', api.DatabaseAppPermissionUserRelationViewSet, 'database-app-permissions-users-relation') -router.register('database-app-permissions-user-groups-relations', api.DatabaseAppPermissionUserGroupRelationViewSet, 'database-app-permissions-user-groups-relation') -router.register('database-app-permissions-database-apps-relations', api.DatabaseAppPermissionDatabaseAppRelationViewSet, 'database-app-permissions-database-apps-relation') -router.register('database-app-permissions-system-users-relations', api.DatabaseAppPermissionSystemUserRelationViewSet, 'database-app-permissions-system-users-relation') - -user_permission_urlpatterns = [ - path('/database-apps/', api.UserGrantedDatabaseAppsApi.as_view(), name='user-database-apps'), - path('database-apps/', api.UserGrantedDatabaseAppsApi.as_view(), name='my-database-apps'), - - # DatabaseApps as tree - path('/database-apps/tree/', api.UserGrantedDatabaseAppsAsTreeApi.as_view(), name='user-databases-apps-tree'), - path('database-apps/tree/', api.UserGrantedDatabaseAppsAsTreeApi.as_view(), name='my-databases-apps-tree'), - - path('/database-apps//system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'), - path('database-apps//system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'), -] - -user_group_permission_urlpatterns = [ - path('/database-apps/', api.UserGroupGrantedDatabaseAppsApi.as_view(), name='user-group-database-apps'), -] - -permission_urlpatterns = [ - # 授权规则中授权的用户和数据库应用 - path('/users/all/', api.DatabaseAppPermissionAllUserListApi.as_view(), name='database-app-permission-all-users'), - path('/database-apps/all/', api.DatabaseAppPermissionAllDatabaseAppListApi.as_view(), name='database-app-permission-all-database-apps'), - - # 验证用户是否有某个数据库应用的权限 - path('user/validate/', api.ValidateUserDatabaseAppPermissionApi.as_view(), name='validate-user-database-app-permission'), -] - -database_app_permission_urlpatterns = [ - path('users/', include(user_permission_urlpatterns)), - path('user-groups/', include(user_group_permission_urlpatterns)), - path('database-app-permissions/', include(permission_urlpatterns)) -] - -database_app_permission_urlpatterns += router.urls diff --git a/apps/perms/urls/k8s_app_permission.py b/apps/perms/urls/k8s_app_permission.py deleted file mode 100644 index 2c145948b..000000000 --- a/apps/perms/urls/k8s_app_permission.py +++ /dev/null @@ -1,45 +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('k8s-app-permissions', api.K8sAppPermissionViewSet, 'k8s-app-permission') -router.register('k8s-app-permissions-users-relations', api.K8sAppPermissionUserRelationViewSet, 'k8s-app-permissions-users-relation') -router.register('k8s-app-permissions-user-groups-relations', api.K8sAppPermissionUserGroupRelationViewSet, 'k8s-app-permissions-user-groups-relation') -router.register('k8s-app-permissions-k8s-apps-relations', api.K8sAppPermissionK8sAppRelationViewSet, 'k8s-app-permissions-k8s-apps-relation') -router.register('k8s-app-permissions-system-users-relations', api.K8sAppPermissionSystemUserRelationViewSet, 'k8s-app-permissions-system-users-relation') - -user_permission_urlpatterns = [ - path('/k8s-apps/', api.UserGrantedK8sAppsApi.as_view(), name='user-k8s-apps'), - path('k8s-apps/', api.UserGrantedK8sAppsApi.as_view(), name='my-k8s-apps'), - - # k8sApps as tree - path('/k8s-apps/tree/', api.UserGrantedK8sAppsAsTreeApi.as_view(), name='user-k8ss-apps-tree'), - path('k8s-apps/tree/', api.UserGrantedK8sAppsAsTreeApi.as_view(), name='my-k8ss-apps-tree'), - - path('/k8s-apps//system-users/', api.UserGrantedK8sAppSystemUsersApi.as_view(), name='user-k8s-app-system-users'), - path('k8s-apps//system-users/', api.UserGrantedK8sAppSystemUsersApi.as_view(), name='user-k8s-app-system-users'), -] - -user_group_permission_urlpatterns = [ - path('/k8s-apps/', api.UserGroupGrantedK8sAppsApi.as_view(), name='user-group-k8s-apps'), -] - -permission_urlpatterns = [ - path('/users/all/', api.K8sAppPermissionAllUserListApi.as_view(), name='k8s-app-permission-all-users'), - path('/k8s-apps/all/', api.K8sAppPermissionAllK8sAppListApi.as_view(), name='k8s-app-permission-all-k8s-apps'), - - path('user/validate/', api.ValidateUserK8sAppPermissionApi.as_view(), name='validate-user-k8s-app-permission'), -] - -k8s_app_permission_urlpatterns = [ - path('users/', include(user_permission_urlpatterns)), - path('user-groups/', include(user_group_permission_urlpatterns)), - path('k8s-app-permissions/', include(permission_urlpatterns)) -] - -k8s_app_permission_urlpatterns += router.urls diff --git a/apps/perms/urls/remote_app_permission.py b/apps/perms/urls/remote_app_permission.py deleted file mode 100644 index 798ca9639..000000000 --- a/apps/perms/urls/remote_app_permission.py +++ /dev/null @@ -1,43 +0,0 @@ -# coding:utf-8 - -from django.urls import path -from rest_framework_bulk.routes import BulkRouter -from .. import api - - -router = BulkRouter() -router.register('remote-app-permissions', api.RemoteAppPermissionViewSet, 'remote-app-permission') -router.register('remote-app-permissions-users-relations', api.RemoteAppPermissionUserRelationViewSet, 'remote-app-permissions-users-relation') -router.register('remote-app-permissions-remote-apps-relations', api.RemoteAppPermissionRemoteAppRelationViewSet, 'remote-app-permissions-remote-apps-relation') - - -remote_app_permission_urlpatterns = [ - # 查询用户授权的RemoteApp - path('users//remote-apps/', api.UserGrantedRemoteAppsApi.as_view(), name='user-remote-apps'), - path('users/remote-apps/', api.UserGrantedRemoteAppsApi.as_view(), name='my-remote-apps'), - - # 获取用户授权的RemoteApp树 - path('users//remote-apps/tree/', api.UserGrantedRemoteAppsAsTreeApi.as_view(), name='user-remote-apps-as-tree'), - path('users/remote-apps/tree/', api.UserGrantedRemoteAppsAsTreeApi.as_view(), name='my-remote-apps-as-tree'), - - # 查询用户组授权的RemoteApp - path('user-groups//remote-apps/', api.UserGroupGrantedRemoteAppsApi.as_view(), name='user-group-remote-apps'), - - # RemoteApp System users - path('users//remote-apps//system-users/', api.UserGrantedRemoteAppSystemUsersApi.as_view(), name='user-remote-app-system-users'), - path('users/remote-apps//system-users/', api.UserGrantedRemoteAppSystemUsersApi.as_view(), name='my-remote-app-system-users'), - - # 校验用户对RemoteApp的权限 - path('remote-app-permissions/user/validate/', api.ValidateUserRemoteAppPermissionApi.as_view(), name='validate-user-remote-app-permission'), - - # 用户和RemoteApp变更 - path('remote-app-permissions//users/add/', api.RemoteAppPermissionAddUserApi.as_view(), name='remote-app-permission-add-user'), - path('remote-app-permissions//users/remove/', api.RemoteAppPermissionRemoveUserApi.as_view(), name='remote-app-permission-remove-user'), - path('remote-app-permissions//remote-apps/remove/', api.RemoteAppPermissionRemoveRemoteAppApi.as_view(), name='remote-app-permission-remove-remote-app'), - path('remote-app-permissions//remote-apps/add/', api.RemoteAppPermissionAddRemoteAppApi.as_view(), name='remote-app-permission-add-remote-app'), - - path('remote-app-permissions//remote-apps/all/', api.RemoteAppPermissionAllRemoteAppListApi.as_view(), name='remote-app-permission-all-remote-apps'), - path('remote-app-permissions//users/all/', api.RemoteAppPermissionAllUserListApi.as_view(), name='remote-app-permission-all-users'), -] - -remote_app_permission_urlpatterns += router.urls diff --git a/apps/perms/urls/views_urls.py b/apps/perms/urls/views_urls.py deleted file mode 100644 index 89d8c241a..000000000 --- a/apps/perms/urls/views_urls.py +++ /dev/null @@ -1,5 +0,0 @@ -# coding:utf-8 -app_name = 'perms' - -urlpatterns = [ -] diff --git a/apps/perms/utils/__init__.py b/apps/perms/utils/__init__.py index c29f9a2ae..e204cd61b 100644 --- a/apps/perms/utils/__init__.py +++ b/apps/perms/utils/__init__.py @@ -3,8 +3,3 @@ from .asset import * from .application import * - -# TODO: 删除 -from .remote_app_permission import * -from .database_app_permission import * -from .k8s_app_permission import * diff --git a/apps/perms/utils/database_app_permission.py b/apps/perms/utils/database_app_permission.py deleted file mode 100644 index dd51ca458..000000000 --- a/apps/perms/utils/database_app_permission.py +++ /dev/null @@ -1,100 +0,0 @@ -# coding: utf-8 -# - -from django.utils.translation import ugettext as _ -from django.db.models import Q - -from orgs.utils import set_to_root_org -from ..models import DatabaseAppPermission -from common.tree import TreeNode -from applications.models import DatabaseApp -from assets.models import SystemUser - - -__all__ = [ - 'DatabaseAppPermissionUtil', - 'construct_database_apps_tree_root', - 'parse_database_app_to_tree_node' -] - - -def get_user_database_app_permissions(user, include_group=True): - if include_group: - groups = user.groups.all() - arg = Q(users=user) | Q(user_groups__in=groups) - else: - arg = Q(users=user) - return DatabaseAppPermission.objects.all().valid().filter(arg) - - -def get_user_group_database_app_permission(user_group): - return DatabaseAppPermission.objects.all().valid().filter( - user_groups=user_group - ) - - -class DatabaseAppPermissionUtil: - get_permissions_map = { - 'User': get_user_database_app_permissions, - 'UserGroup': get_user_group_database_app_permission - } - - def __init__(self, obj): - self.object = obj - self.change_org_if_need() - - @staticmethod - def change_org_if_need(): - set_to_root_org() - - @property - def permissions(self): - obj_class = self.object.__class__.__name__ - func = self.get_permissions_map[obj_class] - _permissions = func(self.object) - return _permissions - - def get_database_apps(self): - database_apps = DatabaseApp.objects.filter( - granted_by_permissions__in=self.permissions - ).distinct() - return database_apps - - def get_database_app_system_users(self, database_app): - queryset = self.permissions - kwargs = {'database_apps': database_app} - queryset = queryset.filter(**kwargs) - system_users_ids = queryset.values_list('system_users', flat=True) - system_users_ids = system_users_ids.distinct() - system_users = SystemUser.objects.filter(id__in=system_users_ids) - system_users = system_users.order_by('-priority') - return system_users - - -def construct_database_apps_tree_root(amount): - tree_root = { - 'id': 'ID_DATABASE_APP_ROOT', - 'name': '{} ({})'.format(_('DatabaseApp'), amount), - 'title': 'DatabaseApp', - 'pId': '', - 'open': False, - 'isParent': True, - 'iconSkin': '', - 'meta': {'type': 'database_app'} - } - return TreeNode(**tree_root) - - -def parse_database_app_to_tree_node(parent, database_app): - pid = parent.id if parent else '' - tree_node = { - 'id': database_app.id, - 'name': database_app.name, - 'title': database_app.name, - 'pId': pid, - 'open': False, - 'isParent': False, - 'iconSkin': 'file', - 'meta': {'type': 'database_app'} - } - return TreeNode(**tree_node) diff --git a/apps/perms/utils/k8s_app_permission.py b/apps/perms/utils/k8s_app_permission.py deleted file mode 100644 index 74cf79066..000000000 --- a/apps/perms/utils/k8s_app_permission.py +++ /dev/null @@ -1,93 +0,0 @@ -# coding: utf-8 -# - -from django.utils.translation import ugettext as _ -from django.db.models import Q - -from orgs.utils import set_to_root_org -from ..models import K8sAppPermission -from common.tree import TreeNode -from applications.models import K8sApp -from assets.models import SystemUser - - -def get_user_k8s_app_permissions(user, include_group=True): - if include_group: - groups = user.groups.all() - arg = Q(users=user) | Q(user_groups__in=groups) - else: - arg = Q(users=user) - return K8sAppPermission.objects.all().valid().filter(arg) - - -def get_user_group_k8s_app_permission(user_group): - return K8sAppPermission.objects.all().valid().filter( - user_groups=user_group - ) - - -class K8sAppPermissionUtil: - get_permissions_map = { - 'User': get_user_k8s_app_permissions, - 'UserGroup': get_user_group_k8s_app_permission - } - - def __init__(self, obj): - self.object = obj - self.change_org_if_need() - - @staticmethod - def change_org_if_need(): - set_to_root_org() - - @property - def permissions(self): - obj_class = self.object.__class__.__name__ - func = self.get_permissions_map[obj_class] - _permissions = func(self.object) - return _permissions - - def get_k8s_apps(self): - k8s_apps = K8sApp.objects.filter( - granted_by_permissions__in=self.permissions - ).distinct() - return k8s_apps - - def get_k8s_app_system_users(self, k8s_app): - queryset = self.permissions - kwargs = {'k8s_apps': k8s_app} - queryset = queryset.filter(**kwargs) - system_users_ids = queryset.values_list('system_users', flat=True) - system_users_ids = system_users_ids.distinct() - system_users = SystemUser.objects.filter(id__in=system_users_ids) - system_users = system_users.order_by('-priority') - return system_users - - -def construct_k8s_apps_tree_root(amount): - tree_root = { - 'id': 'ID_K8S_APP_ROOT', - 'name': '{} ({})'.format(_('KubernetesApp'), amount), - 'title': 'K8sApp', - 'pId': '', - 'open': False, - 'isParent': True, - 'iconSkin': '', - 'meta': {'type': 'k8s_app'} - } - return TreeNode(**tree_root) - - -def parse_k8s_app_to_tree_node(parent, k8s_app): - pid = parent.id if parent else '' - tree_node = { - 'id': k8s_app.id, - 'name': k8s_app.name, - 'title': k8s_app.name, - 'pId': pid, - 'open': False, - 'isParent': False, - 'iconSkin': 'file', - 'meta': {'type': 'k8s_app'} - } - return TreeNode(**tree_node) diff --git a/apps/perms/utils/remote_app_permission.py b/apps/perms/utils/remote_app_permission.py deleted file mode 100644 index 0fb857611..000000000 --- a/apps/perms/utils/remote_app_permission.py +++ /dev/null @@ -1,99 +0,0 @@ -# coding: utf-8 -# - -from django.utils.translation import ugettext as _ -from django.db.models import Q - -from common.tree import TreeNode -from orgs.utils import set_to_root_org - -from ..models import RemoteAppPermission -from ..hands import RemoteApp, SystemUser - -__all__ = [ - 'RemoteAppPermissionUtil', - 'construct_remote_apps_tree_root', - 'parse_remote_app_to_tree_node', -] - - -def get_user_remote_app_permissions(user, include_group=True): - if include_group: - groups = user.groups.all() - arg = Q(users=user) | Q(user_groups__in=groups) - else: - arg = Q(users=user) - return RemoteAppPermission.objects.all().valid().filter(arg) - - -def get_user_group_remote_app_permissions(user_group): - return RemoteAppPermission.objects.all().valid().filter( - user_groups=user_group - ) - - -class RemoteAppPermissionUtil: - get_permissions_map = { - "User": get_user_remote_app_permissions, - "UserGroup": get_user_group_remote_app_permissions, - } - - def __init__(self, obj): - self.object = obj - self.change_org_if_need() - - @staticmethod - def change_org_if_need(): - set_to_root_org() - - @property - def permissions(self): - obj_class = self.object.__class__.__name__ - func = self.get_permissions_map[obj_class] - _permissions = func(self.object) - return _permissions - - def get_remote_apps(self): - remote_apps = RemoteApp.objects.filter( - granted_by_permissions__in=self.permissions - ).distinct() - return remote_apps - - def get_remote_app_system_users(self, remote_app): - queryset = self.permissions - kwargs = {"remote_apps": remote_app} - queryset = queryset.filter(**kwargs) - system_users_ids = queryset.values_list('system_users', flat=True) - system_users_ids = system_users_ids.distinct() - system_users = SystemUser.objects.filter(id__in=system_users_ids) - system_users = system_users.order_by('-priority') - return system_users - - -def construct_remote_apps_tree_root(amount): - tree_root = { - 'id': 'ID_REMOTE_APP_ROOT', - 'name': '{} ({})'.format(_('RemoteApp'), amount), - 'title': 'RemoteApp', - 'pId': '', - 'open': False, - 'isParent': True, - 'iconSkin': '', - 'meta': {'type': 'remote_app'} - } - return TreeNode(**tree_root) - - -def parse_remote_app_to_tree_node(parent, remote_app): - pid = parent.id if parent else '' - tree_node = { - 'id': remote_app.id, - 'name': remote_app.name, - 'title': remote_app.name, - 'pId': pid, - 'open': False, - 'isParent': False, - 'iconSkin': 'file', - 'meta': {'type': 'remote_app'} - } - return TreeNode(**tree_node) diff --git a/apps/tickets/api/ticket/ticket.py b/apps/tickets/api/ticket/ticket.py index db5a8850d..f3903a15e 100644 --- a/apps/tickets/api/ticket/ticket.py +++ b/apps/tickets/api/ticket/ticket.py @@ -12,6 +12,7 @@ from common.permissions import IsValidUser, IsOrgAdmin from tickets import serializers from tickets.models import Ticket from tickets.permissions.ticket import IsAssignee, NotClosed +from tickets.serializers.ticket.utils import get_dynamic_mapping_fields_mapping_rule_by_view __all__ = ['TicketViewSet'] @@ -66,8 +67,5 @@ class TicketViewSet(CommonApiMixin, viewsets.ModelViewSet): return super().update(request, *args, **kwargs) def get_dynamic_mapping_fields_mapping_rule(self): - from tickets.serializers.ticket.meta import get_meta_field_mapping_rule_by_view - meta_field_mapping_rule = get_meta_field_mapping_rule_by_view(self) - return { - 'meta': meta_field_mapping_rule, - } + fields_mapping_rule = get_dynamic_mapping_fields_mapping_rule_by_view(view=self) + return fields_mapping_rule diff --git a/apps/tickets/models/ticket/mixin/meta/apply_application.py b/apps/tickets/models/ticket/mixin/meta/apply_application.py index f8722daae..08eae72d8 100644 --- a/apps/tickets/models/ticket/mixin/meta/apply_application.py +++ b/apps/tickets/models/ticket/mixin/meta/apply_application.py @@ -1,6 +1,7 @@ from django.utils.translation import ugettext as __ from orgs.utils import tmp_to_org, tmp_to_root_org -from applications.models import Application, Category +from applications.models import Application +from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices from assets.models import SystemUser from perms.models import ApplicationPermission from tickets.utils import convert_model_data_field_name_to_verbose_name @@ -10,9 +11,9 @@ class ConstructDisplayFieldMixin: def construct_meta_apply_application_open_fields_display(self): meta_display_fields = ['apply_category_display', 'apply_type_display'] apply_category = self.meta['apply_category'] - apply_category_display = dict(Category.choices)[apply_category] + apply_category_display = ApplicationCategoryChoices.get_label(apply_category) apply_type = self.meta['apply_type'] - apply_type_display = dict(Category.get_type_choices(apply_category))[apply_type] + apply_type_display = ApplicationTypeChoices.get_label(apply_type) meta_display_values = [apply_category_display, apply_type_display] meta_display = dict(zip(meta_display_fields, meta_display_values)) return meta_display diff --git a/apps/tickets/serializers/ticket/__init__.py b/apps/tickets/serializers/ticket/__init__.py index bb2ee74d6..705837a0d 100644 --- a/apps/tickets/serializers/ticket/__init__.py +++ b/apps/tickets/serializers/ticket/__init__.py @@ -1,2 +1,3 @@ from .ticket import * from .meta import * +from .utils import * diff --git a/apps/tickets/serializers/ticket/meta/__init__.py b/apps/tickets/serializers/ticket/meta/__init__.py index 9b5ed21c9..7b5fbad28 100644 --- a/apps/tickets/serializers/ticket/meta/__init__.py +++ b/apps/tickets/serializers/ticket/meta/__init__.py @@ -1 +1 @@ -from .base import * +from .meta import * diff --git a/apps/tickets/serializers/ticket/meta/apply_application.py b/apps/tickets/serializers/ticket/meta/apply_application.py index aa6c1d4a5..f9b516970 100644 --- a/apps/tickets/serializers/ticket/meta/apply_application.py +++ b/apps/tickets/serializers/ticket/meta/apply_application.py @@ -1,7 +1,8 @@ from rest_framework import serializers from django.utils.translation import ugettext_lazy as _ -from applications.models import Category, Application +from applications.models import Application +from applications.const import ApplicationCategoryChoices, ApplicationTypeChoices from assets.models import SystemUser from .mixin import BaseApproveSerializerMixin @@ -13,13 +14,13 @@ __all__ = [ class ApplySerializer(serializers.Serializer): # 申请信息 apply_category = serializers.ChoiceField( - required=True, choices=Category.choices, label=_('Category') + required=True, choices=ApplicationCategoryChoices.choices, label=_('Category') ) apply_category_display = serializers.CharField( read_only=True, label=_('Category display') ) apply_type = serializers.ChoiceField( - required=True, choices=Category.get_all_type_choices(), label=_('Type') + required=True, choices=ApplicationTypeChoices.choices, label=_('Type') ) apply_type_display = serializers.CharField( required=False, read_only=True, label=_('Type display') @@ -39,16 +40,6 @@ class ApplySerializer(serializers.Serializer): required=True, label=_('Date expired') ) - def validate_apply_type(self, tp): - category = self.root.initial_data['meta'].get('apply_category') - if not category: - return tp - valid_type_types = list((dict(Category.get_type_choices(category)).keys())) - if tp in valid_type_types: - return tp - error = _('Type `{}` is not a valid choice `({}){}`'.format(tp, category, valid_type_types)) - raise serializers.ValidationError(error) - class ApproveSerializer(BaseApproveSerializerMixin, serializers.Serializer): # 审批信息 diff --git a/apps/tickets/serializers/ticket/meta/base.py b/apps/tickets/serializers/ticket/meta/base.py deleted file mode 100644 index 4a53776e9..000000000 --- a/apps/tickets/serializers/ticket/meta/base.py +++ /dev/null @@ -1,104 +0,0 @@ -import copy -from rest_framework import serializers -from django.utils.translation import ugettext_lazy as _ - -from common.exceptions import JMSException -from tickets import const -from . import apply_asset, apply_application, login_confirm - -__all__ = [ - 'meta_dynamic_mapping_fields_mapping_rules', - 'get_meta_field_mapping_rule_by_view', -] - -# -# ticket type -# ----------- - - -types = const.TicketTypeChoices.values -type_apply_asset = const.TicketTypeChoices.apply_asset.value -type_apply_application = const.TicketTypeChoices.apply_application.value -type_login_confirm = const.TicketTypeChoices.login_confirm.value - -# -# ticket type -# ----------- - - -actions = const.TicketActionChoices.values -action_open = const.TicketActionChoices.open.value -action_approve = const.TicketActionChoices.approve.value -action_reject = const.TicketActionChoices.reject.value -action_close = const.TicketActionChoices.close.value - - -# -# define meta field `DynamicMappingField` mapping_rules -# ----------------------------------------------------- - - -meta_dynamic_mapping_fields_mapping_rules = { - 'default': serializers.ReadOnlyField, - 'type': { - type_apply_asset: { - action_open: apply_asset.ApplySerializer, - action_approve: apply_asset.ApproveSerializer, - }, - type_apply_application: { - action_open: apply_application.ApplySerializer, - action_approve: apply_application.ApproveSerializer, - }, - type_login_confirm: { - action_open: login_confirm.ApplySerializer, - } - } -} - - -# -# get meta dynamic field mapping rule by view -# ------------------------------------------- - - -def get_meta_field_mapping_rule_by_view(view): - mapping_rules = copy.deepcopy(meta_dynamic_mapping_fields_mapping_rules) - request = view.request - - # type - tp = request.query_params.get('type') - if not tp: - return ['default'] - if tp not in types: - error = _('Query parameter `type` ({}) not in choices: {}'.format(tp, types)) - raise JMSException(error) - if tp not in mapping_rules['type']: - return ['default'] - - # action - action = view.action - if action in ['metadata']: - # options - action = request.query_params.get('action') - if not action: - error = _('Please carry query parameter `action`') - raise JMSException(error) - if action not in actions: - error = _('Query parameter `action` ({}) not in choices: {}'.format(action, actions)) - raise JMSException(error) - if action not in mapping_rules['type'][tp]: - return ['default'] - - # display - if action in ['list', 'retrieve']: - return ['default'] - - if not mapping_rules['type'][tp].get(action): - return ['default'] - - return ['type', tp, action] - - - - - diff --git a/apps/tickets/serializers/ticket/meta/meta.py b/apps/tickets/serializers/ticket/meta/meta.py new file mode 100644 index 000000000..63b17b0c9 --- /dev/null +++ b/apps/tickets/serializers/ticket/meta/meta.py @@ -0,0 +1,73 @@ +import copy +from common.drf.fields import IgnoreSensitiveInfoReadOnlyJSONField +from tickets import const +from . import apply_asset, apply_application, login_confirm + +__all__ = [ + 'get_meta_field_dynamic_mapping_rules', + 'get_meta_field_mapping_rule_by_view', +] + +# +# ticket type +# ----------- + + +type_apply_asset = const.TicketTypeChoices.apply_asset.value +type_apply_application = const.TicketTypeChoices.apply_application.value +type_login_confirm = const.TicketTypeChoices.login_confirm.value + +# +# ticket action +# ------------- + + +actions = const.TicketActionChoices.values +action_open = const.TicketActionChoices.open.value +action_approve = const.TicketActionChoices.approve.value +action_reject = const.TicketActionChoices.reject.value +action_close = const.TicketActionChoices.close.value + + +# +# defines the dynamic mapping rules for the DynamicMappingField `meta` +# -------------------------------------------------------------------- + + +__META_FIELD_DYNAMIC_MAPPING_RULES = { + 'default': IgnoreSensitiveInfoReadOnlyJSONField, + 'type': { + type_apply_asset: { + action_open: apply_asset.ApplySerializer, + action_approve: apply_asset.ApproveSerializer, + }, + type_apply_application: { + action_open: apply_application.ApplySerializer, + action_approve: apply_application.ApproveSerializer, + }, + type_login_confirm: { + action_open: login_confirm.ApplySerializer, + } + } +} + + +# Note: +# The dynamic mapping rules of `meta` field is obtained +# through call method `get_meta_field_dynamic_mapping_rules` + +def get_meta_field_dynamic_mapping_rules(): + return copy.deepcopy(__META_FIELD_DYNAMIC_MAPPING_RULES) + + +# +# get `meta dynamic field` mapping rule by `view object` +# ------------------------------------------------------ + + +def get_meta_field_mapping_rule_by_view(view): + query_type = view.request.query_params.get('type') + query_action = view.request.query_params.get('action') + action = query_action if query_action else view.action + mapping_rule = ['type', query_type, action] + return mapping_rule diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py index cf531a353..61370e995 100644 --- a/apps/tickets/serializers/ticket/ticket.py +++ b/apps/tickets/serializers/ticket/ticket.py @@ -8,7 +8,7 @@ from orgs.mixins.serializers import OrgResourceModelSerializerMixin from users.models import User from tickets import const from tickets.models import Ticket -from .meta import meta_dynamic_mapping_fields_mapping_rules +from .meta import get_meta_field_dynamic_mapping_rules __all__ = [ 'TicketSerializer', 'TicketDisplaySerializer', @@ -21,7 +21,7 @@ class TicketSerializer(OrgResourceModelSerializerMixin): type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type')) action_display = serializers.ReadOnlyField(source='get_action_display', label=_('Action')) status_display = serializers.ReadOnlyField(source='get_status_display', label=_('Status')) - meta = DynamicMappingField(mapping_rules=meta_dynamic_mapping_fields_mapping_rules) + meta = DynamicMappingField(mapping_rules=get_meta_field_dynamic_mapping_rules()) class Meta: model = Ticket diff --git a/apps/tickets/serializers/ticket/utils.py b/apps/tickets/serializers/ticket/utils.py new file mode 100644 index 000000000..88fef96d1 --- /dev/null +++ b/apps/tickets/serializers/ticket/utils.py @@ -0,0 +1,16 @@ +from .meta import get_meta_field_mapping_rule_by_view + +__all__ = [ + 'get_dynamic_mapping_fields_mapping_rule_by_view' +] + + +# +# get `dynamic fields` mapping rule by `view object` +# ---------------------------------------------------- + + +def get_dynamic_mapping_fields_mapping_rule_by_view(view): + return { + 'meta': get_meta_field_mapping_rule_by_view(view=view) + }