agent-exec transport 143b750c2cb52ec907a1a45df1416122/1ca104a38359
ci / svc-realcombo (pull_request) Successful in 3m55s
ci / test (pull_request) Failing after 7m46s
ci / test-pg (pull_request) Successful in 15m11s

This commit is contained in:
agent-exec-transport
2026-10-20 14:05:34 +00:00
parent 431de54886
commit fc3655f296
@@ -485,7 +485,7 @@ func (e *env) loginAlice(t *testing.T) *userClient {
}, nil)
apiKey := e.cliOut(t, "apikey", "create", "--dsn", e.dbDSN, "--owner", aliceOwner, "--name", "mi-acceptance")
// IAM U4 把建项目改为系统动作 project.create:由引导管理员经真实 API 授予 alice。
// 建项目需角色上的 project.create:由引导管理员经真实 API 建角色并绑定 alice。
e.grantAliceProjectCreate(t)
uc := &userClient{
api: &http.Client{Timeout: httpWait, Transport: bearerRT{key: apiKey}},
@@ -498,44 +498,53 @@ func (e *env) loginAlice(t *testing.T) *userClient {
return uc
}
// grantAliceProjectCreate 以 mi-root 身份经 admin API 为 alice 追加系统授权
// project.create(保留其已有授权;PUT 为整体替换,按 authorityEpoch 乐观并发)。
// mi-root 由 serve 启动时的 SUPER_ADMIN_* 建立,可直接为其发 API key。
// grantAliceProjectCreate 以 mi-root 身份经 admin API 为 alice 授予项目访问:
// project.create 只由已启用账号所绑定的已启用角色上的单一布尔决定,管理员
// 身份或系统授权都不再隐含它。故新建角色、写入 project.create
// (allowed=capAllowed=true,按 roleVersion 乐观并发),再按 alice 的
// stateVersion 绑定该角色。mi-root 由 serve 启动时的 SUPER_ADMIN_* 建立,
// 可直接为其发 API key。
func (e *env) grantAliceProjectCreate(t *testing.T) {
t.Helper()
rootKey := e.cliOut(t, "apikey", "create", "--dsn", e.dbDSN, "--owner", "mi-root", "--name", "mi-acceptance-root")
root := &http.Client{Timeout: httpWait, Transport: bearerRT{key: rootKey}}
var users []struct {
UserID string `json:"userId"`
Owner string `json:"owner"`
UserID string `json:"userId"`
Owner string `json:"owner"`
StateVersion int64 `json:"stateVersion"`
}
e.do(t, root, "GET", "/api/admin/users", nil, &users)
userID := ""
userID, stateVersion := "", int64(0)
for _, u := range users {
if u.Owner == aliceOwner {
userID = u.UserID
userID, stateVersion = u.UserID, u.StateVersion
}
}
if userID == "" {
t.Fatalf("user %s not found in /api/admin/users", aliceOwner)
}
var grants struct {
Actions []string `json:"actions"`
AuthorityEpoch int64 `json:"authorityEpoch"`
var role struct {
RoleID string `json:"roleId"`
RoleVersion int64 `json:"roleVersion"`
}
e.do(t, root, "GET", "/api/admin/users/"+userID+"/system-grants", nil, &grants)
actions := grants.Actions
has := false
for _, a := range actions {
has = has || a == "project.create"
e.do(t, root, "POST", "/api/admin/roles", map[string]any{
"name": "mi-project-access",
"description": "multi-instance acceptance: project.create",
}, &role)
if role.RoleID == "" {
t.Fatal("POST /api/admin/roles returned no roleId")
}
if !has {
actions = append(actions, "project.create")
}
e.do(t, root, "PUT", "/api/admin/users/"+userID+"/system-grants", map[string]any{
"expectedAuthorityEpoch": grants.AuthorityEpoch,
"idempotencyKey": "mi-alice-project-create",
"actions": actions,
e.do(t, root, "PUT", "/api/admin/roles/"+role.RoleID+"/permissions", map[string]any{
"expectedRoleVersion": role.RoleVersion,
"idempotencyKey": "mi-role-project-create",
"changes": []map[string]any{
{"actionId": "project.create", "allowed": true, "capAllowed": true},
},
}, nil)
e.do(t, root, "PUT", "/api/admin/users/"+userID+"/role", map[string]any{
"roleId": role.RoleID,
"expectedStateVersion": stateVersion,
"idempotencyKey": "mi-alice-project-access-role",
}, nil)
}