//go:build e2e package e2e import ( "encoding/json" "fmt" "net/http" "os" "testing" "github.com/stretchr/testify/require" ) func TestPermission_Catalog(t *testing.T) { 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) { 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) { 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) 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) { 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) 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) { 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)) 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) { 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) 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) { 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 }