mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:57:33 +00:00
feat: org v2 ListOrganizations (#8411)
# Which Problems Are Solved Org v2 service does not have a ListOrganizations endpoint. # How the Problems Are Solved Implement ListOrganizations endpoint. # Additional Changes - moved descriptions in the protos to comments - corrected the RemoveNoPermissions for the ListUsers, to get the correct TotalResults # Additional Context For new typescript login
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
@@ -82,6 +83,17 @@ type Org struct {
|
||||
Domain string
|
||||
}
|
||||
|
||||
func orgsCheckPermission(ctx context.Context, orgs *Orgs, permissionCheck domain_pkg.PermissionCheck) {
|
||||
orgs.Orgs = slices.DeleteFunc(orgs.Orgs,
|
||||
func(org *Org) bool {
|
||||
if err := permissionCheck(ctx, domain_pkg.PermissionOrgRead, org.ID, org.ID); err != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type OrgSearchQueries struct {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
@@ -254,7 +266,18 @@ func (q *Queries) ExistsOrg(ctx context.Context, id, domain string) (verifiedID
|
||||
return org.ID, nil
|
||||
}
|
||||
|
||||
func (q *Queries) SearchOrgs(ctx context.Context, queries *OrgSearchQueries) (orgs *Orgs, err error) {
|
||||
func (q *Queries) SearchOrgs(ctx context.Context, queries *OrgSearchQueries, permissionCheck domain_pkg.PermissionCheck) (*Orgs, error) {
|
||||
orgs, err := q.searchOrgs(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if permissionCheck != nil {
|
||||
orgsCheckPermission(ctx, orgs, permissionCheck)
|
||||
}
|
||||
return orgs, nil
|
||||
}
|
||||
|
||||
func (q *Queries) searchOrgs(ctx context.Context, queries *OrgSearchQueries) (orgs *Orgs, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/database"
|
||||
db_mock "github.com/zitadel/zitadel/internal/database/mock"
|
||||
@@ -441,3 +442,126 @@ func TestQueries_IsOrgUnique(t *testing.T) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestOrg_RemoveNoPermission(t *testing.T) {
|
||||
type want struct {
|
||||
orgs []*Org
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
want want
|
||||
orgs *Orgs
|
||||
permissions []string
|
||||
}{
|
||||
{
|
||||
"permissions for all",
|
||||
want{
|
||||
orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{"first", "second", "third"},
|
||||
},
|
||||
{
|
||||
"permissions for one, first",
|
||||
want{
|
||||
orgs: []*Org{
|
||||
{ID: "first"},
|
||||
},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{"first"},
|
||||
},
|
||||
{
|
||||
"permissions for one, second",
|
||||
want{
|
||||
orgs: []*Org{
|
||||
{ID: "second"},
|
||||
},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{"second"},
|
||||
},
|
||||
{
|
||||
"permissions for one, third",
|
||||
want{
|
||||
orgs: []*Org{
|
||||
{ID: "third"},
|
||||
},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{"third"},
|
||||
},
|
||||
{
|
||||
"permissions for two, first third",
|
||||
want{
|
||||
orgs: []*Org{
|
||||
{ID: "first"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{"first", "third"},
|
||||
},
|
||||
{
|
||||
"permissions for two, second third",
|
||||
want{
|
||||
orgs: []*Org{
|
||||
{ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{"second", "third"},
|
||||
},
|
||||
{
|
||||
"no permissions",
|
||||
want{
|
||||
orgs: []*Org{},
|
||||
},
|
||||
&Orgs{
|
||||
Orgs: []*Org{
|
||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
||||
},
|
||||
},
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
checkPermission := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
||||
for _, perm := range tt.permissions {
|
||||
if resourceID == perm {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("failed")
|
||||
}
|
||||
orgsCheckPermission(context.Background(), tt.orgs, checkPermission)
|
||||
require.Equal(t, tt.want.orgs, tt.orgs.Orgs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -123,27 +124,18 @@ type NotifyUser struct {
|
||||
PasswordSet bool
|
||||
}
|
||||
|
||||
func (u *Users) RemoveNoPermission(ctx context.Context, permissionCheck domain.PermissionCheck) {
|
||||
removableIndexes := make([]int, 0)
|
||||
for i := range u.Users {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
if ctxData.UserID != u.Users[i].ID {
|
||||
if err := permissionCheck(ctx, domain.PermissionUserRead, u.Users[i].ResourceOwner, u.Users[i].ID); err != nil {
|
||||
removableIndexes = append(removableIndexes, i)
|
||||
func usersCheckPermission(ctx context.Context, users *Users, permissionCheck domain.PermissionCheck) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
users.Users = slices.DeleteFunc(users.Users,
|
||||
func(user *User) bool {
|
||||
if ctxData.UserID != user.ID {
|
||||
if err := permissionCheck(ctx, domain.PermissionUserRead, user.ResourceOwner, user.ID); err != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
removed := 0
|
||||
for _, removeIndex := range removableIndexes {
|
||||
u.Users = removeUser(u.Users, removeIndex-removed)
|
||||
removed++
|
||||
}
|
||||
// reset count as some users could be removed
|
||||
u.SearchResponse.Count = uint64(len(u.Users))
|
||||
}
|
||||
|
||||
func removeUser(slice []*User, s int) []*User {
|
||||
return append(slice[:s], slice[s+1:]...)
|
||||
return false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
type UserSearchQueries struct {
|
||||
@@ -597,7 +589,18 @@ func (q *Queries) GetNotifyUser(ctx context.Context, shouldTriggered bool, queri
|
||||
return user, err
|
||||
}
|
||||
|
||||
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries) (users *Users, err error) {
|
||||
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, permissionCheck domain.PermissionCheck) (*Users, error) {
|
||||
users, err := q.searchUsers(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if permissionCheck != nil {
|
||||
usersCheckPermission(ctx, users, permissionCheck)
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (q *Queries) searchUsers(ctx context.Context, queries *UserSearchQueries) (users *Users, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
|
@@ -17,7 +17,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
|
||||
func Test_RemoveNoPermission(t *testing.T) {
|
||||
func TestUser_RemoveNoPermission(t *testing.T) {
|
||||
type want struct {
|
||||
users []*User
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func Test_RemoveNoPermission(t *testing.T) {
|
||||
}
|
||||
return errors.New("failed")
|
||||
}
|
||||
tt.users.RemoveNoPermission(context.Background(), checkPermission)
|
||||
usersCheckPermission(context.Background(), tt.users, checkPermission)
|
||||
require.Equal(t, tt.want.users, tt.users.Users)
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user