feat(applications): 添加 k8s 应用

pull/4471/head
xinwen 2020-08-07 11:52:34 +08:00 committed by 老广
parent 0a242c3e81
commit 91649a3908
30 changed files with 831 additions and 48 deletions

View File

@ -1,2 +1,3 @@
from .remote_app import * from .remote_app import *
from .database_app import * from .database_app import *
from .k8s_app import *

View File

@ -0,0 +1,20 @@
# 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

@ -0,0 +1,34 @@
# Generated by Django 2.2.13 on 2020-08-07 07:13
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('applications', '0004_auto_20191218_1705'),
]
operations = [
migrations.CreateModel(
name='K8sApp',
fields=[
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
('updated_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Updated by')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('name', models.CharField(max_length=128, verbose_name='Name')),
('type', models.CharField(choices=[('k8s', 'Kubernetes')], default='k8s', max_length=128, verbose_name='Type')),
('cluster', models.CharField(max_length=1024, verbose_name='Cluster')),
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
],
options={
'verbose_name': 'KubernetesApp',
'ordering': ('name',),
'unique_together': {('org_id', 'name')},
},
),
]

View File

@ -1,2 +1,3 @@
from .remote_app import * from .remote_app import *
from .database_app import * from .database_app import *
from .k8s_app import *

View File

@ -0,0 +1,27 @@
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,2 +1,3 @@
from .remote_app import * from .remote_app import *
from .database_app import * from .database_app import *
from .k8s_app import *

View File

@ -0,0 +1,22 @@
from rest_framework import serializers
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from .. import models
__all__ = [
'K8sAppSerializer',
]
class K8sAppSerializer(BulkOrgResourceModelSerializer):
type_display = serializers.CharField(source='get_type_display', read_only=True)
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

