Skip to content

Commit

Permalink
add gateway app binding (#329)
Browse files Browse the repository at this point in the history
* add gateway app binding
  • Loading branch information
alex-smile authored Nov 8, 2023
1 parent b9e8cca commit e6e0682
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 6 deletions.
30 changes: 30 additions & 0 deletions src/dashboard/apigateway/apigateway/apps/gateway/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
from django.contrib import admin

from .models import GatewayAppBinding


class GatewayAppBindingAdmin(admin.ModelAdmin):
list_display = ["id", "gateway", "bk_app_code", "updated_time"]
search_fields = ["gateway__id", "bk_app_code"]
list_filter = ["gateway"]


admin.site.register(GatewayAppBinding, GatewayAppBindingAdmin)
22 changes: 22 additions & 0 deletions src/dashboard/apigateway/apigateway/apps/gateway/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
from django.apps import AppConfig


class GatewayConfig(AppConfig):
name = "apigateway.apps.gateway"
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
# Generated by Django 3.2.18 on 2023-11-08 03:52

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('core', '0032_gateway__developers'),
]

operations = [
migrations.CreateModel(
name='GatewayAppBinding',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_time', models.DateTimeField(auto_now_add=True, null=True)),
('updated_time', models.DateTimeField(auto_now=True, null=True)),
('created_by', models.CharField(blank=True, max_length=32, null=True)),
('updated_by', models.CharField(blank=True, max_length=32, null=True)),
('bk_app_code', models.CharField(db_index=True, max_length=32)),
('gateway', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.gateway')),
],
options={
'db_table': 'gateway_app_binding',
'unique_together': {('gateway', 'bk_app_code')},
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
39 changes: 39 additions & 0 deletions src/dashboard/apigateway/apigateway/apps/gateway/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
from django.db import models

from apigateway.common.mixins.models import OperatorModelMixin, TimestampedModelMixin
from apigateway.core.models import Gateway


class GatewayAppBinding(TimestampedModelMixin, OperatorModelMixin):
"""
网关绑定的蓝鲸应用
- 仅影响 HomePage 中运维开发分数的计算
"""

gateway = models.ForeignKey(Gateway, on_delete=models.CASCADE)
bk_app_code = models.CharField(max_length=32, db_index=True)

def __str__(self):
return f"<GatewayAppBinding: {self.bk_app_code}/{self.gateway_id}>"

class Meta:
db_table = "gateway_app_binding"
unique_together = ("gateway", "bk_app_code")
29 changes: 27 additions & 2 deletions src/dashboard/apigateway/apigateway/apps/gateway/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@
from tencent_apigateway_common.i18n.field import SerializerTranslatedField

from apigateway.apps.gateway.utils import get_gateway_feature_flags
from apigateway.biz.gateway_app_binding import GatewayAppBindingHandler
from apigateway.common.contexts import APIAuthContext, APIFeatureFlagContext
from apigateway.core.constants import API_NAME_PATTERN, APIHostingTypeEnum, APITypeEnum, UserAuthTypeEnum
from apigateway.core.constants import (
API_NAME_PATTERN,
APP_CODE_PATTERN,
APIHostingTypeEnum,
APITypeEnum,
UserAuthTypeEnum,
)
from apigateway.core.models import Gateway
from apigateway.core.validators import ReservedAPINameValidator
from apigateway.utils.crypto import calculate_fingerprint
Expand All @@ -47,6 +54,11 @@ class GatewayCreateSLZ(serializers.ModelSerializer):
choices=APIHostingTypeEnum.get_choices(),
default=settings.DEFAULT_GATEWAY_HOSTING_TYPE,
)
bk_app_codes = serializers.ListField(
child=serializers.RegexField(APP_CODE_PATTERN),
allow_empty=True,
required=False,
)

class Meta:
model = Gateway
Expand All @@ -59,8 +71,9 @@ class Meta:
"is_public",
"user_auth_type",
"hosting_type",
"bk_app_codes",
)
no_write_fields = ["user_auth_type"]
no_write_fields = ["user_auth_type", "bk_app_codes"]
lookup_field = "id"

# 使用 UniqueTogetherValidator,方便错误提示信息统一处理
Expand Down Expand Up @@ -96,6 +109,11 @@ def create(self, validated_data):
class GatewayUpdateSLZ(serializers.ModelSerializer):
maintainers = serializers.ListField(child=serializers.CharField(), allow_empty=True)
developers = serializers.ListField(child=serializers.CharField(), allow_empty=True, default=list)
bk_app_codes = serializers.ListField(
child=serializers.RegexField(APP_CODE_PATTERN),
allow_empty=True,
required=False,
)

class Meta:
model = Gateway
Expand All @@ -104,6 +122,7 @@ class Meta:
"maintainers",
"developers",
"is_public",
"bk_app_codes",
)
lookup_field = "id"

