Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change to gateway name #137

Merged
merged 11 commits into from
Dec 1, 2023
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
13 changes: 13 additions & 0 deletions sdks/apigw-manager/CHANGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,19 @@
- 基础镜像 apigw-manager 中,sync-apigateway.sh 中去除指令 apply_apigw_permissions
- 优化请求 bk-apigateway 接口失败时,打印的错误消息
- 优化 README.md,提供 examples
alex-smile marked this conversation as resolved.
Show resolved Hide resolved
- 以下函数中的参数名 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__

### 2.0.1
- 修复镜像 sync-apigateway 中,同步任务失败时,脚本退出码为 0 的问题
Expand Down
10 changes: 5 additions & 5 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 @@ -273,9 +273,9 @@ BK_APIGW_JWT_PROVIDER_CLS = "apigw-manager.apigw.providers.DummyEnvPayloadJWTPro
同时提供以下环境变量

```
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>";
```
```
8 changes: 4 additions & 4 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 @@ -323,9 +323,9 @@ UserModelBackend

.. 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},
alex-smile marked this conversation as resolved.
Show resolved Hide resolved
)

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"],
)
)
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ def _fix_defined_version(self, defined_version):

return defined_version

def _should_create_resource_version(self, manager, api_name, defined_version, latest_version):
def _should_create_resource_version(self, manager, gateway_name, defined_version, latest_version):
# 版本一致,且没有变更
if latest_version and defined_version.public == latest_version.public and not manager.is_dirty(api_name):
if latest_version and defined_version.public == latest_version.public and not manager.is_dirty(gateway_name):
return False
return True

Expand Down Expand Up @@ -101,21 +101,21 @@ def handle(self, stage, title, comment, generate_sdks, *args, **kwargs):

releaser = self.Releaser(configuration)
manager = self.ResourceSignatureManager()
api_name = configuration.api_name
gateway_name = configuration.gateway_name

# 如何判断是否需要创建新版本?
# 1. 使用配置中的版本号与线上最新版本进行比较,如果版本 public 部分不一致,则需要创建新版本
# 2. 如果配置中的版本号与线上最新版本 public 部分一致,为了避免开发者忘记更新版本号的情况,获取同步结果进行判断:
# - 如果有资源变更,则需要创建新版本
if self._should_create_resource_version(manager, api_name, fixed_defined_version, latest_version):
if self._should_create_resource_version(manager, gateway_name, fixed_defined_version, latest_version):
exists = self._check_resource_version_exists(fetcher, fixed_defined_version)
resource_version = self._create_resource_version(
releaser=releaser,
version=self._get_version_to_be_created(fixed_defined_version, exists),
title=title or definition.get("title", ""),
comment=comment or definition.get("comment", ""),
)
manager.reset_dirty(api_name)
manager.reset_dirty(gateway_name)

else:
generate_sdks = False
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ def do(self, manager, configuration, print_, no_save, *args, **kwargs):
return

public_key_manager = helper.make_default_public_key_manager()
public_key_manager.set(configuration.api_name, result["public_key"], result.get("issuer"))
public_key_manager.set(configuration.gateway_name, result["public_key"], result.get("issuer"))
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@
class Command(BaseCommand):
"""Get the esb public key and store it into the database"""

def handle(self, api_name, *args, **kwargs):
for gateway_name in ["bk-esb", "apigw"]:
super(Command, self).handle(gateway_name, *args, **kwargs)
def handle(self, gateway_name, *args, **kwargs):
for _gateway_name in ["bk-esb", "apigw"]:
super(Command, self).handle(_gateway_name, *args, **kwargs)
Loading
Loading