@ -12,6 +12,7 @@ app_name = 'applications'
router = BulkRouter() router = BulkRouter()
router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app') router.register(r'remote-apps', api.RemoteAppViewSet, 'remote-app')
router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app') router.register(r'database-apps', api.DatabaseAppViewSet, 'database-app')
router.register(r'k8s-apps', api.K8sAppViewSet, 'k8s-app')
urlpatterns = [ urlpatterns = [
path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'), path('remote-apps/<uuid:pk>/connection-info/', api.RemoteAppConnectionInfoApi.as_view(), name='remote-app-connection-info'),

View File

@ -0,0 +1,23 @@
# Generated by Django 2.2.13 on 2020-08-07 02:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0053_auto_20200723_1232'),
]
operations = [
migrations.AddField(
model_name='systemuser',
name='token',
field=models.TextField(default='', verbose_name='Token'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet'), ('vnc', 'vnc'), ('mysql', 'mysql'), ('k8s', 'k8s')], default='ssh', max_length=16, verbose_name='Protocol'),
),
]

View File

@ -91,12 +91,14 @@ class SystemUser(BaseUser):
PROTOCOL_TELNET = 'telnet' PROTOCOL_TELNET = 'telnet'
PROTOCOL_VNC = 'vnc' PROTOCOL_VNC = 'vnc'
PROTOCOL_MYSQL = 'mysql' PROTOCOL_MYSQL = 'mysql'
PROTOCOL_K8S = 'k8s'
PROTOCOL_CHOICES = ( PROTOCOL_CHOICES = (
(PROTOCOL_SSH, 'ssh'), (PROTOCOL_SSH, 'ssh'),
(PROTOCOL_RDP, 'rdp'), (PROTOCOL_RDP, 'rdp'),
(PROTOCOL_TELNET, 'telnet'), (PROTOCOL_TELNET, 'telnet'),
(PROTOCOL_VNC, 'vnc'), (PROTOCOL_VNC, 'vnc'),
(PROTOCOL_MYSQL, 'mysql'), (PROTOCOL_MYSQL, 'mysql'),
(PROTOCOL_K8S, 'k8s'),
) )
LOGIN_AUTO = 'auto' LOGIN_AUTO = 'auto'
@ -118,6 +120,7 @@ class SystemUser(BaseUser):
login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode')) login_mode = models.CharField(choices=LOGIN_MODE_CHOICES, default=LOGIN_AUTO, max_length=10, verbose_name=_('Login mode'))
cmd_filters = models.ManyToManyField('CommandFilter', related_name='system_users', verbose_name=_("Command filter"), blank=True) cmd_filters = models.ManyToManyField('CommandFilter', related_name='system_users', verbose_name=_("Command filter"), blank=True)
sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root")) sftp_root = models.CharField(default='tmp', max_length=128, verbose_name=_("SFTP Root"))
token = models.TextField(default='', verbose_name=_('Token'))
_prefer = 'system_user' _prefer = 'system_user'
def __str__(self): def __str__(self):

View File

@ -33,13 +33,14 @@ class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
'login_mode', 'login_mode_display', 'login_mode', 'login_mode_display',
'priority', 'username_same_with_user', 'priority', 'username_same_with_user',
'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment', 'auto_push', 'cmd_filters', 'sudo', 'shell', 'comment',
'auto_generate_key', 'sftp_root', 'auto_generate_key', 'sftp_root', 'token',
'assets_amount', 'date_created', 'created_by' 'assets_amount', 'date_created', 'created_by'
] ]
extra_kwargs = { extra_kwargs = {
'password': {"write_only": True}, 'password': {"write_only": True},
'public_key': {"write_only": True}, 'public_key': {"write_only": True},
'private_key': {"write_only": True}, 'private_key': {"write_only": True},
'token': {"write_only": True},
'nodes_amount': {'label': _('Node')}, 'nodes_amount': {'label': _('Node')},
'assets_amount': {'label': _('Asset')}, 'assets_amount': {'label': _('Asset')},
'login_mode_display': {'label': _('Login mode display')}, 'login_mode_display': {'label': _('Login mode display')},
@ -169,7 +170,7 @@ class SystemUserWithAuthInfoSerializer(SystemUserSerializer):
'login_mode', 'login_mode_display', 'login_mode', 'login_mode_display',
'priority', 'username_same_with_user', 'priority', 'username_same_with_user',
'auto_push', 'sudo', 'shell', 'comment', 'auto_push', 'sudo', 'shell', 'comment',
'auto_generate_key', 'sftp_root', 'auto_generate_key', 'sftp_root', 'token'
] ]
extra_kwargs = { extra_kwargs = {
'nodes_amount': {'label': _('Node')}, 'nodes_amount': {'label': _('Node')},

Binary file not shown.

View File

@ -21,15 +21,15 @@ msgstr ""
msgid "Custom" msgid "Custom"
msgstr "自定义" msgstr "自定义"
#: applications/models/database_app.py:18 applications/models/remote_app.py:21 #: applications/models/database_app.py:18 applications/models/k8s_app.py:11
#: assets/models/asset.py:145 assets/models/base.py:232 #: applications/models/remote_app.py:21 assets/models/asset.py:145
#: assets/models/cluster.py:18 assets/models/cmd_filter.py:21 #: assets/models/base.py:232 assets/models/cluster.py:18
#: assets/models/domain.py:20 assets/models/group.py:20 #: assets/models/cmd_filter.py:21 assets/models/domain.py:20
#: assets/models/label.py:18 ops/mixin.py:24 orgs/models.py:22 #: assets/models/group.py:20 assets/models/label.py:18 ops/mixin.py:24
#: perms/models/base.py:48 settings/models.py:27 terminal/models.py:26 #: orgs/models.py:22 perms/models/base.py:48 settings/models.py:27
#: terminal/models.py:342 terminal/models.py:374 terminal/models.py:411 #: terminal/models.py:26 terminal/models.py:342 terminal/models.py:374
#: users/forms/profile.py:20 users/models/group.py:15 users/models/user.py:489 #: terminal/models.py:411 users/forms/profile.py:20 users/models/group.py:15
#: users/templates/users/_select_user_modal.html:13 #: users/models/user.py:489 users/templates/users/_select_user_modal.html:13
#: users/templates/users/user_asset_permission.html:37 #: users/templates/users/user_asset_permission.html:37
#: users/templates/users/user_asset_permission.html:154 #: users/templates/users/user_asset_permission.html:154
#: users/templates/users/user_database_app_permission.html:36 #: users/templates/users/user_database_app_permission.html:36
@ -46,8 +46,9 @@ msgstr "自定义"
msgid "Name" msgid "Name"
msgstr "名称" msgstr "名称"
#: applications/models/database_app.py:22 assets/models/cmd_filter.py:52 #: applications/models/database_app.py:22 applications/models/k8s_app.py:14
#: terminal/models.py:376 terminal/models.py:413 tickets/models/ticket.py:40 #: assets/models/cmd_filter.py:52 terminal/models.py:376 terminal/models.py:413
#: tickets/models/ticket.py:40
#: users/templates/users/user_granted_database_app.html:35 #: users/templates/users/user_granted_database_app.html:35
msgid "Type" msgid "Type"
msgstr "类型" msgstr "类型"
@ -69,16 +70,16 @@ msgstr "数据库"
# msgid "Date created" # msgid "Date created"
# msgstr "创建日期" # msgstr "创建日期"
#: applications/models/database_app.py:33 applications/models/remote_app.py:45 #: applications/models/database_app.py:33 applications/models/k8s_app.py:18
#: assets/models/asset.py:150 assets/models/asset.py:226 #: applications/models/remote_app.py:45 assets/models/asset.py:150
#: assets/models/base.py:237 assets/models/cluster.py:29 #: assets/models/asset.py:226 assets/models/base.py:237
#: assets/models/cmd_filter.py:23 assets/models/cmd_filter.py:57 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:23
#: assets/models/domain.py:21 assets/models/domain.py:54 #: assets/models/cmd_filter.py:57 assets/models/domain.py:21
#: assets/models/group.py:23 assets/models/label.py:23 ops/models/adhoc.py:37 #: assets/models/domain.py:54 assets/models/group.py:23
#: orgs/models.py:25 perms/models/base.py:56 settings/models.py:32 #: assets/models/label.py:23 ops/models/adhoc.py:37 orgs/models.py:25
#: terminal/models.py:36 terminal/models.py:381 terminal/models.py:418 #: perms/models/base.py:56 settings/models.py:32 terminal/models.py:36
#: users/models/group.py:16 users/models/user.py:522 #: terminal/models.py:381 terminal/models.py:418 users/models/group.py:16
#: users/templates/users/user_detail.html:115 #: users/models/user.py:522 users/templates/users/user_detail.html:115
#: users/templates/users/user_granted_database_app.html:38 #: users/templates/users/user_granted_database_app.html:38
#: users/templates/users/user_granted_remote_app.html:37 #: users/templates/users/user_granted_remote_app.html:37
#: users/templates/users/user_group_detail.html:62 #: users/templates/users/user_group_detail.html:62
@ -99,6 +100,19 @@ msgstr "备注"
msgid "DatabaseApp" msgid "DatabaseApp"
msgstr "数据库应用" msgstr "数据库应用"
#: applications/models/k8s_app.py:9
msgid "Kubernetes"
msgstr ""
#: applications/models/k8s_app.py:16 assets/models/cluster.py:40
msgid "Cluster"
msgstr "集群"
#: applications/models/k8s_app.py:26 perms/models/k8s_app_permission.py:18
#: perms/utils/k8s_app_permission.py:70
msgid "KubernetesApp"
msgstr "Kubernetes应用"
#: applications/models/remote_app.py:23 assets/models/asset.py:352 #: applications/models/remote_app.py:23 assets/models/asset.py:352
#: assets/models/authbook.py:26 assets/models/gathered_user.py:14 #: assets/models/authbook.py:26 assets/models/gathered_user.py:14
#: assets/serializers/admin_user.py:32 assets/serializers/asset_user.py:47 #: assets/serializers/admin_user.py:32 assets/serializers/asset_user.py:47
@ -219,12 +233,12 @@ msgid "Hostname"
msgstr "主机名" msgstr "主机名"
#: assets/models/asset.py:190 assets/models/domain.py:52 #: assets/models/asset.py:190 assets/models/domain.py:52
#: assets/models/user.py:114 terminal/serializers/session.py:29 #: assets/models/user.py:116 terminal/serializers/session.py:29
msgid "Protocol" msgid "Protocol"
msgstr "协议" msgstr "协议"
#: assets/models/asset.py:192 assets/serializers/asset.py:69 #: assets/models/asset.py:192 assets/serializers/asset.py:69
#: perms/serializers/user_permission.py:60 #: perms/serializers/user_permission.py:71
msgid "Protocols" msgid "Protocols"
msgstr "协议组" msgstr "协议组"
@ -233,7 +247,7 @@ msgstr "协议组"
msgid "Domain" msgid "Domain"
msgstr "网域" msgstr "网域"
#: assets/models/asset.py:195 assets/models/user.py:109 #: assets/models/asset.py:195 assets/models/user.py:111
#: perms/models/asset_permission.py:91 #: perms/models/asset_permission.py:91
#: xpack/plugins/change_auth_plan/models.py:56 #: xpack/plugins/change_auth_plan/models.py:56
#: xpack/plugins/gathered_user/models.py:24 #: xpack/plugins/gathered_user/models.py:24
@ -427,10 +441,6 @@ msgstr "系统"
msgid "Default Cluster" msgid "Default Cluster"
msgstr "默认Cluster" msgstr "默认Cluster"
#: assets/models/cluster.py:40
msgid "Cluster"
msgstr "集群"
#: assets/models/cluster.py:56 #: assets/models/cluster.py:56
msgid "Beijing unicom" msgid "Beijing unicom"
msgstr "北京联通" msgstr "北京联通"
@ -443,7 +453,7 @@ msgstr "北京电信"
msgid "BGP full netcom" msgid "BGP full netcom"
msgstr "BGP全网通" msgstr "BGP全网通"
#: assets/models/cmd_filter.py:33 assets/models/user.py:119 #: assets/models/cmd_filter.py:33 assets/models/user.py:121
msgid "Command filter" msgid "Command filter"
msgstr "命令过滤器" msgstr "命令过滤器"
@ -468,7 +478,7 @@ msgstr "允许"
msgid "Filter" msgid "Filter"
msgstr "过滤器" msgstr "过滤器"
#: assets/models/cmd_filter.py:53 assets/models/user.py:113 #: assets/models/cmd_filter.py:53 assets/models/user.py:115
msgid "Priority" msgid "Priority"
msgstr "优先级" msgstr "优先级"
@ -597,57 +607,62 @@ msgstr "键"
msgid "Node" msgid "Node"
msgstr "节点" msgstr "节点"
#: assets/models/user.py:105 #: assets/models/user.py:107
msgid "Automatic login" msgid "Automatic login"
msgstr "自动登录" msgstr "自动登录"
#: assets/models/user.py:106 #: assets/models/user.py:108
msgid "Manually login" msgid "Manually login"
msgstr "手动登录" msgstr "手动登录"
#: assets/models/user.py:108 #: assets/models/user.py:110
msgid "Username same with user" msgid "Username same with user"
msgstr "用户名与用户相同" msgstr "用户名与用户相同"
#: assets/models/user.py:110 templates/_nav.html:39 #: assets/models/user.py:112 templates/_nav.html:39
#: xpack/plugins/change_auth_plan/models.py:52 #: xpack/plugins/change_auth_plan/models.py:52
msgid "Assets" msgid "Assets"
msgstr "资产管理" msgstr "资产管理"
#: assets/models/user.py:111 templates/_nav.html:17 #: assets/models/user.py:113 templates/_nav.html:17
#: users/views/profile/password.py:42 users/views/profile/pubkey.py:36 #: users/views/profile/password.py:42 users/views/profile/pubkey.py:36
msgid "Users" msgid "Users"
msgstr "用户管理" msgstr "用户管理"
#: assets/models/user.py:112 users/templates/users/user_group_list.html:90 #: assets/models/user.py:114 users/templates/users/user_group_list.html:90
#: users/templates/users/user_profile.html:124 #: users/templates/users/user_profile.html:124
msgid "User groups" msgid "User groups"
msgstr "用户组" msgstr "用户组"
#: assets/models/user.py:115 #: assets/models/user.py:117
msgid "Auto push" msgid "Auto push"
msgstr "自动推送" msgstr "自动推送"
#: assets/models/user.py:116 #: assets/models/user.py:118
msgid "Sudo" msgid "Sudo"
msgstr "Sudo" msgstr "Sudo"
#: assets/models/user.py:117 #: assets/models/user.py:119
msgid "Shell" msgid "Shell"
msgstr "Shell" msgstr "Shell"
#: assets/models/user.py:118 #: assets/models/user.py:120
msgid "Login mode" msgid "Login mode"
msgstr "登录模式" msgstr "登录模式"
#: assets/models/user.py:120 #: assets/models/user.py:122
msgid "SFTP Root" msgid "SFTP Root"
msgstr "SFTP根路径" msgstr "SFTP根路径"
#: assets/models/user.py:195 audits/models.py:39 #: assets/models/user.py:123 authentication/models.py:88
msgid "Token"
msgstr ""
#: assets/models/user.py:198 audits/models.py:39
#: perms/forms/asset_permission.py:95 perms/forms/remote_app_permission.py:49 #: perms/forms/asset_permission.py:95 perms/forms/remote_app_permission.py:49
#: perms/models/asset_permission.py:92 #: perms/models/asset_permission.py:92
#: perms/models/database_app_permission.py:22 #: perms/models/database_app_permission.py:22
#: perms/models/k8s_app_permission.py:22
#: perms/models/remote_app_permission.py:16 templates/_nav.html:45 #: perms/models/remote_app_permission.py:16 templates/_nav.html:45
#: terminal/backends/command/models.py:20 #: terminal/backends/command/models.py:20
#: terminal/backends/command/serializers.py:14 terminal/models.py:189 #: terminal/backends/command/serializers.py:14 terminal/models.py:189
@ -1212,10 +1227,6 @@ msgstr "登录复核"
msgid "City" msgid "City"
msgstr "城市" msgstr "城市"
#: authentication/models.py:88
msgid "Token"
msgstr ""
#: authentication/models.py:89 #: authentication/models.py:89
msgid "Expired" msgid "Expired"
msgstr "过期时间" msgstr "过期时间"
@ -1780,6 +1791,10 @@ msgstr "失效日期"
msgid "DatabaseApp permission" msgid "DatabaseApp permission"
msgstr "数据库应用授权" msgstr "数据库应用授权"
#: perms/models/k8s_app_permission.py:27
msgid "KubernetesApp permission"
msgstr "Kubernetes应用授权"
#: perms/models/remote_app_permission.py:20 #: perms/models/remote_app_permission.py:20
#: users/templates/users/_user_detail_nav_header.html:47 #: users/templates/users/_user_detail_nav_header.html:47
msgid "RemoteApp permission" msgid "RemoteApp permission"

View File

@ -12,3 +12,6 @@ from .database_app_permission import *
from .database_app_permission_relation import * from .database_app_permission_relation import *
from .user_database_app_permission import * from .user_database_app_permission import *
from .system_user_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

@ -0,0 +1,21 @@
# 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

@ -0,0 +1,111 @@
# 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

@ -0,0 +1,119 @@
# 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:
tree_root = utils.construct_k8s_apps_tree_root()
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

@ -0,0 +1,44 @@
# Generated by Django 2.2.13 on 2020-08-07 07:13
import common.utils.django
from django.conf import settings
from django.db import migrations, models
import django.utils.timezone
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0054_auto_20200807_1032'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('applications', '0005_k8sapp'),
('users', '0028_auto_20200728_1805'),
('perms', '0011_auto_20200721_1739'),
]
operations = [
migrations.CreateModel(
name='K8sAppPermission',
fields=[
('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')),
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('is_active', models.BooleanField(default=True, verbose_name='Active')),
('date_start', models.DateTimeField(db_index=True, default=django.utils.timezone.now, verbose_name='Date start')),
('date_expired', models.DateTimeField(db_index=True, default=common.utils.django.date_expired_default, verbose_name='Date expired')),
('created_by', models.CharField(blank=True, max_length=128, verbose_name='Created by')),
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('k8s_apps', models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='applications.K8sApp', verbose_name='KubernetesApp')),
('system_users', models.ManyToManyField(related_name='granted_by_k8s_app_permissions', to='assets.SystemUser', verbose_name='System user')),
('user_groups', models.ManyToManyField(blank=True, to='users.UserGroup', verbose_name='User group')),
('users', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'KubernetesApp permission',
'ordering': ('name',),
'unique_together': {('org_id', 'name')},
},
),
]

