diff --git a/internal/api/grpc/idp/v2/query.go b/internal/api/grpc/idp/v2/query.go index 476ac3dcdd..0b66175c14 100644 --- a/internal/api/grpc/idp/v2/query.go +++ b/internal/api/grpc/idp/v2/query.go @@ -96,7 +96,7 @@ func configToPb(config *query.IDPTemplate) *idp_pb.IDPConfig { IsCreationAllowed: config.IsCreationAllowed, IsAutoCreation: config.IsAutoCreation, IsAutoUpdate: config.IsAutoUpdate, - AutoLinking: autoLinkingOptionToPb(config.AutoLinking), + AutoLinking: AutoLinkingOptionToPb(config.AutoLinking), }, } if config.OAuthIDPTemplate != nil { @@ -150,7 +150,7 @@ func configToPb(config *query.IDPTemplate) *idp_pb.IDPConfig { return idpConfig } -func autoLinkingOptionToPb(linking domain.AutoLinkingOption) idp_pb.AutoLinkingOption { +func AutoLinkingOptionToPb(linking domain.AutoLinkingOption) idp_pb.AutoLinkingOption { switch linking { case domain.AutoLinkingOptionUnspecified: return idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_UNSPECIFIED diff --git a/internal/api/grpc/settings/v2/integration_test/settings_test.go b/internal/api/grpc/settings/v2/integration_test/settings_test.go index 16942137c9..daa800b0bd 100644 --- a/internal/api/grpc/settings/v2/integration_test/settings_test.go +++ b/internal/api/grpc/settings/v2/integration_test/settings_test.go @@ -7,11 +7,15 @@ 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" "github.com/zitadel/zitadel/internal/integration" + "github.com/zitadel/zitadel/pkg/grpc/idp" + idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp/v2" object_pb "github.com/zitadel/zitadel/pkg/grpc/object/v2" "github.com/zitadel/zitadel/pkg/grpc/settings/v2" ) @@ -178,3 +182,281 @@ func TestServer_SetSecuritySettings(t *testing.T) { }) } } + +func idpResponse(id, name string, linking, creation, autoCreation, autoUpdate bool, autoLinking idp_pb.AutoLinkingOption) *settings.IdentityProvider { + return &settings.IdentityProvider{ + Id: id, + Name: name, + Type: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OAUTH, + Options: &idp_pb.Options{ + IsLinkingAllowed: linking, + IsCreationAllowed: creation, + IsAutoCreation: autoCreation, + IsAutoUpdate: autoUpdate, + AutoLinking: autoLinking, + }, + } +} + +func TestServer_GetActiveIdentityProviders(t *testing.T) { + instance := integration.NewInstance(CTX) + isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner) + + instance.AddGenericOAuthProvider(isolatedIAMOwnerCTX, gofakeit.AppName()) // inactive + idpActiveName := gofakeit.AppName() + idpActiveResp := instance.AddGenericOAuthProvider(isolatedIAMOwnerCTX, idpActiveName) + instance.AddProviderToDefaultLoginPolicy(isolatedIAMOwnerCTX, idpActiveResp.GetId()) + idpActiveResponse := idpResponse(idpActiveResp.GetId(), idpActiveName, true, true, true, true, idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + idpLinkingDisallowedName := gofakeit.AppName() + idpLinkingDisallowedResp := instance.AddGenericOAuthProviderWithOptions(isolatedIAMOwnerCTX, idpLinkingDisallowedName, false, true, true, idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + instance.AddProviderToDefaultLoginPolicy(isolatedIAMOwnerCTX, idpLinkingDisallowedResp.GetId()) + idpLinkingDisallowedResponse := idpResponse(idpLinkingDisallowedResp.GetId(), idpLinkingDisallowedName, false, true, true, true, idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + idpCreationDisallowedName := gofakeit.AppName() + idpCreationDisallowedResp := instance.AddGenericOAuthProviderWithOptions(isolatedIAMOwnerCTX, idpCreationDisallowedName, true, false, true, idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + instance.AddProviderToDefaultLoginPolicy(isolatedIAMOwnerCTX, idpCreationDisallowedResp.GetId()) + idpCreationDisallowedResponse := idpResponse(idpCreationDisallowedResp.GetId(), idpCreationDisallowedName, true, false, true, true, idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + idpNoAutoCreationName := gofakeit.AppName() + idpNoAutoCreationResp := instance.AddGenericOAuthProviderWithOptions(isolatedIAMOwnerCTX, idpNoAutoCreationName, true, true, false, idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + instance.AddProviderToDefaultLoginPolicy(isolatedIAMOwnerCTX, idpNoAutoCreationResp.GetId()) + idpNoAutoCreationResponse := idpResponse(idpNoAutoCreationResp.GetId(), idpNoAutoCreationName, true, true, false, true, idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) + idpNoAutoLinkingName := gofakeit.AppName() + idpNoAutoLinkingResp := instance.AddGenericOAuthProviderWithOptions(isolatedIAMOwnerCTX, idpNoAutoLinkingName, true, true, true, idp.AutoLinkingOption_AUTO_LINKING_OPTION_UNSPECIFIED) + instance.AddProviderToDefaultLoginPolicy(isolatedIAMOwnerCTX, idpNoAutoLinkingResp.GetId()) + idpNoAutoLinkingResponse := idpResponse(idpNoAutoLinkingResp.GetId(), idpNoAutoLinkingName, true, true, true, true, idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_UNSPECIFIED) + + type args struct { + ctx context.Context + req *settings.GetActiveIdentityProvidersRequest + } + tests := []struct { + name string + args args + want *settings.GetActiveIdentityProvidersResponse + wantErr bool + }{ + { + name: "permission error", + args: args{ + ctx: instance.WithAuthorization(CTX, integration.UserTypeLogin), + req: &settings.GetActiveIdentityProvidersRequest{}, + }, + wantErr: true, + }, + { + name: "success, all", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{}, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 5, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpActiveResponse, + idpLinkingDisallowedResponse, + idpCreationDisallowedResponse, + idpNoAutoCreationResponse, + idpNoAutoLinkingResponse, + }, + }, + }, + { + name: "success, exclude linking disallowed", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + LinkingAllowed: gu.Ptr(true), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 4, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpActiveResponse, + idpCreationDisallowedResponse, + idpNoAutoCreationResponse, + idpNoAutoLinkingResponse, + }, + }, + }, + { + name: "success, only linking disallowed", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + LinkingAllowed: gu.Ptr(false), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 1, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpLinkingDisallowedResponse, + }, + }, + }, + { + name: "success, exclude creation disallowed", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + CreationAllowed: gu.Ptr(true), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 4, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpActiveResponse, + idpLinkingDisallowedResponse, + idpNoAutoCreationResponse, + idpNoAutoLinkingResponse, + }, + }, + }, + { + name: "success, only creation disallowed", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + CreationAllowed: gu.Ptr(false), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 1, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpCreationDisallowedResponse, + }, + }, + }, + { + name: "success, auto creation", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + AutoCreation: gu.Ptr(true), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 4, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpActiveResponse, + idpLinkingDisallowedResponse, + idpCreationDisallowedResponse, + idpNoAutoLinkingResponse, + }, + }, + }, + { + name: "success, no auto creation", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + AutoCreation: gu.Ptr(false), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 1, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpNoAutoCreationResponse, + }, + }, + }, + { + name: "success, auto linking", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + AutoLinking: gu.Ptr(true), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 4, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpActiveResponse, + idpLinkingDisallowedResponse, + idpCreationDisallowedResponse, + idpNoAutoCreationResponse, + }, + }, + }, + { + name: "success, no auto linking", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + AutoLinking: gu.Ptr(false), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 1, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpNoAutoLinkingResponse, + }, + }, + }, + { + name: "success, exclude all", + args: args{ + ctx: isolatedIAMOwnerCTX, + req: &settings.GetActiveIdentityProvidersRequest{ + LinkingAllowed: gu.Ptr(true), + CreationAllowed: gu.Ptr(true), + AutoCreation: gu.Ptr(true), + AutoLinking: gu.Ptr(true), + }, + }, + want: &settings.GetActiveIdentityProvidersResponse{ + Details: &object_pb.ListDetails{ + TotalResult: 1, + Timestamp: timestamppb.Now(), + }, + IdentityProviders: []*settings.IdentityProvider{ + idpActiveResponse, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, time.Minute) + assert.EventuallyWithT(t, func(ct *assert.CollectT) { + got, err := instance.Client.SettingsV2.GetActiveIdentityProviders(tt.args.ctx, tt.args.req) + if tt.wantErr { + assert.Error(ct, err) + return + } + if !assert.NoError(ct, err) { + return + } + for i, result := range tt.want.GetIdentityProviders() { + assert.EqualExportedValues(ct, result, got.GetIdentityProviders()[i]) + } + integration.AssertListDetails(ct, tt.want, got) + }, retryDuration, tick) + }) + } +} diff --git a/internal/api/grpc/settings/v2/settings.go b/internal/api/grpc/settings/v2/settings.go index 3e48ab0c04..8b9ab9b845 100644 --- a/internal/api/grpc/settings/v2/settings.go +++ b/internal/api/grpc/settings/v2/settings.go @@ -120,7 +120,12 @@ func (s *Server) GetLockoutSettings(ctx context.Context, req *settings.GetLockou } func (s *Server) GetActiveIdentityProviders(ctx context.Context, req *settings.GetActiveIdentityProvidersRequest) (*settings.GetActiveIdentityProvidersResponse, error) { - links, err := s.query.IDPLoginPolicyLinks(ctx, object.ResourceOwnerFromReq(ctx, req.GetCtx()), &query.IDPLoginPolicyLinksSearchQuery{}, false) + queries, err := activeIdentityProvidersToQuery(req) + if err != nil { + return nil, err + } + + links, err := s.query.IDPLoginPolicyLinks(ctx, object.ResourceOwnerFromReq(ctx, req.GetCtx()), &query.IDPLoginPolicyLinksSearchQuery{Queries: queries}, false) if err != nil { return nil, err } @@ -131,6 +136,43 @@ func (s *Server) GetActiveIdentityProviders(ctx context.Context, req *settings.G }, nil } +func activeIdentityProvidersToQuery(req *settings.GetActiveIdentityProvidersRequest) (_ []query.SearchQuery, err error) { + q := make([]query.SearchQuery, 0, 4) + if req.CreationAllowed != nil { + creationQuery, err := query.NewIDPTemplateIsCreationAllowedSearchQuery(*req.CreationAllowed) + if err != nil { + return nil, err + } + q = append(q, creationQuery) + } + if req.LinkingAllowed != nil { + creationQuery, err := query.NewIDPTemplateIsLinkingAllowedSearchQuery(*req.LinkingAllowed) + if err != nil { + return nil, err + } + q = append(q, creationQuery) + } + if req.AutoCreation != nil { + creationQuery, err := query.NewIDPTemplateIsAutoCreationSearchQuery(*req.AutoCreation) + if err != nil { + return nil, err + } + q = append(q, creationQuery) + } + if req.AutoLinking != nil { + compare := query.NumberEquals + if *req.AutoLinking { + compare = query.NumberNotEquals + } + creationQuery, err := query.NewIDPTemplateAutoLinkingSearchQuery(0, compare) + if err != nil { + return nil, err + } + q = append(q, creationQuery) + } + return q, nil +} + func (s *Server) GetGeneralSettings(ctx context.Context, _ *settings.GetGeneralSettingsRequest) (*settings.GetGeneralSettingsResponse, error) { instance := authz.GetInstance(ctx) return &settings.GetGeneralSettingsResponse{ diff --git a/internal/api/grpc/settings/v2/settings_converter.go b/internal/api/grpc/settings/v2/settings_converter.go index 222e548d1b..b1329a5973 100644 --- a/internal/api/grpc/settings/v2/settings_converter.go +++ b/internal/api/grpc/settings/v2/settings_converter.go @@ -5,9 +5,11 @@ import ( "google.golang.org/protobuf/types/known/durationpb" + idp_api "github.com/zitadel/zitadel/internal/api/grpc/idp/v2" "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" + idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp/v2" "github.com/zitadel/zitadel/pkg/grpc/settings/v2" ) @@ -189,6 +191,13 @@ func identityProviderToPb(idp *query.IDPLoginPolicyLink) *settings.IdentityProvi Id: idp.IDPID, Name: domain.IDPName(idp.IDPName, idp.IDPType), Type: idpTypeToPb(idp.IDPType), + Options: &idp_pb.Options{ + IsLinkingAllowed: idp.IsLinkingAllowed, + IsCreationAllowed: idp.IsCreationAllowed, + IsAutoCreation: idp.IsAutoCreation, + IsAutoUpdate: idp.IsAutoUpdate, + AutoLinking: idp_api.AutoLinkingOptionToPb(idp.AutoLinking), + }, } } diff --git a/internal/api/grpc/settings/v2/settings_converter_test.go b/internal/api/grpc/settings/v2/settings_converter_test.go index 40c381986a..937daf6712 100644 --- a/internal/api/grpc/settings/v2/settings_converter_test.go +++ b/internal/api/grpc/settings/v2/settings_converter_test.go @@ -16,6 +16,7 @@ import ( "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/query" + "github.com/zitadel/zitadel/pkg/grpc/idp/v2" "github.com/zitadel/zitadel/pkg/grpc/settings/v2" ) @@ -382,14 +383,24 @@ func Test_lockoutSettingsToPb(t *testing.T) { func Test_identityProvidersToPb(t *testing.T) { arg := []*query.IDPLoginPolicyLink{ { - IDPID: "1", - IDPName: "foo", - IDPType: domain.IDPTypeOIDC, + IDPID: "1", + IDPName: "foo", + IDPType: domain.IDPTypeOIDC, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: domain.AutoLinkingOptionUsername, }, { - IDPID: "2", - IDPName: "bar", - IDPType: domain.IDPTypeGitHub, + IDPID: "2", + IDPName: "bar", + IDPType: domain.IDPTypeGitHub, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: domain.AutoLinkingOptionEmail, }, } want := []*settings.IdentityProvider{ @@ -397,11 +408,25 @@ func Test_identityProvidersToPb(t *testing.T) { Id: "1", Name: "foo", Type: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OIDC, + Options: &idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + }, }, { Id: "2", Name: "bar", Type: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITHUB, + Options: &idp.Options{ + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL, + }, }, } got := identityProvidersToPb(arg) diff --git a/internal/integration/client.go b/internal/integration/client.go index dde8822acd..e16bb1166f 100644 --- a/internal/integration/client.go +++ b/internal/integration/client.go @@ -379,7 +379,18 @@ func (i *Instance) SetUserPassword(ctx context.Context, userID, password string, return resp.GetDetails() } +func (i *Instance) AddProviderToDefaultLoginPolicy(ctx context.Context, id string) { + _, err := i.Client.Admin.AddIDPToLoginPolicy(ctx, &admin.AddIDPToLoginPolicyRequest{ + IdpId: id, + }) + logging.OnError(err).Panic("add provider to default login policy") +} + func (i *Instance) AddGenericOAuthProvider(ctx context.Context, name string) *admin.AddGenericOAuthProviderResponse { + return i.AddGenericOAuthProviderWithOptions(ctx, name, true, true, true, idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME) +} + +func (i *Instance) AddGenericOAuthProviderWithOptions(ctx context.Context, name string, isLinkingAllowed, isCreationAllowed, isAutoCreation bool, autoLinking idp.AutoLinkingOption) *admin.AddGenericOAuthProviderResponse { resp, err := i.Client.Admin.AddGenericOAuthProvider(ctx, &admin.AddGenericOAuthProviderRequest{ Name: name, ClientId: "clientID", @@ -390,11 +401,11 @@ func (i *Instance) AddGenericOAuthProvider(ctx context.Context, name string) *ad Scopes: []string{"openid", "profile", "email"}, IdAttribute: "id", ProviderOptions: &idp.Options{ - IsLinkingAllowed: true, - IsCreationAllowed: true, - IsAutoCreation: true, + IsLinkingAllowed: isLinkingAllowed, + IsCreationAllowed: isCreationAllowed, + IsAutoCreation: isAutoCreation, IsAutoUpdate: true, - AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME, + AutoLinking: autoLinking, }, }) logging.OnError(err).Panic("create generic OAuth idp") diff --git a/internal/query/idp_login_policy_link.go b/internal/query/idp_login_policy_link.go index f257e88ad4..bdc2ef15b1 100644 --- a/internal/query/idp_login_policy_link.go +++ b/internal/query/idp_login_policy_link.go @@ -15,10 +15,15 @@ import ( ) type IDPLoginPolicyLink struct { - IDPID string - IDPName string - IDPType domain.IDPType - OwnerType domain.IdentityProviderType + IDPID string + IDPName string + IDPType domain.IDPType + OwnerType domain.IdentityProviderType + IsCreationAllowed bool + IsLinkingAllowed bool + IsAutoCreation bool + IsAutoUpdate bool + AutoLinking domain.AutoLinkingOption } type IDPLoginPolicyLinks struct { @@ -127,6 +132,11 @@ func prepareIDPLoginPolicyLinksQuery(ctx context.Context, db prepareDatabase, re IDPTemplateNameCol.identifier(), IDPTemplateTypeCol.identifier(), IDPTemplateOwnerTypeCol.identifier(), + IDPTemplateIsCreationAllowedCol.identifier(), + IDPTemplateIsLinkingAllowedCol.identifier(), + IDPTemplateIsAutoCreationCol.identifier(), + IDPTemplateIsAutoUpdateCol.identifier(), + IDPTemplateAutoLinkingCol.identifier(), countColumn.identifier()). From(idpLoginPolicyLinkTable.identifier()). LeftJoin(join(IDPTemplateIDCol, IDPLoginPolicyLinkIDPIDCol)). @@ -141,29 +151,60 @@ func prepareIDPLoginPolicyLinksQuery(ctx context.Context, db prepareDatabase, re var count uint64 for rows.Next() { var ( - idpName = sql.NullString{} - idpType = sql.NullInt16{} - idpOwnerType = sql.NullInt16{} - link = new(IDPLoginPolicyLink) + idpName = sql.NullString{} + idpType = sql.NullInt16{} + idpOwnerType = sql.NullInt16{} + link = new(IDPLoginPolicyLink) + isCreationAllowed = sql.NullBool{} + isLinkingAllowed = sql.NullBool{} + isAutoCreation = sql.NullBool{} + isAutoUpdate = sql.NullBool{} + autoLinking = sql.NullInt16{} ) err := rows.Scan( &link.IDPID, &idpName, &idpType, &idpOwnerType, + &isCreationAllowed, + &isLinkingAllowed, + &isAutoCreation, + &isAutoUpdate, + &autoLinking, &count, ) if err != nil { return nil, err } - link.IDPName = idpName.String + if idpName.Valid { + link.IDPName = idpName.String + } //IDPType 0 is oidc so we have to set unspecified manually if idpType.Valid { link.IDPType = domain.IDPType(idpType.Int16) } else { link.IDPType = domain.IDPTypeUnspecified } - link.OwnerType = domain.IdentityProviderType(idpOwnerType.Int16) + if idpOwnerType.Valid { + link.OwnerType = domain.IdentityProviderType(idpOwnerType.Int16) + } + if isCreationAllowed.Valid { + link.IsCreationAllowed = isCreationAllowed.Bool + } + if isLinkingAllowed.Valid { + link.IsLinkingAllowed = isLinkingAllowed.Bool + } + if isAutoCreation.Valid { + link.IsAutoCreation = isAutoCreation.Bool + } + if isAutoUpdate.Valid { + link.IsAutoUpdate = isAutoUpdate.Bool + } + if autoLinking.Valid { + link.AutoLinking = domain.AutoLinkingOption(autoLinking.Int16) + } else { + link.AutoLinking = domain.AutoLinkingOptionUnspecified + } links = append(links, link) } diff --git a/internal/query/idp_login_policy_link_test.go b/internal/query/idp_login_policy_link_test.go index 45cb8e594d..245eb22ccc 100644 --- a/internal/query/idp_login_policy_link_test.go +++ b/internal/query/idp_login_policy_link_test.go @@ -19,6 +19,11 @@ var ( ` projections.idp_templates6.name,` + ` projections.idp_templates6.type,` + ` projections.idp_templates6.owner_type,` + + ` projections.idp_templates6.is_creation_allowed,` + + ` projections.idp_templates6.is_linking_allowed,` + + ` projections.idp_templates6.is_auto_creation,` + + ` projections.idp_templates6.is_auto_update,` + + ` projections.idp_templates6.auto_linking,` + ` COUNT(*) OVER ()` + ` FROM projections.idp_login_policy_links5` + ` LEFT JOIN projections.idp_templates6 ON projections.idp_login_policy_links5.idp_id = projections.idp_templates6.id AND projections.idp_login_policy_links5.instance_id = projections.idp_templates6.instance_id` + @@ -31,6 +36,11 @@ var ( "name", "type", "owner_type", + "is_creation_allowed", + "is_linking_allowed", + "is_auto_creation", + "is_auto_update", + "auto_linking", "count", } ) @@ -61,6 +71,11 @@ func Test_IDPLoginPolicyLinkPrepares(t *testing.T) { "idp-name", domain.IDPTypeJWT, domain.IdentityProviderTypeSystem, + true, + true, + true, + true, + domain.AutoLinkingOptionUsername, }, }, ), @@ -71,10 +86,15 @@ func Test_IDPLoginPolicyLinkPrepares(t *testing.T) { }, Links: []*IDPLoginPolicyLink{ { - IDPID: "idp-id", - IDPName: "idp-name", - IDPType: domain.IDPTypeJWT, - OwnerType: domain.IdentityProviderTypeSystem, + IDPID: "idp-id", + IDPName: "idp-name", + IDPType: domain.IDPTypeJWT, + OwnerType: domain.IdentityProviderTypeSystem, + IsCreationAllowed: true, + IsLinkingAllowed: true, + IsAutoCreation: true, + IsAutoUpdate: true, + AutoLinking: domain.AutoLinkingOptionUsername, }, }, }, @@ -94,6 +114,11 @@ func Test_IDPLoginPolicyLinkPrepares(t *testing.T) { nil, nil, nil, + false, + false, + false, + false, + 0, }, }, ), @@ -104,9 +129,14 @@ func Test_IDPLoginPolicyLinkPrepares(t *testing.T) { }, Links: []*IDPLoginPolicyLink{ { - IDPID: "idp-id", - IDPName: "", - IDPType: domain.IDPTypeUnspecified, + IDPID: "idp-id", + IDPName: "", + IDPType: domain.IDPTypeUnspecified, + IsCreationAllowed: false, + IsLinkingAllowed: false, + IsAutoCreation: false, + IsAutoUpdate: false, + AutoLinking: domain.AutoLinkingOptionUnspecified, }, }, }, diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go index e3250f1ae7..dd36cee196 100644 --- a/internal/query/idp_template.go +++ b/internal/query/idp_template.go @@ -825,6 +825,22 @@ func NewIDPTemplateResourceOwnerListSearchQuery(ids ...string) (SearchQuery, err return NewListQuery(IDPTemplateResourceOwnerCol, list, ListIn) } +func NewIDPTemplateIsCreationAllowedSearchQuery(value bool) (SearchQuery, error) { + return NewBoolQuery(IDPTemplateIsCreationAllowedCol, value) +} + +func NewIDPTemplateIsLinkingAllowedSearchQuery(value bool) (SearchQuery, error) { + return NewBoolQuery(IDPTemplateIsLinkingAllowedCol, value) +} + +func NewIDPTemplateIsAutoCreationSearchQuery(value bool) (SearchQuery, error) { + return NewBoolQuery(IDPTemplateIsAutoCreationCol, value) +} + +func NewIDPTemplateAutoLinkingSearchQuery(value int, method NumberComparison) (SearchQuery, error) { + return NewNumberQuery(IDPTemplateAutoLinkingCol, value, method) +} + func (q *IDPTemplateSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder { query = q.SearchRequest.toQuery(query) for _, q := range q.Queries { diff --git a/proto/zitadel/settings/v2/login_settings.proto b/proto/zitadel/settings/v2/login_settings.proto index 9fdbb45993..2b5194f0b5 100644 --- a/proto/zitadel/settings/v2/login_settings.proto +++ b/proto/zitadel/settings/v2/login_settings.proto @@ -6,6 +6,7 @@ option go_package = "github.com/zitadel/zitadel/pkg/grpc/settings/v2;settings"; import "protoc-gen-openapiv2/options/annotations.proto"; import "zitadel/settings/v2/settings.proto"; +import "zitadel/idp/v2/idp.proto"; import "google/protobuf/duration.proto"; message LoginSettings { @@ -134,6 +135,7 @@ message IdentityProvider { string id = 1; string name = 2; IdentityProviderType type = 3; + zitadel.idp.v2.Options options = 4; } enum IdentityProviderType { diff --git a/proto/zitadel/settings/v2/settings_service.proto b/proto/zitadel/settings/v2/settings_service.proto index cc8e5d05cc..77c20eb1c6 100644 --- a/proto/zitadel/settings/v2/settings_service.proto +++ b/proto/zitadel/settings/v2/settings_service.proto @@ -324,7 +324,7 @@ service SettingsService { }; } -// Get the security settings + // Get the security settings rpc GetSecuritySettings(GetSecuritySettingsRequest) returns (GetSecuritySettingsResponse) { option (google.api.http) = { get: "/v2/settings/security"; @@ -343,7 +343,7 @@ service SettingsService { }; } -// Set the security settings + // Set the security settings rpc SetSecuritySettings(SetSecuritySettingsRequest) returns (SetSecuritySettingsResponse) { option (google.api.http) = { put: "/v2/policies/security"; @@ -429,6 +429,10 @@ message GetLockoutSettingsResponse { message GetActiveIdentityProvidersRequest { zitadel.object.v2.RequestContext ctx = 1; + optional bool creation_allowed = 2; + optional bool linking_allowed = 3; + optional bool auto_creation = 4; + optional bool auto_linking = 5; } message GetActiveIdentityProvidersResponse {