mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 17:27:31 +00:00
feat: organization settings for user uniqueness (#10246)
# Which Problems Are Solved Currently the username uniqueness is on instance level, we want to achieve a way to set it at organization level. # How the Problems Are Solved Addition of endpoints and a resource on organization level, where this setting can be managed. If nothing it set, the uniqueness is expected to be at instance level, where only users with instance permissions should be able to change this setting. # Additional Changes None # Additional Context Includes #10086 Closes #9964 --------- Co-authored-by: Marco A. <marco@zitadel.com>
This commit is contained in:
@@ -19,8 +19,9 @@ import (
|
||||
var _ settingsconnect.SettingsServiceHandler = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
|
||||
assetsAPIDomain func(context.Context) string
|
||||
}
|
||||
|
||||
|
281
internal/api/grpc/settings/v2beta/integration_test/query_test.go
Normal file
281
internal/api/grpc/settings/v2beta/integration_test/query_test.go
Normal file
@@ -0,0 +1,281 @@
|
||||
//go:build integration
|
||||
|
||||
package settings_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/settings/v2beta"
|
||||
)
|
||||
|
||||
func TestServer_ListOrganizationSettings(t *testing.T) {
|
||||
instance := integration.NewInstance(CTX)
|
||||
iamOwnerCtx := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
dep func(*settings.ListOrganizationSettingsRequest, *settings.ListOrganizationSettingsResponse)
|
||||
req *settings.ListOrganizationSettingsRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *settings.ListOrganizationSettingsResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list by id, unauthenticated",
|
||||
args: args{
|
||||
ctx: CTX,
|
||||
dep: func(request *settings.ListOrganizationSettingsRequest, response *settings.ListOrganizationSettingsResponse) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp.GetOrganizationId(), true)
|
||||
|
||||
request.Filters[0].Filter = &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{orgResp.GetOrganizationId()},
|
||||
},
|
||||
}
|
||||
},
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list by id, no permission",
|
||||
args: args{
|
||||
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeNoPermission),
|
||||
dep: func(request *settings.ListOrganizationSettingsRequest, response *settings.ListOrganizationSettingsResponse) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp.GetOrganizationId(), true)
|
||||
|
||||
request.Filters[0].Filter = &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{orgResp.GetOrganizationId()},
|
||||
},
|
||||
}
|
||||
},
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &settings.ListOrganizationSettingsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
OrganizationSettings: []*settings.OrganizationSettings{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list by id, missing permission",
|
||||
args: args{
|
||||
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
|
||||
dep: func(request *settings.ListOrganizationSettingsRequest, response *settings.ListOrganizationSettingsResponse) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp.GetOrganizationId(), true)
|
||||
|
||||
request.Filters[0].Filter = &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{orgResp.GetOrganizationId()},
|
||||
},
|
||||
}
|
||||
},
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &settings.ListOrganizationSettingsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
OrganizationSettings: []*settings.OrganizationSettings{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list, not found",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{
|
||||
Filter: &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{"notexisting"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
want: &settings.ListOrganizationSettingsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 0,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
OrganizationSettings: []*settings.OrganizationSettings{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *settings.ListOrganizationSettingsRequest, response *settings.ListOrganizationSettingsResponse) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
settingsResp := instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp.GetOrganizationId(), true)
|
||||
|
||||
request.Filters[0].Filter = &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{orgResp.GetOrganizationId()},
|
||||
},
|
||||
}
|
||||
response.OrganizationSettings[0] = &settings.OrganizationSettings{
|
||||
OrganizationId: orgResp.GetOrganizationId(),
|
||||
CreationDate: settingsResp.GetSetDate(),
|
||||
ChangeDate: settingsResp.GetSetDate(),
|
||||
OrganizationScopedUsernames: true,
|
||||
}
|
||||
},
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &settings.ListOrganizationSettingsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
OrganizationSettings: []*settings.OrganizationSettings{{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list multiple id",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *settings.ListOrganizationSettingsRequest, response *settings.ListOrganizationSettingsResponse) {
|
||||
orgResp1 := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
settingsResp1 := instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp1.GetOrganizationId(), true)
|
||||
orgResp2 := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
settingsResp2 := instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp2.GetOrganizationId(), true)
|
||||
orgResp3 := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
settingsResp3 := instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp3.GetOrganizationId(), true)
|
||||
|
||||
request.Filters[0].Filter = &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{orgResp1.GetOrganizationId(), orgResp2.GetOrganizationId(), orgResp3.GetOrganizationId()},
|
||||
},
|
||||
}
|
||||
response.OrganizationSettings[2] = &settings.OrganizationSettings{
|
||||
OrganizationId: orgResp1.GetOrganizationId(),
|
||||
CreationDate: settingsResp1.GetSetDate(),
|
||||
ChangeDate: settingsResp1.GetSetDate(),
|
||||
OrganizationScopedUsernames: true,
|
||||
}
|
||||
response.OrganizationSettings[1] = &settings.OrganizationSettings{
|
||||
OrganizationId: orgResp2.GetOrganizationId(),
|
||||
CreationDate: settingsResp2.GetSetDate(),
|
||||
ChangeDate: settingsResp2.GetSetDate(),
|
||||
OrganizationScopedUsernames: true,
|
||||
}
|
||||
response.OrganizationSettings[0] = &settings.OrganizationSettings{
|
||||
OrganizationId: orgResp3.GetOrganizationId(),
|
||||
CreationDate: settingsResp3.GetSetDate(),
|
||||
ChangeDate: settingsResp3.GetSetDate(),
|
||||
OrganizationScopedUsernames: true,
|
||||
}
|
||||
},
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &settings.ListOrganizationSettingsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 3,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
OrganizationSettings: []*settings.OrganizationSettings{{}, {}, {}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list multiple id, only org scoped usernames",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *settings.ListOrganizationSettingsRequest, response *settings.ListOrganizationSettingsResponse) {
|
||||
orgResp1 := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp1.GetOrganizationId(), false)
|
||||
orgResp2 := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
settingsResp2 := instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp2.GetOrganizationId(), true)
|
||||
orgResp3 := instance.CreateOrganization(iamOwnerCtx, gofakeit.Company(), gofakeit.Email())
|
||||
instance.SetOrganizationSettings(iamOwnerCtx, t, orgResp3.GetOrganizationId(), false)
|
||||
|
||||
request.Filters[0].Filter = &settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter{
|
||||
InOrganizationIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{orgResp1.GetOrganizationId(), orgResp2.GetOrganizationId(), orgResp3.GetOrganizationId()},
|
||||
},
|
||||
}
|
||||
request.Filters[1].Filter = &settings.OrganizationSettingsSearchFilter_OrganizationScopedUsernamesFilter{
|
||||
OrganizationScopedUsernamesFilter: &settings.OrganizationScopedUsernamesFilter{
|
||||
OrganizationScopedUsernames: true,
|
||||
},
|
||||
}
|
||||
response.OrganizationSettings[0] = &settings.OrganizationSettings{
|
||||
OrganizationId: orgResp2.GetOrganizationId(),
|
||||
CreationDate: settingsResp2.GetSetDate(),
|
||||
ChangeDate: settingsResp2.GetSetDate(),
|
||||
OrganizationScopedUsernames: true,
|
||||
}
|
||||
},
|
||||
req: &settings.ListOrganizationSettingsRequest{
|
||||
Filters: []*settings.OrganizationSettingsSearchFilter{{}, {}},
|
||||
},
|
||||
},
|
||||
want: &settings.ListOrganizationSettingsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
OrganizationSettings: []*settings.OrganizationSettings{{}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.dep != nil {
|
||||
tt.args.dep(tt.args.req, tt.want)
|
||||
}
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCtx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, listErr := instance.Client.SettingsV2beta.ListOrganizationSettings(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, listErr)
|
||||
return
|
||||
}
|
||||
require.NoError(ttt, listErr)
|
||||
|
||||
// always first check length, otherwise its failed anyway
|
||||
if assert.Len(ttt, got.OrganizationSettings, len(tt.want.OrganizationSettings)) {
|
||||
for i := range tt.want.OrganizationSettings {
|
||||
assert.EqualExportedValues(ttt, tt.want.OrganizationSettings[i], got.OrganizationSettings[i])
|
||||
}
|
||||
}
|
||||
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
||||
}, retryDuration, tick, "timeout waiting for expected execution result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationResponse, actual *filter.PaginationResponse) {
|
||||
assert.Equal(t, expected.AppliedLimit, actual.AppliedLimit)
|
||||
assert.Equal(t, expected.TotalResult, actual.TotalResult)
|
||||
}
|
@@ -7,6 +7,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -178,3 +180,279 @@ func TestServer_SetSecuritySettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_SetOrganizationSettings(t *testing.T) {
|
||||
instance := integration.NewInstance(CTX)
|
||||
iamOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *settings.SetOrganizationSettingsRequest
|
||||
}
|
||||
type want struct {
|
||||
set bool
|
||||
setDate bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(req *settings.SetOrganizationSettingsRequest)
|
||||
args args
|
||||
want want
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "permission error",
|
||||
args: args{
|
||||
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
|
||||
req: &settings.SetOrganizationSettingsRequest{
|
||||
OrganizationId: Instance.DefaultOrg.GetId(),
|
||||
OrganizationScopedUsernames: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "org not provided",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.SetOrganizationSettingsRequest{
|
||||
OrganizationId: "",
|
||||
OrganizationScopedUsernames: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "org not existing",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.SetOrganizationSettingsRequest{
|
||||
OrganizationId: "notexisting",
|
||||
OrganizationScopedUsernames: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "success no changes",
|
||||
prepare: func(req *settings.SetOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
},
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.SetOrganizationSettingsRequest{},
|
||||
},
|
||||
want: want{
|
||||
set: false,
|
||||
setDate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success user uniqueness",
|
||||
prepare: func(req *settings.SetOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
},
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.SetOrganizationSettingsRequest{
|
||||
OrganizationScopedUsernames: gu.Ptr(true),
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
set: true,
|
||||
setDate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success no change",
|
||||
prepare: func(req *settings.SetOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
},
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.SetOrganizationSettingsRequest{
|
||||
OrganizationScopedUsernames: gu.Ptr(false),
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
set: false,
|
||||
setDate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
creationDate := time.Now().UTC()
|
||||
if tt.prepare != nil {
|
||||
tt.prepare(tt.args.req)
|
||||
}
|
||||
|
||||
got, err := instance.Client.SettingsV2beta.SetOrganizationSettings(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
setDate := time.Time{}
|
||||
if tt.want.set {
|
||||
setDate = time.Now().UTC()
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assertOrganizationSettingsResponse(t, creationDate, setDate, tt.want.setDate, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertOrganizationSettingsResponse(t *testing.T, creationDate, setDate time.Time, expectedSetDate bool, actualResp *settings.SetOrganizationSettingsResponse) {
|
||||
if expectedSetDate {
|
||||
if !setDate.IsZero() {
|
||||
assert.WithinRange(t, actualResp.GetSetDate().AsTime(), creationDate, setDate)
|
||||
} else {
|
||||
assert.WithinRange(t, actualResp.GetSetDate().AsTime(), creationDate, time.Now().UTC())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, actualResp.SetDate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_DeleteOrganizationSettings(t *testing.T) {
|
||||
instance := integration.NewInstance(CTX)
|
||||
iamOwnerCTX := instance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *settings.DeleteOrganizationSettingsRequest
|
||||
}
|
||||
type want struct {
|
||||
deletion bool
|
||||
deletionDate bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare func(t *testing.T, req *settings.DeleteOrganizationSettingsRequest)
|
||||
args args
|
||||
want want
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "permission error",
|
||||
prepare: func(t *testing.T, req *settings.DeleteOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
instance.SetOrganizationSettings(iamOwnerCTX, t, orgResp.GetOrganizationId(), true)
|
||||
},
|
||||
args: args{
|
||||
ctx: instance.WithAuthorizationToken(CTX, integration.UserTypeOrgOwner),
|
||||
req: &settings.DeleteOrganizationSettingsRequest{
|
||||
OrganizationId: Instance.DefaultOrg.GetId(),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "org not provided",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.DeleteOrganizationSettingsRequest{
|
||||
OrganizationId: "",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "org not existing",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.DeleteOrganizationSettingsRequest{
|
||||
OrganizationId: "notexisting",
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
deletion: false,
|
||||
deletionDate: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success user uniqueness",
|
||||
prepare: func(t *testing.T, req *settings.DeleteOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
instance.SetOrganizationSettings(iamOwnerCTX, t, orgResp.GetOrganizationId(), true)
|
||||
},
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.DeleteOrganizationSettingsRequest{},
|
||||
},
|
||||
want: want{
|
||||
deletion: true,
|
||||
deletionDate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success no existing",
|
||||
prepare: func(t *testing.T, req *settings.DeleteOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
},
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.DeleteOrganizationSettingsRequest{},
|
||||
},
|
||||
want: want{
|
||||
deletion: false,
|
||||
deletionDate: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success already deleted",
|
||||
prepare: func(t *testing.T, req *settings.DeleteOrganizationSettingsRequest) {
|
||||
orgResp := instance.CreateOrganization(iamOwnerCTX, gofakeit.Company(), gofakeit.Email())
|
||||
req.OrganizationId = orgResp.GetOrganizationId()
|
||||
instance.SetOrganizationSettings(iamOwnerCTX, t, orgResp.GetOrganizationId(), true)
|
||||
instance.DeleteOrganizationSettings(iamOwnerCTX, t, orgResp.GetOrganizationId())
|
||||
},
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
req: &settings.DeleteOrganizationSettingsRequest{},
|
||||
},
|
||||
want: want{
|
||||
deletion: false,
|
||||
deletionDate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
creationDate := time.Now().UTC()
|
||||
if tt.prepare != nil {
|
||||
tt.prepare(t, tt.args.req)
|
||||
}
|
||||
|
||||
got, err := instance.Client.SettingsV2beta.DeleteOrganizationSettings(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
deletionDate := time.Time{}
|
||||
if tt.want.deletion {
|
||||
deletionDate = time.Now().UTC()
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assertDeleteOrganizationSettingsResponse(t, creationDate, deletionDate, tt.want.deletionDate, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertDeleteOrganizationSettingsResponse(t *testing.T, creationDate, deletionDate time.Time, expectedDeletionDate bool, actualResp *settings.DeleteOrganizationSettingsResponse) {
|
||||
if expectedDeletionDate {
|
||||
if !deletionDate.IsZero() {
|
||||
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), creationDate, deletionDate)
|
||||
} else {
|
||||
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), creationDate, time.Now().UTC())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, actualResp.DeletionDate)
|
||||
}
|
||||
}
|
||||
|
114
internal/api/grpc/settings/v2beta/query.go
Normal file
114
internal/api/grpc/settings/v2beta/query.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2beta"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
filter_pb "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/settings/v2beta"
|
||||
)
|
||||
|
||||
func (s *Server) ListOrganizationSettings(ctx context.Context, req *connect.Request[settings.ListOrganizationSettingsRequest]) (*connect.Response[settings.ListOrganizationSettingsResponse], error) {
|
||||
queries, err := s.listOrganizationSettingsRequestToModel(req.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := s.query.SearchOrganizationSettings(ctx, queries, s.checkPermission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&settings.ListOrganizationSettingsResponse{
|
||||
OrganizationSettings: organizationSettingsListToPb(resp.OrganizationSettingsList),
|
||||
Pagination: filter.QueryToPaginationPb(queries.SearchRequest, resp.SearchResponse),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) listOrganizationSettingsRequestToModel(req *settings.ListOrganizationSettingsRequest) (*query.OrganizationSettingsSearchQueries, error) {
|
||||
offset, limit, asc, err := filter.PaginationPbToQuery(s.systemDefaults, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := organizationSettingsFiltersToQuery(req.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.OrganizationSettingsSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: organizationSettingsFieldNameToSortingColumn(req.SortingColumn),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func organizationSettingsFieldNameToSortingColumn(field *settings.OrganizationSettingsFieldName) query.Column {
|
||||
if field == nil {
|
||||
return query.OrganizationSettingsColumnCreationDate
|
||||
}
|
||||
switch *field {
|
||||
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_CREATION_DATE:
|
||||
return query.OrganizationSettingsColumnCreationDate
|
||||
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_ORGANIZATION_ID:
|
||||
return query.OrganizationSettingsColumnID
|
||||
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_CHANGE_DATE:
|
||||
return query.OrganizationSettingsColumnChangeDate
|
||||
case settings.OrganizationSettingsFieldName_ORGANIZATION_SETTINGS_FIELD_NAME_UNSPECIFIED:
|
||||
return query.OrganizationSettingsColumnCreationDate
|
||||
default:
|
||||
return query.OrganizationSettingsColumnCreationDate
|
||||
}
|
||||
}
|
||||
|
||||
func organizationSettingsFiltersToQuery(queries []*settings.OrganizationSettingsSearchFilter) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, qry := range queries {
|
||||
q[i], err = organizationSettingsToModel(qry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func organizationSettingsToModel(filter *settings.OrganizationSettingsSearchFilter) (query.SearchQuery, error) {
|
||||
switch q := filter.Filter.(type) {
|
||||
case *settings.OrganizationSettingsSearchFilter_InOrganizationIdsFilter:
|
||||
return organizationInIDsFilterToQuery(q.InOrganizationIdsFilter)
|
||||
case *settings.OrganizationSettingsSearchFilter_OrganizationScopedUsernamesFilter:
|
||||
return organizationScopedUsernamesFilterToQuery(q.OrganizationScopedUsernamesFilter)
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "SETTINGS-uvTDqZHlvS", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func organizationInIDsFilterToQuery(q *filter_pb.InIDsFilter) (query.SearchQuery, error) {
|
||||
return query.NewOrganizationSettingsOrganizationIDSearchQuery(q.Ids)
|
||||
}
|
||||
|
||||
func organizationScopedUsernamesFilterToQuery(q *settings.OrganizationScopedUsernamesFilter) (query.SearchQuery, error) {
|
||||
return query.NewOrganizationSettingsOrganizationScopedUsernamesSearchQuery(q.OrganizationScopedUsernames)
|
||||
}
|
||||
|
||||
func organizationSettingsListToPb(settingsList []*query.OrganizationSettings) []*settings.OrganizationSettings {
|
||||
o := make([]*settings.OrganizationSettings, len(settingsList))
|
||||
for i, organizationSettings := range settingsList {
|
||||
o[i] = organizationSettingsToPb(organizationSettings)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func organizationSettingsToPb(organizationSettings *query.OrganizationSettings) *settings.OrganizationSettings {
|
||||
return &settings.OrganizationSettings{
|
||||
OrganizationId: organizationSettings.ID,
|
||||
CreationDate: timestamppb.New(organizationSettings.CreationDate),
|
||||
ChangeDate: timestamppb.New(organizationSettings.ChangeDate),
|
||||
OrganizationScopedUsernames: organizationSettings.OrganizationScopedUsernames,
|
||||
}
|
||||
}
|
@@ -11,6 +11,8 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
settings "github.com/zitadel/zitadel/pkg/grpc/settings/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/settings/v2beta/settingsconnect"
|
||||
@@ -19,20 +21,27 @@ import (
|
||||
var _ settingsconnect.SettingsServiceHandler = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
|
||||
checkPermission domain.PermissionCheck
|
||||
assetsAPIDomain func(context.Context) string
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
systemDefaults: systemDefaults,
|
||||
command: command,
|
||||
query: query,
|
||||
checkPermission: checkPermission,
|
||||
assetsAPIDomain: assets.AssetAPI(),
|
||||
}
|
||||
}
|
||||
|
@@ -167,3 +167,31 @@ func (s *Server) SetSecuritySettings(ctx context.Context, req *connect.Request[s
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) SetOrganizationSettings(ctx context.Context, req *connect.Request[settings.SetOrganizationSettingsRequest]) (*connect.Response[settings.SetOrganizationSettingsResponse], error) {
|
||||
details, err := s.command.SetOrganizationSettings(ctx, organizationSettingsToCommand(req.Msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var setDate *timestamppb.Timestamp
|
||||
if !details.EventDate.IsZero() {
|
||||
setDate = timestamppb.New(details.EventDate)
|
||||
}
|
||||
return connect.NewResponse(&settings.SetOrganizationSettingsResponse{
|
||||
SetDate: setDate,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteOrganizationSettings(ctx context.Context, req *connect.Request[settings.DeleteOrganizationSettingsRequest]) (*connect.Response[settings.DeleteOrganizationSettingsResponse], error) {
|
||||
details, err := s.command.DeleteOrganizationSettings(ctx, req.Msg.GetOrganizationId())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var deletionDate *timestamppb.Timestamp
|
||||
if !details.EventDate.IsZero() {
|
||||
deletionDate = timestamppb.New(details.EventDate)
|
||||
}
|
||||
return connect.NewResponse(&settings.DeleteOrganizationSettingsResponse{
|
||||
DeletionDate: deletionDate,
|
||||
}), nil
|
||||
}
|
||||
|
@@ -243,3 +243,10 @@ func securitySettingsToCommand(req *settings.SetSecuritySettingsRequest) *comman
|
||||
EnableImpersonation: req.GetEnableImpersonation(),
|
||||
}
|
||||
}
|
||||
|
||||
func organizationSettingsToCommand(req *settings.SetOrganizationSettingsRequest) *command.SetOrganizationSettings {
|
||||
return &command.SetOrganizationSettings{
|
||||
OrganizationID: req.OrganizationId,
|
||||
OrganizationScopedUsernames: req.OrganizationScopedUsernames,
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user