View File

@ -4,3 +4,4 @@
from .asset_permission import * from .asset_permission import *
from .remote_app_permission import * from .remote_app_permission import *
from .database_app_permission import * from .database_app_permission import *
from .k8s_app_permission import *

View File

@ -0,0 +1,39 @@
# 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

@ -9,3 +9,5 @@ from .asset_permission_relation import *
from .database_app_permission import * from .database_app_permission import *
from .database_app_permission_relation import * from .database_app_permission_relation import *
from .base import * from .base import *
from .k8s_app_permission import *
from .k8s_app_permission_relation import *

View File

@ -0,0 +1,50 @@
# coding: utf-8
#
from django.db.models import Count
from rest_framework import serializers
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'
]
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'
]

View File

@ -0,0 +1,73 @@
# 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

@ -14,6 +14,7 @@ __all__ = [
'ActionsSerializer', 'AssetSystemUserSerializer', 'ActionsSerializer', 'AssetSystemUserSerializer',
'RemoteAppSystemUserSerializer', 'RemoteAppSystemUserSerializer',
'DatabaseAppSystemUserSerializer', 'DatabaseAppSystemUserSerializer',
'K8sAppSystemUserSerializer',
] ]
@ -53,6 +54,16 @@ class DatabaseAppSystemUserSerializer(serializers.ModelSerializer):
read_only_fields = fields read_only_fields = fields
class K8sAppSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
only_fields = (
'id', 'name', 'username', 'priority', 'protocol', 'login_mode',
)
fields = list(only_fields)
read_only_fields = fields
class AssetGrantedSerializer(serializers.ModelSerializer): class AssetGrantedSerializer(serializers.ModelSerializer):
""" """
被授权资产的数据结构 被授权资产的数据结构