Expand Down Expand Up @@ -133,6 +152,7 @@ class GatewayDetailSLZ(serializers.ModelSerializer):
max_length=512,
required=False,
)
bk_app_codes = serializers.SerializerMethodField()

class Meta:
model = Gateway
Expand All @@ -156,6 +176,7 @@ class Meta:
"public_key_fingerprint",
"feature_flags",
"is_official",
"bk_app_codes",
)
extra_kwargs = {
"description_en": {
Expand Down Expand Up @@ -185,6 +206,9 @@ def get_is_official(self, obj):
api_type = self.context["auth_config"]["api_type"]
return APITypeEnum.is_official(api_type)

def get_bk_app_codes(self, obj):
return self.context.get("bk_app_codes", [])

@classmethod
def from_instance(cls, instance: Gateway):
# 根据网关实例创建,简化调用
Expand All @@ -193,6 +217,7 @@ def from_instance(cls, instance: Gateway):
context={
"auth_config": APIAuthContext().get_config(instance.pk),
"feature_flags": APIFeatureFlagContext().get_config(instance.pk, {}),
"bk_app_codes": GatewayAppBindingHandler.get_bound_app_codes(instance),
},
)

Expand Down
8 changes: 8 additions & 0 deletions src/dashboard/apigateway/apigateway/apps/gateway/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from apigateway.apps.audit.utils import record_audit_log
from apigateway.apps.gateway import serializers
from apigateway.biz.gateway import GatewayHandler
from apigateway.biz.gateway_app_binding import GatewayAppBindingHandler
from apigateway.common.contexts import APIAuthContext
from apigateway.common.error_codes import error_codes
from apigateway.controller.tasks import revoke_release, rolling_update_release
Expand Down Expand Up @@ -67,6 +68,7 @@ def create(self, request, *args, **kwargs):
gateway=slz.instance,
user_auth_type=slz.validated_data["user_auth_type"],
username=request.user.username,
app_codes_to_binding=slz.validated_data.get("bk_app_codes"),
)

# 3. record audit log
Expand Down Expand Up @@ -114,10 +116,16 @@ def update(self, request, *args, **kwargs):
slz = serializers.GatewayUpdateSLZ(instance, data=request.data)
slz.is_valid(raise_exception=True)

bk_app_codes = slz.validated_data.pop("bk_app_codes", None)

slz.save(
updated_by=request.user.username,
)

# 更新绑定的应用
if bk_app_codes is not None:
GatewayAppBindingHandler.update_gateway_app_bindings(instance, bk_app_codes)

GatewayHandler().add_update_audit_log(slz.instance, request.user.username)

