Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 214 additions & 0 deletions apollo-openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ tags:
description: 权限管理相关接口,包括权限查询等功能
- name: User Management
description: 用户管理相关接口,支持Portal用户登录态和具备用户管理权限的Consumer Token调用
- name: User Token Management
description: 用户访问Token相关接口,包括当前Token身份、权限范围和可调用OpenAPI能力查询
- name: Portal Management
description: Portal UI 登录态管理接口,主要供当前版本 Portal 前端调用
paths:
Expand Down Expand Up @@ -4889,6 +4891,69 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExceptionResponse'
/openapi/v1/user-tokens/current:
get:
summary: 获取当前用户访问Token身份和权限范围(new added)
operationId: getCurrentUserToken
deprecated: false
description: GET /openapi/v1/user-tokens/current,仅支持用户访问Token调用,返回当前Token身份、权限范围和可调用OpenAPI能力
tags:
- User Token Management
responses:
'200':
description: 成功获取当前用户访问Token信息
content:
application/json:
schema:
$ref: '#/components/schemas/OpenUserTokenCurrentCapability'
'403':
description: 仅支持用户访问Token访问
content:
application/json:
schema:
$ref: '#/components/schemas/ExceptionResponse'
/openapi/v1/user-tokens/current/capabilities:
get:
summary: 获取当前用户访问Token能力(new added)
operationId: getCurrentUserTokenCapabilities
deprecated: false
description: GET /openapi/v1/user-tokens/current/capabilities,仅支持用户访问Token调用,返回当前Token身份、权限范围和可调用OpenAPI能力
tags:
- User Token Management
responses:
'200':
description: 成功获取当前用户访问Token能力
content:
application/json:
schema:
$ref: '#/components/schemas/OpenUserTokenCurrentCapability'
'403':
description: 仅支持用户访问Token访问
content:
application/json:
schema:
$ref: '#/components/schemas/ExceptionResponse'
/openapi/v1/user-tokens/whoami:
get:
summary: 获取当前用户访问Token身份别名(new added)
operationId: getCurrentUserTokenWhoami
deprecated: false
description: GET /openapi/v1/user-tokens/whoami,/current 的别名,仅支持用户访问Token调用
tags:
- User Token Management
responses:
'200':
description: 成功获取当前用户访问Token信息
content:
application/json:
schema:
$ref: '#/components/schemas/OpenUserTokenCurrentCapability'
'403':
description: 仅支持用户访问Token访问
content:
application/json:
schema:
$ref: '#/components/schemas/ExceptionResponse'
/openapi/v1/users:
get:
summary: 搜索用户(new added)
Expand Down Expand Up @@ -6863,6 +6928,155 @@ components:
enabled:
type: integer
description: 是否启用,1 表示启用,0 表示禁用
OpenUserTokenCurrentCapability:
type: object
description: 当前用户访问Token身份、权限范围和可调用OpenAPI能力
required:
- authType
- userId
- tokenId
- tokenName
- tokenPrefix
- rateLimit
- expires
- dataChangeCreatedTime
- denyAll
- allOperations
- operations
- allApps
- appIds
- allEnvs
- envs
- allNamespaces
- namespaces
- actions
properties:
authType:
type: string
description: 当前认证类型,用户访问Token固定为 USER_TOKEN
example: USER_TOKEN
userId:
type: string
description: Token所属Portal用户ID
tokenId:
type: integer
format: int64
description: 用户访问Token ID
tokenName:
type: string
description: 用户访问Token名称
tokenPrefix:
type: string
description: 用户访问Token前缀,用于识别Token但不暴露完整密钥
rateLimit:
type: integer
description: 每秒限流阈值,0或空表示不限流
expires:
type: string
format: date-time
description: Token过期时间
lastUsedTime:
type: string
format: date-time
description: Token最近使用时间
dataChangeCreatedTime:
type: string
format: date-time
description: Token创建时间
denyAll:
type: boolean
description: 是否拒绝所有OpenAPI访问
allOperations:
type: boolean
description: 是否允许当前用户拥有的全部操作
operations:
type: array
uniqueItems: true
description: 显式授权的操作列表,空列表配合 allOperations 表示全部操作
items:
type: string
allApps:
type: boolean
description: 是否允许当前用户可访问的全部应用
appIds:
type: array
uniqueItems: true
description: 显式授权的应用ID列表,空列表配合 allApps 表示全部应用
items:
type: string
allEnvs:
type: boolean
description: 是否允许全部环境
envs:
type: array
uniqueItems: true
description: 显式授权的环境列表,空列表配合 allEnvs 表示全部环境
items:
type: string
allNamespaces:
type: boolean
description: 是否允许全部命名空间
namespaces:
type: array
description: 显式授权的命名空间范围列表,空列表配合 allNamespaces 表示全部命名空间
items:
$ref: '#/components/schemas/OpenUserTokenNamespaceScope'
actions:
type: array
description: 当前Token可调用的OpenAPI操作目录
items:
$ref: '#/components/schemas/OpenUserTokenOpenApiAction'
OpenUserTokenNamespaceScope:
type: object
description: 用户访问Token的命名空间授权范围
properties:
appId:
type: string
description: 应用ID,空值或星号表示不限应用
env:
type: string
description: 环境,空值或星号表示不限环境
clusterName:
type: string
description: 集群名称,空值或星号表示不限集群
namespaceName:
type: string
description: 命名空间名称,空值或星号表示不限命名空间
OpenUserTokenOpenApiAction:
type: object
description: 用户访问Token可调用的OpenAPI操作说明
properties:
id:
type: string
description: OpenAPI操作标识
method:
type: string
description: HTTP方法
example: GET
path:
type: string
description: OpenAPI路径
requiredOperations:
type: array
description: 调用该OpenAPI操作需要满足的用户Token操作权限列表
items:
type: string
grantedOperations:
type: array
description: 当前Token已满足的操作权限列表
items:
type: string
operationMatch:
type: string
description: requiredOperations 的匹配方式,ANY 表示满足任一操作即可,NONE 表示无需操作权限
example: ANY
resourceScope:
type: string
description: 该OpenAPI操作涉及的资源范围类型
example: namespace
description:
type: string
description: OpenAPI操作说明
OpenEnvNamespaceRoleUserDTO:
type: object
properties:
Expand Down
90 changes: 90 additions & 0 deletions tests/test_user_token_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import unittest
from pathlib import Path