View File

@ -6,6 +6,7 @@ from .asset_permission import asset_permission_urlpatterns
from .remote_app_permission import remote_app_permission_urlpatterns from .remote_app_permission import remote_app_permission_urlpatterns
from .database_app_permission import database_app_permission_urlpatterns from .database_app_permission import database_app_permission_urlpatterns
from .system_user_permission import system_users_permission_urlpatterns from .system_user_permission import system_users_permission_urlpatterns
from .k8s_app_permission import k8s_app_permission_urlpatterns
app_name = 'perms' app_name = 'perms'
@ -16,5 +17,6 @@ old_version_urlpatterns = [
urlpatterns = asset_permission_urlpatterns + \ urlpatterns = asset_permission_urlpatterns + \
remote_app_permission_urlpatterns + \ remote_app_permission_urlpatterns + \
database_app_permission_urlpatterns + \ database_app_permission_urlpatterns + \
k8s_app_permission_urlpatterns + \
old_version_urlpatterns + \ old_version_urlpatterns + \
system_users_permission_urlpatterns system_users_permission_urlpatterns

View File

@ -0,0 +1,45 @@
# 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

@ -4,3 +4,4 @@
from .asset_permission import * from .asset_permission import *
from .remote_app_permission import * from .remote_app_permission import *
from .database_app_permission import * from .database_app_permission import *
from .k8s_app_permission import *

View File

@ -0,0 +1,93 @@
# 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():
tree_root = {
'id': 'ID_K8S_APP_ROOT',
'name': _('KubernetesApp'),
'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

@ -0,0 +1,18 @@
# Generated by Django 2.2.13 on 2020-08-10 09:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('terminal', '0024_auto_20200715_1713'),
]
operations = [
migrations.AlterField(
model_name='session',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('vnc', 'vnc'), ('telnet', 'telnet'), ('mysql', 'mysql'), ('k8s', 'kubernetes')], db_index=True, default='ssh', max_length=8),
),
]

View File

@ -179,6 +179,7 @@ class Session(OrgModelMixin):
('vnc', 'vnc'), ('vnc', 'vnc'),
('telnet', 'telnet'), ('telnet', 'telnet'),
('mysql', 'mysql'), ('mysql', 'mysql'),
('k8s', 'kubernetes')
) )
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)