template-monorepo/test/e2e/journey_rbac_test.go

148 lines
5.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//go:build e2e
package e2e
import (
"encoding/json"
"fmt"
"net/http"
"testing"
"github.com/stretchr/testify/require"
)
// TestJourney_TenantAdminCustomRole 模擬 Tenant Admin 為一個新工種「qa_engineer」
// 從零建出可用角色的流程:
// 建 Role → 從 catalog 取 perm id → 全量 PUT 權限 → 設 IdP Mapping → 指派給 user
// → 用該 user 視角看 /permissions/me 確認權限生效 → 撤銷 → 刪 mapping → 刪 role
//
// 整段都用 seed owner tokenno-role user 用來驗證「被指派後拿到權限」。
func TestJourney_TenantAdminCustomRole(t *testing.T) {
j := NewJourney(t, "J-2", "Tenant Admin 從零建立 qa_engineer 角色 → 指派 → 驗證 → 撤銷")
defer j.Summary()
owner := NewClient(t)
noRole := NewNoRoleClient(t)
var (
roleKey = "journey_qa_engineer"
externalKey = fmt.Sprintf("journey-qa-group-%s", noRole.Fixture.UID)
roleID string
permissionID string
)
// 清掉殘留(前一輪可能 fail 沒走到清理 step
t.Cleanup(func() {
_, _ = owner.Do(t, http.MethodDelete, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
}, true)
if roleID != "" {
_, _ = owner.Do(t, http.MethodDelete, "/api/v1/permissions/users/"+noRole.Fixture.UID+"/roles/"+roleID, nil, true)
_, _ = owner.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+roleID, nil, true)
}
})
j.Step("1", "POST /permissions/roles — 建立 qa_engineer 角色", func(t *testing.T) {
env := owner.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": roleKey,
"display_name": "QA Engineer",
"status": "open",
}, true)
var role struct {
ID string `json:"id"`
Key string `json:"key"`
}
require.NoError(t, json.Unmarshal(env.Data, &role))
require.Equal(t, roleKey, role.Key)
require.NotEmpty(t, role.ID)
roleID = role.ID
})
j.Step("2", "GET /permissions/catalog — 從平台 catalog 撈第一個 leaf permission id", func(t *testing.T) {
permissionID = firstCatalogPermissionID(t, owner)
require.NotEmpty(t, permissionID)
})
j.Step("3", "PUT /permissions/roles/:id/permissions — 把選到的 permission 灌進 role", func(t *testing.T) {
require.NotEmpty(t, roleID)
require.NotEmpty(t, permissionID)
owner.DoExpectOK(t, http.MethodPut, "/api/v1/permissions/roles/"+roleID+"/permissions", map[string][]string{
"permission_ids": {permissionID},
}, true)
// 讀回比對parent closure 會把祖先一起加進去,所以集合 >= 1 + 必含 permissionID
env := owner.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/roles/"+roleID+"/permissions", nil, true)
var list struct {
Permissions []struct {
ID string `json:"id"`
} `json:"permissions"`
}
require.NoError(t, json.Unmarshal(env.Data, &list))
found := false
for _, p := range list.Permissions {
if p.ID == permissionID {
found = true
break
}
}
require.True(t, found)
})
j.Step("4", "PUT /permissions/role-mappings — 設定 zitadel:qa-group → qa_engineer 對映", func(t *testing.T) {
env := owner.DoExpectOK(t, http.MethodPut, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
"internal_role_key": roleKey,
}, true)
var mapping struct {
ExternalKey string `json:"external_key"`
InternalRoleKey string `json:"internal_role_key"`
}
require.NoError(t, json.Unmarshal(env.Data, &mapping))
require.Equal(t, externalKey, mapping.ExternalKey)
require.Equal(t, roleKey, mapping.InternalRoleKey)
})
j.Step("5", "POST /permissions/users/:uid/roles — 把 qa_engineer 手動指派給 no-role user", func(t *testing.T) {
require.NotEmpty(t, roleID)
owner.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/users/"+noRole.Fixture.UID+"/roles", map[string]string{
"role_id": roleID,
}, true)
})
j.Step("6", "GET /permissions/me (no-role user 視角) — 確認新角色 + 權限都拿到了", func(t *testing.T) {
env := noRole.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/me?include_tree=false", nil, true)
var data struct {
Roles []string `json:"roles"`
Permissions map[string]string `json:"permissions"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.Contains(t, data.Roles, roleKey, "no-role user should now have qa_engineer")
require.NotEmpty(t, data.Permissions, "expected non-empty permissions after role assignment")
})
j.Step("7", "DELETE /permissions/users/:uid/roles/:id — 撤銷指派", func(t *testing.T) {
require.NotEmpty(t, roleID)
owner.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/users/"+noRole.Fixture.UID+"/roles/"+roleID, nil, true)
env := noRole.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/me?include_tree=false", nil, true)
var data struct {
Roles []string `json:"roles"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.NotContains(t, data.Roles, roleKey)
})
j.Step("8", "DELETE /permissions/role-mappings + /roles — 收尾刪 mapping + role", func(t *testing.T) {
owner.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
}, true)
require.NotEmpty(t, roleID)
owner.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+roleID, nil, true)
// 標記已清t.Cleanup 那邊就不會再重複打
roleID = ""
})
}