Skip to content

Commit 2b4b933

Browse files
authored
feat: 蓝鲸插件支持限制接入系统业务调用 (#70)
* feat: 蓝鲸插件支持限制接入系统业务调用 * feat: release bk-plugin-framework 2.2.0 * minor: update dependencies * minor: review fix * minor: update README
1 parent 9f76160 commit 2b4b933

12 files changed

Lines changed: 147 additions & 10 deletions

File tree

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ class SopsExecPlugin(Plugin):
234234
...
235235
```
236236

237-
### 回调插件使用系统
237+
## 回调插件使用系统
238238

239239
如果**插件使用系统**支持接收蓝鲸插件的回调协议,则只需要添加两个配置,蓝鲸插件就可以在执行完成后回调**插件使用系统**(回调结果不影响插件任务执行结果),这样做的收益在于可以缓解因为**插件使用系统**不断轮询而带来的请求压力。
240240

@@ -260,6 +260,25 @@ class SopsExecPlugin(Plugin):
260260

261261
__注意__: 如果当前版本插件有在执行中的任务,请升级插件版本后再进行配置
262262

263+
## 限制插件使用系统下的特定业务使用(可选, bk-plugin-framework>=2.2.0)
264+
265+
如果你希望插件的执行只提供给使用系统的特定业务,可以在 `meta.py` 中添加对应的定义。
266+
267+
```python
268+
allow_scope = {
269+
"[插件使用系统的app_code]": {
270+
"type": "[业务概念的类型值,如 project]", # str
271+
"value": ["1", "2"] # List[str]
272+
}
273+
}
274+
```
275+
276+
上面的配置表示允许插件使用系统业务 1 和业务 2 调用 invoke 接口运行任务,其他的情况接口都会返回 403。
277+
278+
__注意__:
279+
1. 该功能需要插件使用系统支持,在调用 invoke 接口时设置请求头 `Bkplugin-Scope-Type``Bkplugin-Scope-Value`
280+
2. 如果不希望进行限制,请不要进行该配置。
281+
3. 如果插件开放给了特定插件使用系统,但是没有配置 allow_scope,默认全部请求通过。
263282

264283
## 插件初始化方法(可选)
265284

bk-plugin-framework/bk_plugin_framework/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@
1010
specific language governing permissions and limitations under the License.
1111
"""
1212

13-
__version__ = "2.1.1"
13+
__version__ = "2.2.0"

bk-plugin-framework/bk_plugin_framework/services/bpf_service/api/invoke.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
from django.utils.decorators import method_decorator
1616
from rest_framework import status
17-
from rest_framework import permissions
1817
from rest_framework import serializers
1918
from rest_framework.views import APIView
2019
from rest_framework.response import Response
@@ -27,6 +26,7 @@
2726

2827
from bk_plugin_framework.hub import VersionHub
2928
from bk_plugin_framework.runtime.executor import BKPluginExecutor
29+
from bk_plugin_framework.services.bpf_service.api.permissions import ScopeAllowPermission
3030
from bk_plugin_framework.services.bpf_service.api.serializers import StandardResponseSerializer
3131

3232
logger = logging.getLogger("bk_plugin")
@@ -53,7 +53,7 @@ class InvokeDataSerializer(serializers.Serializer):
5353
class Invoke(APIView):
5454

5555
authentication_classes = [] # csrf exempt
56-
permission_classes = [permissions.AllowAny]
56+
permission_classes = [ScopeAllowPermission]
5757

5858
@swagger_auto_schema(
5959
method="POST",

bk-plugin-framework/bk_plugin_framework/services/bpf_service/api/meta.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,10 @@ def get(self, request):
7272
meta_module = import_module("bk_plugin.meta")
7373
except ImportError:
7474
description = ""
75+
allow_scope = {}
7576
else:
7677
description = getattr(meta_module, "description", "")
78+
allow_scope = getattr(meta_module, "allow_scope", {})
7779

7880
return Response(
7981
{
@@ -85,6 +87,7 @@ def get(self, request):
8587
"description": description,
8688
"framework_version": FRAMEWORK_VERSION,
8789
"runtime_version": RUNTIME_VERSION,
90+
"allow_scope": allow_scope,
8891
},
8992
"message": "",
9093
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# -*- coding: utf-8 -*-
2+
from importlib import import_module
3+
4+
from rest_framework.permissions import BasePermission
5+
from rest_framework.request import Request
6+
7+
from bk_plugin_framework.utils.validations import validate_allow_scope
8+
9+
try:
10+
meta_module = import_module("bk_plugin.meta")
11+
except ImportError:
12+
allow_scope = {}
13+
else:
14+
allow_scope = getattr(meta_module, "allow_scope", {})
15+
if validate_allow_scope(allow_scope) is False:
16+
raise RuntimeError("allow_scope in meta.py is invalid")
17+
18+
19+
class ScopeAllowPermission(BasePermission):
20+
"""
21+
业务域鉴权,仅支持通过 apigw 的请求
22+
"""
23+
24+
def has_permission(self, request: Request, view):
25+
if not allow_scope:
26+
return True
27+
28+
bk_app_code = request.app.bk_app_code
29+
30+
# 如果没有进行使用方配置,则默认放行,如果不希望放行在插件开发者中心配置即可
31+
if bk_app_code not in allow_scope:
32+
return True
33+
34+
scope_type = request.headers.get("Bkplugin-Scope-Type")
35+
scope_value = request.headers.get("Bkplugin-Scope-Value")
36+
37+
return scope_type == allow_scope[bk_app_code]["type"] and scope_value in allow_scope[bk_app_code]["value"]

bk-plugin-framework/bk_plugin_framework/services/bpf_service/api/plugin_api_dispatch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from django.urls import resolve, Resolver404
1919
from django.utils.decorators import method_decorator
2020
from rest_framework import status
21-
from rest_framework import permissions
2221
from rest_framework import serializers
2322
from rest_framework.views import APIView
2423
from rest_framework.response import Response
@@ -29,6 +28,7 @@
2928
from blueapps.account.decorators import login_exempt
3029
from apigw_manager.apigw.decorators import apigw_require
3130

31+
from bk_plugin_framework.services.bpf_service.api.permissions import ScopeAllowPermission
3232
from bk_plugin_framework.services.bpf_service.api.serializers import StandardResponseSerializer
3333

3434
logger = logging.getLogger("bk_plugin")
@@ -67,7 +67,7 @@ def __init__(self, username):
6767
@method_decorator(apigw_require, name="dispatch")
6868
class PluginAPIDispatch(APIView):
6969
authentication_classes = [] # csrf exempt
70-
permission_classes = [permissions.AllowAny]
70+
permission_classes = [ScopeAllowPermission]
7171

7272
@swagger_auto_schema(
7373
method="POST",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# -*- coding: utf-8 -*-
2+
import logging
3+
4+
import jsonschema
5+
6+
7+
logger = logging.getLogger("bk-plugin-framework")
8+
9+
10+
ALLOW_SCOPE_SCHEMA = {
11+
"type": "object",
12+
"patternProperties": {
13+
".*": {
14+
"type": "object",
15+
"properties": {
16+
"type": {"type": "string"},
17+
"value": {"type": "array", "items": {"type": "string"}},
18+
},
19+
"required": ["type", "value"],
20+
},
21+
},
22+
}
23+
24+
25+
def validate_allow_scope(data: dict):
26+
try:
27+
jsonschema.validate(data, ALLOW_SCOPE_SCHEMA)
28+
except jsonschema.exceptions.ValidationError as e:
29+
logger.exception(f"allow_scope is invalid: {e}")
30+
return False
31+
return True

bk-plugin-framework/dev-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
werkzeug>=2.0.0,<3.0.0
33
pydantic>1.0,<2.0
4+
jsonschema>=2.5.0,<5.0
45

56
# services
67
Django>=2.2,<3.0

bk-plugin-framework/poetry.lock

Lines changed: 29 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bk-plugin-framework/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "bk-plugin-framework"
3-
version = "2.1.1"
3+
version = "2.2.0"
44
description = "bk plugin python framework"
55
authors = ["Your Name <you@example.com>"]
66
license = "MIT"
@@ -11,6 +11,7 @@ pydantic = "^1.0"
1111
werkzeug = "^2.0.0"
1212
apigw-manager = {version = ">=1.0.6, <1.2.0", extras = ["extra"]}
1313
bk-plugin-runtime = "2.0.5"
14+
jsonschema = ">=2.5.0,<5.0.0"
1415

1516
[tool.poetry.dev-dependencies]
1617
pytest = "^7.0.0"

0 commit comments

Comments
 (0)