template-monorepo/test/e2e/permission_test.go

271 lines
9.2 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"
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestPermission_Catalog(t *testing.T) {
e2eStep(t, "P-01", "GET", "/api/v1/permissions/catalog", "讀全平台 Permission Catalog 樹狀結構")
c := NewClient(t)
env := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/catalog?tree=true", nil, true)
var data struct {
Tree []map[string]any `json:"tree"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.NotEmpty(t, data.Tree)
}
func TestPermission_Me(t *testing.T) {
e2eStep(t, "P-02", "GET", "/api/v1/permissions/me", "讀當前 user 的角色 + 權限樹")
c := NewClient(t)
env := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/me?include_tree=true", nil, true)
var data struct {
UID string `json:"uid"`
TenantID string `json:"tenant_id"`
Roles []string `json:"roles"`
Permissions map[string]string `json:"permissions"`
Tree []map[string]any `json:"tree"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.Equal(t, c.Fixture.UID, data.UID)
require.Equal(t, c.Fixture.TenantID, data.TenantID)
require.Contains(t, data.Roles, c.Fixture.RoleKey)
require.NotEmpty(t, data.Permissions)
}
func TestPermission_RoleCRUD(t *testing.T) {
e2eStep(t, "P-03~P-06", "*", "/api/v1/permissions/roles", "租戶角色 CRUD建立 → 列表 → 更新 display_name → 刪除")
c := NewClient(t)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": "e2e_custom_role",
"display_name": "E2E Custom",
"status": "open",
}, true)
var role struct {
ID string `json:"id"`
Key string `json:"key"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
require.Equal(t, "e2e_custom_role", role.Key)
require.NotEmpty(t, role.ID)
// 避免 e2e-up 反覆跑時 role 殘留 → 後續 Create 撞 unique key
t.Cleanup(func() { _, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true) })
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/roles", nil, true)
var list struct {
Roles []struct {
Key string `json:"key"`
} `json:"roles"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
found := false
for _, r := range list.Roles {
if r.Key == "e2e_custom_role" {
found = true
break
}
}
require.True(t, found, "created role should appear in list")
patchEnv := c.DoExpectOK(t, http.MethodPatch, "/api/v1/permissions/roles/"+role.ID, map[string]string{
"display_name": "E2E Custom Renamed",
}, true)
var patched struct {
DisplayName string `json:"display_name"`
}
require.NoError(t, json.Unmarshal(patchEnv.Data, &patched))
require.Equal(t, "E2E Custom Renamed", patched.DisplayName)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_RolePermissions(t *testing.T) {
e2eStep(t, "P-07/P-08", "PUT/GET", "/api/v1/permissions/roles/:id/permissions", "Role 全量替換 Permission含 parent closure再讀回比對")
c := NewClient(t)
permissionID := firstCatalogPermissionID(t, c)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": "e2e_role_permissions",
"display_name": "E2E Role Permissions",
}, true)
var role struct {
ID string `json:"id"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
require.NotEmpty(t, role.ID)
t.Cleanup(func() { _, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true) })
c.DoExpectOK(t, http.MethodPut, "/api/v1/permissions/roles/"+role.ID+"/permissions", map[string][]string{
"permission_ids": {permissionID},
}, true)
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/roles/"+role.ID+"/permissions", nil, true)
var list struct {
Permissions []struct {
ID string `json:"id"`
} `json:"permissions"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
require.NotEmpty(t, list.Permissions)
found := false
for _, p := range list.Permissions {
if p.ID == permissionID {
found = true
break
}
}
require.True(t, found, "role should include requested permission")
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_AssignUserRole(t *testing.T) {
e2eStep(t, "P-09~P-11", "*", "/api/v1/permissions/users/:uid/roles", "User ↔ Role 指派 / 列表 / 撤銷")
c := NewClient(t)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": "e2e_assign_role",
"display_name": "E2E Assign",
}, true)
var role struct {
ID string `json:"id"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
t.Cleanup(func() {
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles/"+role.ID, nil, true)
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
})
c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles", map[string]string{
"role_id": role.ID,
}, true)
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles", nil, true)
var list struct {
UserRoles []struct {
RoleID string `json:"role_id"`
} `json:"user_roles"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
found := false
for _, r := range list.UserRoles {
if r.RoleID == role.ID {
found = true
break
}
}
require.True(t, found)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/users/"+c.Fixture.UID+"/roles/"+role.ID, nil, true)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_RoleMappingCRUD(t *testing.T) {
e2eStep(t, "P-12", "*", "/api/v1/permissions/role-mappings", "外部 IdP group → 內部 Role.Key 對映 CRUD")
c := NewClient(t)
roleKey := "e2e_mapping_role"
externalKey := fmt.Sprintf("e2e-group-%s", c.Fixture.UID)
createEnv := c.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/roles", map[string]string{
"key": roleKey,
"display_name": "E2E Mapping Role",
}, true)
var role struct {
ID string `json:"id"`
}
require.NoError(t, json.Unmarshal(createEnv.Data, &role))
require.NotEmpty(t, role.ID)
t.Cleanup(func() {
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
}, true)
_, _ = c.Do(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
})
upsertEnv := c.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 {
ID string `json:"id"`
ExternalSource string `json:"external_source"`
ExternalKey string `json:"external_key"`
InternalRoleID string `json:"internal_role_id"`
InternalRoleKey string `json:"internal_role_key"`
}
require.NoError(t, json.Unmarshal(upsertEnv.Data, &mapping))
require.NotEmpty(t, mapping.ID)
require.Equal(t, "zitadel", mapping.ExternalSource)
require.Equal(t, externalKey, mapping.ExternalKey)
require.Equal(t, role.ID, mapping.InternalRoleID)
require.Equal(t, roleKey, mapping.InternalRoleKey)
listEnv := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/role-mappings?source=zitadel", nil, true)
var list struct {
Mappings []struct {
ExternalKey string `json:"external_key"`
} `json:"mappings"`
}
require.NoError(t, json.Unmarshal(listEnv.Data, &list))
found := false
for _, item := range list.Mappings {
if item.ExternalKey == externalKey {
found = true
break
}
}
require.True(t, found, "created role mapping should appear in list")
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/role-mappings", map[string]string{
"external_source": "zitadel",
"external_key": externalKey,
}, true)
c.DoExpectOK(t, http.MethodDelete, "/api/v1/permissions/roles/"+role.ID, nil, true)
}
func TestPermission_CasbinRBAC(t *testing.T) {
e2eStep(t, "P-13/P-14", "*", "/api/v1/permissions/{policy/reload,roles}", "Casbin enforcementowner reload policy → no-role user GET /roles=403")
if os.Getenv("E2E_CASBIN") != "1" {
t.Skip("set E2E_CASBIN=1 and use e2e.casbin.yaml to enable Casbin enforcement")
}
owner := NewClient(t)
reloadEnv := owner.DoExpectOK(t, http.MethodPost, "/api/v1/permissions/policy/reload", nil, true)
var reload struct {
Tenant string `json:"tenant"`
TS int64 `json:"ts"`
}
require.NoError(t, json.Unmarshal(reloadEnv.Data, &reload))
require.Equal(t, owner.Fixture.TenantID, reload.Tenant)
require.Positive(t, reload.TS)
noRole := NewNoRoleClient(t)
denied := noRole.DoExpectHTTP(t, http.MethodGet, "/api/v1/permissions/roles", nil, true, http.StatusForbidden)
require.NotEqual(t, int64(successCode), denied.Code)
}
func firstCatalogPermissionID(t *testing.T, c *Client) string {
t.Helper()
env := c.DoExpectOK(t, http.MethodGet, "/api/v1/permissions/catalog?tree=false", nil, true)
var data struct {
List []struct {
ID string `json:"id"`
} `json:"list"`
}
require.NoError(t, json.Unmarshal(env.Data, &data))
require.NotEmpty(t, data.List)
require.NotEmpty(t, data.List[0].ID)
return data.List[0].ID
}