import yaml


SPEC_FILES = (
"apollo-openapi.yaml",
)


class UserTokenContractTest(unittest.TestCase):

def setUp(self):
self.repo_root = Path(__file__).resolve().parents[1]

def _load_spec(self, spec_file):
return yaml.safe_load((self.repo_root / spec_file).read_text(encoding="utf-8"))

def test_user_token_management_paths_use_generated_contract(self):
for spec_file in SPEC_FILES:
spec = self._load_spec(spec_file)

with self.subTest(spec=spec_file):
tag_names = {tag["name"] for tag in spec["tags"]}
self.assertIn("User Token Management", tag_names)

expected_operations = {
"/openapi/v1/user-tokens/current": "getCurrentUserToken",
"/openapi/v1/user-tokens/current/capabilities":
"getCurrentUserTokenCapabilities",
"/openapi/v1/user-tokens/whoami": "getCurrentUserTokenWhoami",
}
for path, operation_id in expected_operations.items():
operation = spec["paths"][path]["get"]
self.assertEqual(operation_id, operation["operationId"])
self.assertEqual(["User Token Management"], operation["tags"])
self.assertEqual(
"#/components/schemas/OpenUserTokenCurrentCapability",
operation["responses"]["200"]["content"]["application/json"]["schema"]["$ref"],
)

def test_user_token_current_capability_schema_matches_portal_response(self):
for spec_file in SPEC_FILES:
spec = self._load_spec(spec_file)
schemas = spec["components"]["schemas"]

with self.subTest(spec=spec_file):
self.assertEqual(
[
"authType",
"userId",
"tokenId",
"tokenName",
"tokenPrefix",
"rateLimit",
"expires",
"dataChangeCreatedTime",
"denyAll",
"allOperations",
"operations",
"allApps",
"appIds",
"allEnvs",
"envs",
"allNamespaces",
"namespaces",
"actions",
],
schemas["OpenUserTokenCurrentCapability"]["required"],
)
capability = schemas["OpenUserTokenCurrentCapability"]["properties"]
self.assertEqual("integer", capability["tokenId"]["type"])
self.assertEqual("int64", capability["tokenId"]["format"])
self.assertEqual("date-time", capability["expires"]["format"])
self.assertTrue(capability["operations"]["uniqueItems"])
self.assertTrue(capability["appIds"]["uniqueItems"])
self.assertTrue(capability["envs"]["uniqueItems"])
self.assertEqual(
"#/components/schemas/OpenUserTokenNamespaceScope",
capability["namespaces"]["items"]["$ref"],
)
self.assertEqual(
"#/components/schemas/OpenUserTokenOpenApiAction",
capability["actions"]["items"]["$ref"],
)


if __name__ == "__main__":
unittest.main()
Loading