reactor&remove: 重构applications模块 & 移除applications、perms中已不再使用的模块 (#5374)

* reactor: 重构applications模块 & 删除applications、perms中已不再使用的模块

 * reactor: 1. 针对application.attrs字段的view-serializer映射逻辑,采用DynamicMapping的方案重写;
 * reactor: 2. 删除applications和perms模块中已不再使用的database-app/k8s-app/remote-app模块;

* reactor: 添加迁移文件(删除perms/databaseperrmission/remoteapppermission/k8sapppermission)

* reactor: 修改细节

Co-authored-by: Bai <bugatti_it@163.com>
pull/5377/head
fit2bot 2021-01-04 05:27:03 +08:00 committed by GitHub
parent 428e8bf2a0
commit 7e7e24f51f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
98 changed files with 934 additions and 3109 deletions

View File

@ -1,5 +1,3 @@
from .application import *
from .mixin import *
from .remote_app import *
from .database_app import *
from .k8s_app import *

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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'),
)

View File

@ -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',
),
]

View File

@ -1,4 +1 @@
from .application import *
from .remote_app import *
from .database_app import *
from .k8s_app import *

View File

@ -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

View File

@ -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', )

View File

@ -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', )

View File

@ -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
}

View File

@ -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

View File

@ -1,5 +1,2 @@
from .application import *
from .remote_app import *
from .database_app import *
from .k8s_app import *
from .common import *

View File

@ -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

View File

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

View File

@ -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)

View File

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

View File

@ -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'))

View File

@ -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')
)

View File

@ -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

View File

@ -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 *

View File

@ -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')
)

View File

@ -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')
)

View File

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

View File

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

View File

@ -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'))

View File

@ -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')
)

View File

@ -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'))

View File

@ -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'))

View File

@ -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')
)

View File

@ -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

View File

@ -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')},
}

View File

@ -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',
]

View File

@ -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)

View File

@ -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),
}

View File

@ -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/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),
]
old_version_urlpatterns = [
re_path('(?P<resource>remote-app)/.*', capi.redirect_plural_name_api)
]
urlpatterns += router.urls + old_version_urlpatterns
urlpatterns += router.urls

View File

@ -1,7 +0,0 @@
# coding:utf-8
from django.urls import path
app_name = 'applications'
urlpatterns = [
]

View File

@ -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']

View File

@ -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
# -------------------

View File

@ -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()

View File

@ -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

View File

@ -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 *

View File

@ -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

View File

@ -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']

View File

@ -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

View File

@ -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,)

View File

@ -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

View File

@ -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,)

View File

@ -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

View File

@ -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

View File

@ -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})

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,6 +0,0 @@
# coding: utf-8
#
from .asset_permission import *
from .remote_app_permission import *
from .database_app_permission import *

View File

@ -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"]

View File

@ -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')}
),
}

View File

@ -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')}
)
}

View File

@ -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',
]

View File

@ -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',
),
]

View File

@ -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)

View File

@ -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 *

View File

@ -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()

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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 *

View File

@ -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

View File

@ -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)

View File

@ -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')},
}

View File

@ -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'
]

View File

@ -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')},
}

View File

@ -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'
]

View File

@ -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']

View File

@ -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'
]

View File

@ -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:

View File

@ -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('(?P<resource>user|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

View File

@ -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('<uuid:pk>/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('<uuid:pk>/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('<uuid:pk>/database-apps/<uuid:database_app_id>/system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'),
path('database-apps/<uuid:database_app_id>/system-users/', api.UserGrantedDatabaseAppSystemUsersApi.as_view(), name='user-database-app-system-users'),
]
user_group_permission_urlpatterns = [
path('<uuid:pk>/database-apps/', api.UserGroupGrantedDatabaseAppsApi.as_view(), name='user-group-database-apps'),
]
permission_urlpatterns = [
# 授权规则中授权的用户和数据库应用
path('<uuid:pk>/users/all/', api.DatabaseAppPermissionAllUserListApi.as_view(), name='database-app-permission-all-users'),
path('<uuid:pk>/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

View File

@ -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('<uuid:pk>/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('<uuid:pk>/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('<uuid:pk>/k8s-apps/<uuid:k8s_app_id>/system-users/', api.UserGrantedK8sAppSystemUsersApi.as_view(), name='user-k8s-app-system-users'),
path('k8s-apps/<uuid:k8s_app_id>/system-users/', api.UserGrantedK8sAppSystemUsersApi.as_view(), name='user-k8s-app-system-users'),
]
user_group_permission_urlpatterns = [
path('<uuid:pk>/k8s-apps/', api.UserGroupGrantedK8sAppsApi.as_view(), name='user-group-k8s-apps'),
]
permission_urlpatterns = [
path('<uuid:pk>/users/all/', api.K8sAppPermissionAllUserListApi.as_view(), name='k8s-app-permission-all-users'),
path('<uuid:pk>/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

View File

@ -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/<uuid:pk>/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/<uuid:pk>/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/<uuid:pk>/remote-apps/', api.UserGroupGrantedRemoteAppsApi.as_view(), name='user-group-remote-apps'),
# RemoteApp System users
path('users/<uuid:pk>/remote-apps/<uuid:remote_app_id>/system-users/', api.UserGrantedRemoteAppSystemUsersApi.as_view(), name='user-remote-app-system-users'),
path('users/remote-apps/<uuid:remote_app_id>/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/<uuid:pk>/users/add/', api.RemoteAppPermissionAddUserApi.as_view(), name='remote-app-permission-add-user'),
path('remote-app-permissions/<uuid:pk>/users/remove/', api.RemoteAppPermissionRemoveUserApi.as_view(), name='remote-app-permission-remove-user'),
path('remote-app-permissions/<uuid:pk>/remote-apps/remove/', api.RemoteAppPermissionRemoveRemoteAppApi.as_view(), name='remote-app-permission-remove-remote-app'),
path('remote-app-permissions/<uuid:pk>/remote-apps/add/', api.RemoteAppPermissionAddRemoteAppApi.as_view(), name='remote-app-permission-add-remote-app'),
path('remote-app-permissions/<uuid:pk>/remote-apps/all/', api.RemoteAppPermissionAllRemoteAppListApi.as_view(), name='remote-app-permission-all-remote-apps'),
path('remote-app-permissions/<uuid:pk>/users/all/', api.RemoteAppPermissionAllUserListApi.as_view(), name='remote-app-permission-all-users'),
]
remote_app_permission_urlpatterns += router.urls

View File

@ -1,5 +0,0 @@
# coding:utf-8
app_name = 'perms'
urlpatterns = [
]

View File

@ -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 *

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -1,2 +1,3 @@
from .ticket import *
from .meta import *
from .utils import *

View File

@ -1 +1 @@
from .base import *
from .meta import *

View File

@ -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):
# 审批信息

View File

@ -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]

View File

@ -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

View File

@ -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

View File

@ -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)
}