return OKJsonResponse("OK")
Expand Down
8 changes: 7 additions & 1 deletion src/dashboard/apigateway/apigateway/biz/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from apigateway.apps.audit.utils import record_audit_log
from apigateway.apps.monitor.models import AlarmStrategy
from apigateway.apps.plugin.models import PluginBinding
from apigateway.biz.gateway_app_binding import GatewayAppBindingHandler
from apigateway.biz.iam import IAMHandler
from apigateway.common.contexts import APIAuthContext
from apigateway.core.api_auth import APIAuthConfig
Expand Down Expand Up @@ -132,6 +133,7 @@ def save_related_data(
user_config: Optional[dict] = None,
unfiltered_sensitive_keys: Optional[List[str]] = None,
api_type: Optional[APITypeEnum] = None,
app_codes_to_binding: Optional[List[str]] = None,
):
# 1. save api auth_config
GatewayHandler().save_auth_config(
Expand All @@ -158,7 +160,11 @@ def save_related_data(
if related_app_code:
APIRelatedApp.objects.add_related_app(gateway.id, related_app_code)

# 6. 在权限中心注册分级管理员,创建用户组
# 6. 更新待绑定的应用
if app_codes_to_binding is not None:
GatewayAppBindingHandler.update_gateway_app_bindings(gateway, app_codes_to_binding)

# 7. 在权限中心注册分级管理员,创建用户组
if settings.USE_BK_IAM_PERMISSION:
IAMHandler.register_grade_manager_and_builtin_user_groups(gateway)

Expand Down
48 changes: 48 additions & 0 deletions src/dashboard/apigateway/apigateway/biz/gateway_app_binding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
#
# TencentBlueKing is pleased to support the open source community by making
# 蓝鲸智云 - API 网关(BlueKing - APIGateway) available.
# Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
# Licensed under the MIT License (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://opensource.org/licenses/MIT
#
# Unless required by applicable law or agreed to in writing, software distributed under
# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
# either express or implied. See the License for the specific language governing permissions and
# limitations under the License.
#
# We undertake not to change the open source license (MIT license) applicable
# to the current version of the project delivered to anyone in the future.
#
from typing import List

from apigateway.apps.gateway.models import GatewayAppBinding
from apigateway.core.models import Gateway


class GatewayAppBindingHandler:
@classmethod
def update_gateway_app_bindings(cls, gateway: Gateway, bk_app_codes: List[str]):
"""
更新网关应用的绑定
- 1. 如果 bk_app_codes 中应用未绑定,则新增绑定
- 2. 如果已绑定的应用未在 bk_app_codes 中,则删除
"""
bound_app_codes = cls.get_bound_app_codes(gateway)
app_codes_to_add = set(bk_app_codes) - set(bound_app_codes)
app_codes_to_delete = set(bound_app_codes) - set(bk_app_codes)

if app_codes_to_add:
GatewayAppBinding.objects.bulk_create(
[GatewayAppBinding(gateway=gateway, bk_app_code=code) for code in app_codes_to_add]
)

if app_codes_to_delete:
GatewayAppBinding.objects.filter(gateway=gateway, bk_app_code__in=app_codes_to_delete).delete()

@staticmethod
def get_bound_app_codes(gateway: Gateway) -> List[str]:
"""获取已绑定的应用"""
return list(GatewayAppBinding.objects.filter(gateway=gateway).values_list("bk_app_code", flat=True))
2 changes: 2 additions & 0 deletions src/dashboard/apigateway/apigateway/conf/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"apigateway.apps.monitor",
"apigateway.schema",
"apigateway.core",
"apigateway.apps.gateway",
"apigateway.apps.access_strategy",
"apigateway.apps.plugin",
"apigateway.apps.label",
Expand Down Expand Up @@ -793,6 +794,7 @@
"MENU_ITEM_ESB_API_DOC": env.bool("FEATURE_FLAG_MENU_ITEM_ESB_API_DOC", True),
"SYNC_ESB_TO_APIGW_ENABLED": env.bool("FEATURE_FLAG_SYNC_ESB_TO_APIGW_ENABLED", True),
"GATEWAY_DEVELOPERS_ENABLED": env.bool("FEATURE_FLAG_GATEWAY_DEVELOPERS_ENABLED", False),
"GATEWAY_APP_BINDING_ENABLED": env.bool("FEATURE_FLAG_GATEWAY_APP_BINDING_ENABLED", False),
# api-support
"ENABLE_SDK": env.bool("FEATURE_FLAG_ENABLE_SDK", False),
"ALLOW_CREATE_APPCHAT": env.bool("FEATURE_FLAG_ALLOW_CREATE_APPCHAT", False),
Expand Down
Loading

0 comments on commit e6e0682

Please sign in to comment.