Skip to content

Commit

Permalink
Change to gateway name (#137)
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-smile authored Dec 1, 2023
1 parent 2b7653c commit 2de55aa
Show file tree
Hide file tree
Showing 47 changed files with 722 additions and 1,581 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/bkapi-client-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install 'tox==3.23.0' 'tox-gh-actions==2.12.0'
python -m pip install . 'tox-gh-actions==2.12.0' -r requirements_tox.txt
working-directory: sdks/bkapi-client-core

- name: Test with tox
run: tox
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ repos:
# Set language to disable pre-commit's virtual-env
language: system
types: [python]
exclude: "^(sdks/apigw-manager/.*|sdks/bkapi-client-core/.*)$"
entry: poetry run isort --settings-path=pyproject.toml
- id: black
name: black
Expand Down
25 changes: 22 additions & 3 deletions sdks/apigw-manager/CHANGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@
- 添加指令 add_related_apps,支持为网关添加关联应用
- definition.yaml 添加 spec_version 字段,指定配置文件版本号
- Django Command 中,通过参数 --gateway-name 指定网关
- 基础镜像 apigw-manager 中,sync-apigateway.sh 中去除指令 apply_apigw_permissions
- 优化请求 bk-apigateway 接口失败时,打印的错误消息
- 优化 README.md,提供 examples

Breaking changes:
- 基础镜像 apigw-manager 中,调整指令名称
- sync-apigateway 改为 sync-apigateway.sh
- apigw-manager 改为 apigw-manager.sh
- call_command 改为 call_command_or_warning
- call_definition_command 改为 call_definition_command_or_warning
- must_call_definition_command 改为 call_definition_command_or_exit
- 基础镜像 apigw-manager 中,sync-apigateway.sh 中去除指令 apply_apigw_permissions
- 优化请求 bk-apigateway 接口失败时,打印的错误消息
- 优化 README.md,提供 examples
- 以下函数中的参数名 api_name 改为 gateway_name
- ApiGatewayJWTUserMiddleware.get_user
- UserModelBackend.authenticate
- PublicKeyProvider.provide
- SettingsPublicKeyProvider.provide
- CachePublicKeyProvider.provide
- 以下函数中参数名 default_api_name 改为 default_gateway_name
- CachePublicKeyProvider.__init__
- DefaultJWTProvider.__init__
- DummyEnvPayloadJWTProvider.__init__
- JWTProvider.__init__
- PublicKeyProvider.__init__
- SettingsPublicKeyProvider.__init__

如果项目添加了自定义镜像,或自定义 Django 中间件,需要按照新的规则进行调整,或者锁定版本号
- 自定义镜像锁定基础镜像 apigw-manager 版本,版本号 < 3.0.0
- SDK 锁定版本号 < 3.0.0,如 poetry 可设置 `apigw-manager = "<3.0.0"`

### 2.0.1
- 修复镜像 sync-apigateway 中,同步任务失败时,脚本退出码为 0 的问题
Expand Down
12 changes: 6 additions & 6 deletions sdks/apigw-manager/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ AUTHENTICATION_BACKENDS += [

#### ApiGatewayJWTGenericMiddleware
认证 JWT 信息,在 `request` 中注入 `jwt` 对象,有以下属性:
- `api_name`:传入的网关名称;
- `gateway_name`:传入的网关名称;

#### ApiGatewayJWTAppMiddleware
解析 JWT 中的应用信息,在 `request` 中注入 `app` 对象,有以下属性:
Expand Down Expand Up @@ -267,15 +267,15 @@ auth.authenticate(request, username=username, verified=verified)
在 Django settings 中提供如下配置

```python
BK_APIGW_JWT_PROVIDER_CLS = "apigw-manager.apigw.providers.DummyEnvPayloadJWTProvider"
BK_APIGW_JWT_PROVIDER_CLS = "apigw_manager.apigw.providers.DummyEnvPayloadJWTProvider"
```

同时提供以下环境变量

```
APIGW_MANAGER_DUMMY_API_NAME # JWT 中的 API name
APIGW_MANAGER_DUMMY_PAYLOAD_APP_CODE # JWT payload 中的 app_code
APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
APIGW_MANAGER_DUMMY_GATEWAY_NAME # JWT 中的网关名
APIGW_MANAGER_DUMMY_PAYLOAD_APP_CODE # JWT payload 中的 app_code
APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
```

## FAQ
Expand All @@ -285,4 +285,4 @@ APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
2. 可设置环境变量 `DATABASE_URL`,指定外部数据库,同步后可通过执行以下 SQL 查询:
```sql
select value from apigw_manager_context where scope="public_key" and key="<BK_APIGW_NAME>";
```
```
10 changes: 5 additions & 5 deletions sdks/apigw-manager/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ ApiGatewayJWTGenericMiddleware
认证 JWT 信息,在 ``request`` 中注入 ``jwt`` 对象,有以下属性:


* ``api_name``\ :传入的网关名称;
* ``gateway_name``\ :传入的网关名称;

ApiGatewayJWTAppMiddleware
~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -317,15 +317,15 @@ UserModelBackend

.. code-block:: python
BK_APIGW_JWT_PROVIDER_CLS = "apigw-manager.apigw.providers.DummyEnvPayloadJWTProvider"
BK_APIGW_JWT_PROVIDER_CLS = "apigw_manager.apigw.providers.DummyEnvPayloadJWTProvider"
同时提供以下环境变量

.. code-block::
APIGW_MANAGER_DUMMY_API_NAME # JWT 中的 API name
APIGW_MANAGER_DUMMY_PAYLOAD_APP_CODE # JWT payload 中的 app_code
APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
APIGW_MANAGER_DUMMY_GATEWAY_NAME # JWT 中的网关名
APIGW_MANAGER_DUMMY_PAYLOAD_APP_CODE # JWT payload 中的 app_code
APIGW_MANAGER_DUMMY_PAYLOAD_USERNAME # JWT payload 中的 username
FAQ
---
Expand Down
2 changes: 1 addition & 1 deletion sdks/apigw-manager/demo/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def jwt_info(request):
if jwt:
data.update(
{
"api_name": request.jwt.api_name,
"gateway_name": request.jwt.gateway_name,
"payload": request.jwt.payload,
}
)
Expand Down
16 changes: 9 additions & 7 deletions sdks/apigw-manager/src/apigw_manager/apigw/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ def __init__(self, get_response):
)
self.provider = jwt_provider_cls(
jwt_key_name=self.JWT_KEY_NAME,
default_api_name=configuration.api_name,
default_gateway_name=configuration.gateway_name,
algorithm=getattr(settings, "APIGW_JWT_ALGORITHM", self.ALGORITHM),
allow_invalid_jwt_token=getattr(settings, "APIGW_ALLOW_INVALID_JWT_TOKEN", False),
public_key_provider=self.PUBLIC_KEY_PROVIDER_CLS(default_api_name=configuration.api_name),
public_key_provider=self.PUBLIC_KEY_PROVIDER_CLS(default_gateway_name=configuration.gateway_name),
)

def __call__(self, request):
Expand Down Expand Up @@ -111,12 +111,14 @@ class ApiGatewayJWTUserMiddleware:
def __init__(self, get_response):
self.get_response = get_response

def get_user(self, request, api_name=None, bk_username=None, verified=False, **credentials):
# 传递 api_name 参数的用途:
def get_user(self, request, gateway_name=None, bk_username=None, verified=False, **credentials):
# 传递 gateway_name 参数的用途:
# 1. 来明确标识这个请求来自于网关
# 2. 用户已经过认证,后端无需再认证
# 3. 避免非预期调用激活对应后端使得用户认证被绕过
return auth.authenticate(request, api_name=api_name, bk_username=bk_username, verified=verified, **credentials)
return auth.authenticate(
request, gateway_name=gateway_name, bk_username=bk_username, verified=verified, **credentials
)

def __call__(self, request):
jwt_info = getattr(request, "jwt", None)
Expand All @@ -130,7 +132,7 @@ def __call__(self, request):
jwt_user = (jwt_info.payload.get("user") or {}).copy()
jwt_user.setdefault("bk_username", jwt_user.pop("username", None))

request.user = self.get_user(request, api_name=jwt_info.api_name, **jwt_user)
request.user = self.get_user(request, gateway_name=jwt_info.gateway_name, **jwt_user)
return self.get_response(request)


Expand All @@ -152,7 +154,7 @@ def make_anonymous_user(self, bk_username=None):
user.username = bk_username # type: ignore
return user

def authenticate(self, request, api_name, bk_username, verified, **credentials):
def authenticate(self, request, gateway_name, bk_username, verified, **credentials):
if not verified:
return self.make_anonymous_user(bk_username=bk_username)
return self.user_maker(bk_username)
2 changes: 1 addition & 1 deletion sdks/apigw-manager/src/apigw_manager/apigw/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class ApiCommand(BaseCommand):
manager_class: typing.Callable

def add_arguments(self, parser):
parser.add_argument("--gateway-name", "--api-name", dest="api_name", help="gateway name")
parser.add_argument("--gateway-name", "--api-name", dest="gateway_name", help="gateway name")
parser.add_argument("--host", help="apigateway host with stage of admin api `bk-apigateway`")

def get_configuration(self, **kwargs):
Expand Down
64 changes: 32 additions & 32 deletions sdks/apigw-manager/src/apigw_manager/apigw/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,62 +106,62 @@ def set_value(self, key, value):

class BasePublicKeyManager(ABC):
@abstractmethod
def get(self, api_name, issuer=None):
def get(self, gateway_name, issuer=None):
return ""

@abstractmethod
def set(self, api_name, public_key, issuer=None):
def set(self, gateway_name, public_key, issuer=None):
return False

def get_best_matched(self, api_name, issuer=None):
public_key = self.get(api_name, issuer)
def get_best_matched(self, gateway_name, issuer=None):
public_key = self.get(gateway_name, issuer)
if public_key:
return public_key

if issuer:
logger.warning(
"please re-update %s public_key according to command fetch_apigw_public_key",
api_name,
gateway_name,
)

return self.get(api_name, issuer=None)
return self.get(gateway_name, issuer=None)

def current(self):
configuration = get_configuration()
return self.get(configuration.api_name)
return self.get(configuration.gateway_name)


class PublicKeyManager(ContextManager, BasePublicKeyManager):
scope = "public_key"

def get(self, api_name, issuer=None):
key = self._get_key(api_name, issuer)
def get(self, gateway_name, issuer=None):
key = self._get_key(gateway_name, issuer)
return self.get_value(key, None)

def set(self, api_name, public_key, issuer=None):
key = self._get_key(api_name, issuer)
def set(self, gateway_name, public_key, issuer=None):
key = self._get_key(gateway_name, issuer)
self.set_value(key, public_key)

def _get_key(self, api_name, issuer=None):
def _get_key(self, gateway_name, issuer=None):
if issuer:
return "%s:%s" % (issuer, api_name)
return api_name
return "%s:%s" % (issuer, gateway_name)
return gateway_name


class ReleaseVersionManager(ContextManager):
scope = "release_version"

def increase(self, api_name):
def increase(self, gateway_name):
current = 0

with atomic():
try:
current = int(self.get_value(api_name, "v0").strip("v"))
current = int(self.get_value(gateway_name, "v0").strip("v"))
except Exception:
pass

version = "v%s" % str(current + 1)
self.set_value(api_name, version)
self.set_value(gateway_name, version)

return version

Expand All @@ -173,34 +173,34 @@ class ResourceSignatureManager(ContextManager):
# 2. 其他明确需要发布的场景,比如同步接口时,发现有资源的增删
# 原则是,如果有涉及到需发布的变更,就要设置 dirty,发布后重置,尽可能避免漏发的情况

def get(self, api_name):
value = self.get_value(api_name)
def get(self, gateway_name):
value = self.get_value(gateway_name)
if not value:
return {}

return json.loads(value)

def set(self, api_name, is_dirty, signature):
self.set_value(api_name, json.dumps({"is_dirty": is_dirty, "signature": signature}))
def set(self, gateway_name, is_dirty, signature):
self.set_value(gateway_name, json.dumps({"is_dirty": is_dirty, "signature": signature}))

def get_signature(self, api_name):
saved = self.get(api_name)
def get_signature(self, gateway_name):
saved = self.get(gateway_name)
return saved.get("signature", "")

def is_dirty(self, api_name, default=False):
saved = self.get(api_name)
def is_dirty(self, gateway_name, default=False):
saved = self.get(gateway_name)
return saved.get("is_dirty", default)

def mark_dirty(self, api_name):
self.set(api_name, True, self.get_signature(api_name))
def mark_dirty(self, gateway_name):
self.set(gateway_name, True, self.get_signature(gateway_name))

def reset_dirty(self, api_name):
self.set(api_name, False, self.get_signature(api_name))
def reset_dirty(self, gateway_name):
self.set(gateway_name, False, self.get_signature(gateway_name))

def update_signature(self, api_name, signature):
saved = self.get(api_name)
def update_signature(self, gateway_name, signature):
saved = self.get(gateway_name)
last_signature = saved.get("signature")
self.set(api_name, saved.get("is_dirty") or last_signature != signature, signature)
self.set(gateway_name, saved.get("is_dirty") or last_signature != signature, signature)


def make_default_public_key_manager() -> BasePublicKeyManager:
Expand Down
13 changes: 7 additions & 6 deletions sdks/apigw-manager/src/apigw_manager/apigw/k8s_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def __init__(self):
config.load_incluster_config(configuration)
self.client = CoreV1Api(api_client=api_client.ApiClient(configuration=configuration))

def get(self, api_name, issuer=None):
def get(self, gateway_name, issuer=None):
try:
secret = self.client.read_namespaced_secret(self._get_name(issuer), self.namespace)
except exceptions.ApiException as err:
Expand All @@ -27,27 +27,28 @@ def get(self, api_name, issuer=None):

public_keys = getattr(secret, "data", None) or {}

if api_name not in public_keys:
if gateway_name not in public_keys:
return None

value = public_keys[api_name]
value = public_keys[gateway_name]

return base64.b64decode(value).decode()

def set(self, api_name, public_key, issuer=None):
def set(self, gateway_name, public_key, issuer=None):
name = self._get_name(issuer)
secret = V1Secret(
metadata=V1ObjectMeta(
name=name,
namespace=self.namespace,
annotations={
"bkgateway/issuer": issuer or "",
"bkgateway/api_name": api_name,
"bkgateway/gateway_name": gateway_name,
"bkgateway/api_name": gateway_name,
},
),
kind="Secret",
type="Opaque",
string_data={api_name: public_key},
string_data={gateway_name: public_key},
)

try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ def do(self, manager, definition, *args, **kwargs):
print("warning!! Add related apps error, %s" % str(err))
return

print("Add related apps for gateway %s: %s" % (manager.config.api_name, ", ".join(definition)))
print("Add related apps for gateway %s: %s" % (manager.config.gateway_name, ", ".join(definition)))
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,15 @@ def do(self, manager, definition, *args, **kwargs):
if permission.get("gateway_name"):
permission["api_name"] = permission.get("gateway_name")

grant_dimension = permission.pop("grant_dimension", "api")
if grant_dimension == "gateway":
grant_dimension = "api"
if permission.get("grant_dimension") in [None, "gateway"]:
permission["grant_dimension"] = "api"

result = manager.apply_permission(grant_dimension=grant_dimension, **permission)
result = manager.apply_permission(**permission)
print(
"Applied permissions for gateway %s, record %s, dimension %s"
% (
permission["api_name"],
result["record_id"],
grant_dimension,
permission["grant_dimension"],
)
)
Loading

0 comments on commit 2de55aa

Please sign in to comment.