Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
本项目基于 requests,提供了一种基于配置构建 SDK 的方案。
该方式下,SDK 会根据配置自动做请求和响应的转换和解析,隐藏文档已描述的通用细节(如参数位置,响应格式等),用户仅需关注具体字段传递,以减轻使用成本。
from demo.client import Client
# 需提供网关地址 endpoint,环境 stage 默认为 prod
client = Client(stage="prod", endpoint="http://bkapi.example.com/api/test/")
# 根据需求,提供应用认证、用户认证信息
client.update_bkapi_authorization(
bk_app_code="xxx",
bk_app_secret="yyy",
bk_username="admin",
)
# 发起请求,检查 response.raise_for_status(),返回 response.json() 数据
result = client.api.test({"key": "value"})
print(result["ok"])
该方式下,SDK 不会有过多的封装,仅做参数传递,调用 requests 进行请求后返回响应,用户对请求流程有更细粒度的控制。
from demo.client import Client
# 需提供网关地址 endpoint,环境 stage 默认为 prod
client = Client(stage="prod", endpoint="http://bkapi.example.com/api/test/")
# 根据需求,提供应用认证、用户认证信息
client.update_bkapi_authorization(
bk_app_code="xxx",
bk_app_secret="yyy",
bk_username="admin",
)
# 发起请求,但不对响应进行解析,直接返回 requests Response 对象,用户可对响应进行更细粒度的控制
response = client.api.test.request({"key": "value"})
result = response.json()
print(result["ok"])
为降低使用成本,SDK 提供了更简单地创建 Client 的方式:get_client_by_request、get_client_by_username
此方式需提供 django request 对象
get_client_by_request
from demo.shortcuts import get_client_by_request
# 需提供 django request,client 可从 request 中获取当前用户名或从 Cookies 中获取用户登录态
# 并且,支持从 django settings 获取默认的 bk_app_code、bk_app_secret、endpoint,也可通过参数指定
client = get_client_by_request(request)
result = client.api.test({"key": "value"})
print(result["ok])
get_client_by_username
from demo.shortcuts import get_client_by_username
# 支持从 django settings 获取默认的 bk_app_code、bk_app_secret、endpoint,也可通过参数指定
client = get_client_by_username("admin")
result = client.api.test({"key": "value"})
print(result["ok])
支持复用 session 连接,提高请求效率
from demo.shortcuts import get_client_by_username
client = get_client_by_username("admin")
with client:
result = client.api.test({"key": "value"})
print(result["ok])
SDK 支持通过配置更改一些默认的行为,Django settings 配置优先级高于环境变量。
变量名 | 描述 | 类型 | 格式 | 默认值 | Django 配置 | 环境变量 | 别名 |
---|---|---|---|---|---|---|---|
BK_APP_CODE | 应用代号 | string | "my_app" | 支持 | 支持 | BKPAAS_APP_ID | |
BK_APP_SECRET | 应用密钥 | string | "my_secret" | 支持 | 支持 | BKPAAS_APP_SECRET | |
DEFAULT_STAGE_MAPPINGS | 指定对应网关的默认环境 | dict | {"my_gateway": "prod"} | 支持 | |||
BK_API_CLIENT_ENABLE_SSL_VERIFY | 是否开启 SSL 证书验证 | bool | True | False | 支持 | ||
BK_API_AUTHORIZATION_COOKIES_MAPPING | 指定 Cookie 和认证参数的映射关系 | dict | {"key": "cookie"} | {"bk_token": "bk_token"} | 支持 | ||
BK_API_URL_TMPL | 网关地址模板,支持 {api_name} 占位符 | string | "http://{api_name}.example.com" | 支持 | 支持 | ||
BK_COMPONENT_API_URL | 组件 API 网关地址 | string | "http://esb.example.com" | 支持 | 支持 | ||
DEFAULT_BK_API_VER | 默认组件版本号 | string | "v1" | "v2" | 支持 | 支持 | |
BK_API_USE_TEST_ENV | 是否使用组件测试环境 | bool | False | False | 支持 |
主要模型定义的类关系请参考 class_diagram.md
在 requests.Session
的基础之上,增加特有业务逻辑封装:
同时扩展原有 hooks 机制,为后续进阶需求提供扩展。 session 管理状态及完成每一个接口调用。
借用 openapi 概念,表示一个路径和方法绑定的操作,相当于网关中的资源及 ESB 中的组件(对应的某个方法),作为配置及调用入口,封装以下属性:
Operation 不发起具体的请求,对应的请求会在调用时交给 Manager 完成。
管理 Session 和 Operation,作为整体的“门面”,可以扩展实现为一个 Client。 其本身不保存状态,而是提供封装,将状态转换成 Session 的基本属性。 其本身也不完成接口调用,而是管理调用流程,处理好请求创建,响应解析和异常处理等步骤。
Operation 的分组,在 ESB 中,可以用系统名作为分组,而在网关中,即使不区分系统,但为了避免资源和 Client 自身属性的冲突,也使用默认分组管理。
Manager 的子类,管理 OperationGroups 以及其定义的 Operations,针对具体的业务场景中,提供更符合业务流程的封装。
在现有的网关文档中,有较大的一类文档基于程序自动生成,其中虽有描述请求方法,认证方式,调用示例,但因为基于模板生成的原因,其内容较为死板和繁琐。如果由人去机械化地拼接接口的每个配置细节,因为这种方式是反人性的,会有较大的出错概率,由此会带来额外的不必要的咨询和反馈。因此希望 SDK 能够自动帮用户去处理这些机械化的配置,只需要关注核心的调用参数即可完成任务。此外,SDK 本身又可看做某个版本的离线文档,通过 SDK 合理的智能提示和补全,也可以免除用户的查找和排除成本。
在接口的设计上,有以下目标:
__init__
方法;因此接口定义使用类属性 + property 的方式进行:
class Group(OperationGroup):
test = bind_property(Operation, name="test", method="GET", path="/test/")
如上示例,声明了一个 API 分组 Group
。test
是该分组下注册在网关的一个资源名称,其方法为 GET
,资源路径为 /test/
。
class Client(APIGatewayClient):
api = bind_property(Group)
上方示例基于 Group
这个 api 分组定义了一个网关客户端,使用 Client
可直接调用这个网关下的所有资源接口。
在接口定义中,bind_property
方法实现了懒加载属性的功能,在调用时自动初始化对应类型,同时基于类型注解实现了泛型,可以帮助 IDE 建立类型系统。
得益于 bind_property
实现的泛型,IDE 可以完成 Client -> Group -> Operation
整个调用链的智能补全。而 Operation
自身的补全方式,完全基于 Operation
定义,仅需维护本项目即可。
为了优化 Python3 的调用逻辑,同时兼容 Python2 的使用,SDK 生成定义的同时,也会生成对应的存根文件(.pyi),将接口文档作为 docstring,在 IDE 中引用即可看到文档:
class Group(OperationGroup):
@property
def test(self) -> Operation:
"""test api"""
class Client(APIGatewayClient):
@property
def api(self) -> Group:
"""api resources"""
存根文件在运行时不会被执行,因此使用等价的 property
描述资源定义,通过 docstring 暴露文档(也是唯一方式)。
启用前需安装额外依赖:
pip install bkapi_client_core[monitor]
在项目入口处,执行以下代码启用 prometheus 指标统计的功能:
from bkapi_client_core import prometheus
prometheus.enable()
以上代码只需要执行一次,开启后,sdk 会对每次请求进行指标的统计,暴露到 /metrics 接口,指标如下:
名称 | 类型 | 描述 | 维度 |
---|---|---|---|
bkapi_requests_duration_seconds | Histogram | 请求耗时 | operation,method |
bkapi_requests_body_bytes | Histogram | 请求体大小 | operation,method |
bkapi_responses_body_bytes | Histogram | 响应体大小 | operation,method |
bkapi_responses_total | Counter | 响应总数 | operation,method,status |
bkapi_failures_total | Counter | 请求失败总数 | operation,method,error |
FAQs
A toolkit for buiding blueking API clients.
We found that bkapi-client-core demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.