mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:17:32 +00:00
feat(api): new settings service (#5775)
* feat: add v2alpha policies service * feat: add v2alpha policies service * fix: rename of attributes and messages in v2alpha api * fix: rename of attributes and messages in v2alpha api * fix: linter corrections * fix: review corrections * fix: review corrections * fix: review corrections * fix: review corrections * fix grpc * refactor: rename to settings and more * Apply suggestions from code review Co-authored-by: Fabi <fabienne.gerschwiler@gmail.com> * add service to docs and rename legal settings * unit tests for converters * go mod tidy * ensure idp name and return list details * fix: use correct resource owner for active idps * change query to join --------- Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Fabi <fabienne.gerschwiler@gmail.com> Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
This commit is contained in:
57
internal/api/grpc/settings/v2/server.go
Normal file
57
internal/api/grpc/settings/v2/server.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/assets"
|
||||
"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/query"
|
||||
settings "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha"
|
||||
)
|
||||
|
||||
var _ settings.SettingsServiceServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
settings.UnimplementedSettingsServiceServer
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
assetsAPIDomain func(context.Context) string
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
externalSecure bool,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
query: query,
|
||||
assetsAPIDomain: assets.AssetAPI(externalSecure),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
settings.RegisterSettingsServiceServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return settings.SettingsService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return settings.SettingsService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return settings.SettingsService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return settings.RegisterSettingsServiceHandler
|
||||
}
|
129
internal/api/grpc/settings/v2/settings.go
Normal file
129
internal/api/grpc/settings/v2/settings.go
Normal file
@@ -0,0 +1,129 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/text"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
object_pb "github.com/zitadel/zitadel/pkg/grpc/object/v2alpha"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha"
|
||||
)
|
||||
|
||||
func (s *Server) GetLoginSettings(ctx context.Context, req *settings.GetLoginSettingsRequest) (*settings.GetLoginSettingsResponse, error) {
|
||||
current, err := s.query.LoginPolicyByID(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settings.GetLoginSettingsResponse{
|
||||
Settings: loginSettingsToPb(current),
|
||||
Details: &object_pb.Details{
|
||||
Sequence: current.Sequence,
|
||||
ChangeDate: timestamppb.New(current.ChangeDate),
|
||||
ResourceOwner: current.OrgID,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetPasswordComplexitySettings(ctx context.Context, req *settings.GetPasswordComplexitySettingsRequest) (*settings.GetPasswordComplexitySettingsResponse, error) {
|
||||
current, err := s.query.PasswordComplexityPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settings.GetPasswordComplexitySettingsResponse{
|
||||
Settings: passwordSettingsToPb(current),
|
||||
Details: &object_pb.Details{
|
||||
Sequence: current.Sequence,
|
||||
ChangeDate: timestamppb.New(current.ChangeDate),
|
||||
ResourceOwner: current.ResourceOwner,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetBrandingSettings(ctx context.Context, req *settings.GetBrandingSettingsRequest) (*settings.GetBrandingSettingsResponse, error) {
|
||||
current, err := s.query.ActiveLabelPolicyByOrg(ctx, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settings.GetBrandingSettingsResponse{
|
||||
Settings: brandingSettingsToPb(current, s.assetsAPIDomain(ctx)),
|
||||
Details: &object_pb.Details{
|
||||
Sequence: current.Sequence,
|
||||
ChangeDate: timestamppb.New(current.ChangeDate),
|
||||
ResourceOwner: current.ResourceOwner,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetDomainSettings(ctx context.Context, req *settings.GetDomainSettingsRequest) (*settings.GetDomainSettingsResponse, error) {
|
||||
current, err := s.query.DomainPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settings.GetDomainSettingsResponse{
|
||||
Settings: domainSettingsToPb(current),
|
||||
Details: &object_pb.Details{
|
||||
Sequence: current.Sequence,
|
||||
ChangeDate: timestamppb.New(current.ChangeDate),
|
||||
ResourceOwner: current.ResourceOwner,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetLegalAndSupportSettings(ctx context.Context, req *settings.GetLegalAndSupportSettingsRequest) (*settings.GetLegalAndSupportSettingsResponse, error) {
|
||||
current, err := s.query.PrivacyPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settings.GetLegalAndSupportSettingsResponse{
|
||||
Settings: legalAndSupportSettingsToPb(current),
|
||||
Details: &object_pb.Details{
|
||||
Sequence: current.Sequence,
|
||||
ChangeDate: timestamppb.New(current.ChangeDate),
|
||||
ResourceOwner: current.ResourceOwner,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetLockoutSettings(ctx context.Context, req *settings.GetLockoutSettingsRequest) (*settings.GetLockoutSettingsResponse, error) {
|
||||
current, err := s.query.LockoutPolicyByOrg(ctx, true, object.ResourceOwnerFromReq(ctx, req.GetCtx()), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &settings.GetLockoutSettingsResponse{
|
||||
Settings: lockoutSettingsToPb(current),
|
||||
Details: &object_pb.Details{
|
||||
Sequence: current.Sequence,
|
||||
ChangeDate: timestamppb.New(current.ChangeDate),
|
||||
ResourceOwner: current.ResourceOwner,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &settings.GetActiveIdentityProvidersResponse{
|
||||
Details: object.ToListDetails(links.SearchResponse),
|
||||
IdentityProviders: identityProvidersToPb(links.Links),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) GetGeneralSettings(ctx context.Context, _ *settings.GetGeneralSettingsRequest) (*settings.GetGeneralSettingsResponse, error) {
|
||||
langs, err := s.query.Languages(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
instance := authz.GetInstance(ctx)
|
||||
return &settings.GetGeneralSettingsResponse{
|
||||
SupportedLanguages: text.LanguageTagsToStrings(langs),
|
||||
DefaultOrgId: instance.DefaultOrganisationID(),
|
||||
DefaultLanguage: instance.DefaultLanguage().String(),
|
||||
}, nil
|
||||
}
|
189
internal/api/grpc/settings/v2/settings_converter.go
Normal file
189
internal/api/grpc/settings/v2/settings_converter.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
settings "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha"
|
||||
)
|
||||
|
||||
// TODO: ?
|
||||
func loginSettingsToPb(current *query.LoginPolicy) *settings.LoginSettings {
|
||||
multi := make([]settings.MultiFactorType, len(current.MultiFactors))
|
||||
for i, typ := range current.MultiFactors {
|
||||
multi[i] = multiFactorTypeToPb(typ)
|
||||
}
|
||||
second := make([]settings.SecondFactorType, len(current.SecondFactors))
|
||||
for i, typ := range current.SecondFactors {
|
||||
second[i] = secondFactorTypeToPb(typ)
|
||||
}
|
||||
|
||||
return &settings.LoginSettings{
|
||||
AllowUsernamePassword: current.AllowUsernamePassword,
|
||||
AllowRegister: current.AllowRegister,
|
||||
AllowExternalIdp: current.AllowExternalIDPs,
|
||||
ForceMfa: current.ForceMFA,
|
||||
PasskeysType: passkeysTypeToPb(current.PasswordlessType),
|
||||
HidePasswordReset: current.HidePasswordReset,
|
||||
IgnoreUnknownUsernames: current.IgnoreUnknownUsernames,
|
||||
AllowDomainDiscovery: current.AllowDomainDiscovery,
|
||||
DisableLoginWithEmail: current.DisableLoginWithEmail,
|
||||
DisableLoginWithPhone: current.DisableLoginWithPhone,
|
||||
DefaultRedirectUri: current.DefaultRedirectURI,
|
||||
PasswordCheckLifetime: durationpb.New(current.PasswordCheckLifetime),
|
||||
ExternalLoginCheckLifetime: durationpb.New(current.ExternalLoginCheckLifetime),
|
||||
MfaInitSkipLifetime: durationpb.New(current.MFAInitSkipLifetime),
|
||||
SecondFactorCheckLifetime: durationpb.New(current.SecondFactorCheckLifetime),
|
||||
MultiFactorCheckLifetime: durationpb.New(current.MultiFactorCheckLifetime),
|
||||
SecondFactors: second,
|
||||
MultiFactors: multi,
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
}
|
||||
}
|
||||
|
||||
func isDefaultToResourceOwnerTypePb(isDefault bool) settings.ResourceOwnerType {
|
||||
if isDefault {
|
||||
return settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE
|
||||
}
|
||||
return settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_ORG
|
||||
}
|
||||
|
||||
func passkeysTypeToPb(passwordlessType domain.PasswordlessType) settings.PasskeysType {
|
||||
switch passwordlessType {
|
||||
case domain.PasswordlessTypeAllowed:
|
||||
return settings.PasskeysType_PASSKEYS_TYPE_ALLOWED
|
||||
case domain.PasswordlessTypeNotAllowed:
|
||||
return settings.PasskeysType_PASSKEYS_TYPE_NOT_ALLOWED
|
||||
default:
|
||||
return settings.PasskeysType_PASSKEYS_TYPE_NOT_ALLOWED
|
||||
}
|
||||
}
|
||||
|
||||
func secondFactorTypeToPb(secondFactorType domain.SecondFactorType) settings.SecondFactorType {
|
||||
switch secondFactorType {
|
||||
case domain.SecondFactorTypeOTP:
|
||||
return settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP
|
||||
case domain.SecondFactorTypeU2F:
|
||||
return settings.SecondFactorType_SECOND_FACTOR_TYPE_U2F
|
||||
case domain.SecondFactorTypeUnspecified:
|
||||
return settings.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED
|
||||
default:
|
||||
return settings.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func multiFactorTypeToPb(typ domain.MultiFactorType) settings.MultiFactorType {
|
||||
switch typ {
|
||||
case domain.MultiFactorTypeU2FWithPIN:
|
||||
return settings.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION
|
||||
case domain.MultiFactorTypeUnspecified:
|
||||
return settings.MultiFactorType_MULTI_FACTOR_TYPE_UNSPECIFIED
|
||||
default:
|
||||
return settings.MultiFactorType_MULTI_FACTOR_TYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func passwordSettingsToPb(current *query.PasswordComplexityPolicy) *settings.PasswordComplexitySettings {
|
||||
return &settings.PasswordComplexitySettings{
|
||||
MinLength: current.MinLength,
|
||||
RequiresUppercase: current.HasUppercase,
|
||||
RequiresLowercase: current.HasLowercase,
|
||||
RequiresNumber: current.HasNumber,
|
||||
RequiresSymbol: current.HasSymbol,
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
}
|
||||
}
|
||||
|
||||
func brandingSettingsToPb(current *query.LabelPolicy, assetPrefix string) *settings.BrandingSettings {
|
||||
return &settings.BrandingSettings{
|
||||
LightTheme: themeToPb(current.Light, assetPrefix, current.ResourceOwner),
|
||||
DarkTheme: themeToPb(current.Dark, assetPrefix, current.ResourceOwner),
|
||||
FontUrl: domain.AssetURL(assetPrefix, current.ResourceOwner, current.FontURL),
|
||||
DisableWatermark: current.WatermarkDisabled,
|
||||
HideLoginNameSuffix: current.HideLoginNameSuffix,
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
}
|
||||
}
|
||||
|
||||
func themeToPb(theme query.Theme, assetPrefix, resourceOwner string) *settings.Theme {
|
||||
return &settings.Theme{
|
||||
PrimaryColor: theme.PrimaryColor,
|
||||
BackgroundColor: theme.BackgroundColor,
|
||||
FontColor: theme.FontColor,
|
||||
WarnColor: theme.WarnColor,
|
||||
LogoUrl: domain.AssetURL(assetPrefix, resourceOwner, theme.LogoURL),
|
||||
IconUrl: domain.AssetURL(assetPrefix, resourceOwner, theme.IconURL),
|
||||
}
|
||||
}
|
||||
|
||||
func domainSettingsToPb(current *query.DomainPolicy) *settings.DomainSettings {
|
||||
return &settings.DomainSettings{
|
||||
LoginNameIncludesDomain: current.UserLoginMustBeDomain,
|
||||
RequireOrgDomainVerification: current.ValidateOrgDomains,
|
||||
SmtpSenderAddressMatchesInstanceDomain: current.SMTPSenderAddressMatchesInstanceDomain,
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
}
|
||||
}
|
||||
|
||||
func legalAndSupportSettingsToPb(current *query.PrivacyPolicy) *settings.LegalAndSupportSettings {
|
||||
return &settings.LegalAndSupportSettings{
|
||||
TosLink: current.TOSLink,
|
||||
PrivacyPolicyLink: current.PrivacyLink,
|
||||
HelpLink: current.HelpLink,
|
||||
SupportEmail: string(current.SupportEmail),
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
}
|
||||
}
|
||||
|
||||
func lockoutSettingsToPb(current *query.LockoutPolicy) *settings.LockoutSettings {
|
||||
return &settings.LockoutSettings{
|
||||
MaxPasswordAttempts: current.MaxPasswordAttempts,
|
||||
ResourceOwnerType: isDefaultToResourceOwnerTypePb(current.IsDefault),
|
||||
}
|
||||
}
|
||||
|
||||
func identityProvidersToPb(idps []*query.IDPLoginPolicyLink) []*settings.IdentityProvider {
|
||||
providers := make([]*settings.IdentityProvider, len(idps))
|
||||
for i, idp := range idps {
|
||||
providers[i] = identityProviderToPb(idp)
|
||||
}
|
||||
return providers
|
||||
}
|
||||
|
||||
func identityProviderToPb(idp *query.IDPLoginPolicyLink) *settings.IdentityProvider {
|
||||
return &settings.IdentityProvider{
|
||||
Id: idp.IDPID,
|
||||
Name: domain.IDPName(idp.IDPName, idp.IDPType),
|
||||
Type: idpTypeToPb(idp.IDPType),
|
||||
}
|
||||
}
|
||||
|
||||
func idpTypeToPb(idpType domain.IDPType) settings.IdentityProviderType {
|
||||
switch idpType {
|
||||
case domain.IDPTypeUnspecified:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_UNSPECIFIED
|
||||
case domain.IDPTypeOIDC:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OIDC
|
||||
case domain.IDPTypeJWT:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_JWT
|
||||
case domain.IDPTypeOAuth:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OAUTH
|
||||
case domain.IDPTypeLDAP:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_LDAP
|
||||
case domain.IDPTypeAzureAD:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_AZURE_AD
|
||||
case domain.IDPTypeGitHub:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITHUB
|
||||
case domain.IDPTypeGitHubEnterprise:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITHUB_ES
|
||||
case domain.IDPTypeGitLab:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITLAB
|
||||
case domain.IDPTypeGitLabSelfHosted:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED
|
||||
case domain.IDPTypeGoogle:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GOOGLE
|
||||
default:
|
||||
return settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
461
internal/api/grpc/settings/v2/settings_converter_test.go
Normal file
461
internal/api/grpc/settings/v2/settings_converter_test.go
Normal file
@@ -0,0 +1,461 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
settings "github.com/zitadel/zitadel/pkg/grpc/settings/v2alpha"
|
||||
)
|
||||
|
||||
var ignoreMessageTypes = map[protoreflect.FullName]bool{
|
||||
"google.protobuf.Duration": true,
|
||||
}
|
||||
|
||||
// allFieldsSet recusively checks if all values in a message
|
||||
// have a non-zero value.
|
||||
func allFieldsSet(t testing.TB, msg protoreflect.Message) {
|
||||
md := msg.Descriptor()
|
||||
name := md.FullName()
|
||||
if ignoreMessageTypes[name] {
|
||||
return
|
||||
}
|
||||
|
||||
fields := md.Fields()
|
||||
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
fd := fields.Get(i)
|
||||
if !msg.Has(fd) {
|
||||
t.Errorf("not all fields set in %q, missing %q", name, fd.Name())
|
||||
continue
|
||||
}
|
||||
|
||||
if fd.Kind() == protoreflect.MessageKind {
|
||||
allFieldsSet(t, msg.Get(fd).Message())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_loginSettingsToPb(t *testing.T) {
|
||||
arg := &query.LoginPolicy{
|
||||
AllowUsernamePassword: true,
|
||||
AllowRegister: true,
|
||||
AllowExternalIDPs: true,
|
||||
ForceMFA: true,
|
||||
PasswordlessType: domain.PasswordlessTypeAllowed,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
AllowDomainDiscovery: true,
|
||||
DisableLoginWithEmail: true,
|
||||
DisableLoginWithPhone: true,
|
||||
DefaultRedirectURI: "example.com",
|
||||
PasswordCheckLifetime: time.Hour,
|
||||
ExternalLoginCheckLifetime: time.Minute,
|
||||
MFAInitSkipLifetime: time.Millisecond,
|
||||
SecondFactorCheckLifetime: time.Microsecond,
|
||||
MultiFactorCheckLifetime: time.Nanosecond,
|
||||
SecondFactors: []domain.SecondFactorType{
|
||||
domain.SecondFactorTypeOTP,
|
||||
domain.SecondFactorTypeU2F,
|
||||
},
|
||||
MultiFactors: []domain.MultiFactorType{
|
||||
domain.MultiFactorTypeU2FWithPIN,
|
||||
},
|
||||
IsDefault: true,
|
||||
}
|
||||
|
||||
want := &settings.LoginSettings{
|
||||
AllowUsernamePassword: true,
|
||||
AllowRegister: true,
|
||||
AllowExternalIdp: true,
|
||||
ForceMfa: true,
|
||||
PasskeysType: settings.PasskeysType_PASSKEYS_TYPE_ALLOWED,
|
||||
HidePasswordReset: true,
|
||||
IgnoreUnknownUsernames: true,
|
||||
AllowDomainDiscovery: true,
|
||||
DisableLoginWithEmail: true,
|
||||
DisableLoginWithPhone: true,
|
||||
DefaultRedirectUri: "example.com",
|
||||
PasswordCheckLifetime: durationpb.New(time.Hour),
|
||||
ExternalLoginCheckLifetime: durationpb.New(time.Minute),
|
||||
MfaInitSkipLifetime: durationpb.New(time.Millisecond),
|
||||
SecondFactorCheckLifetime: durationpb.New(time.Microsecond),
|
||||
MultiFactorCheckLifetime: durationpb.New(time.Nanosecond),
|
||||
SecondFactors: []settings.SecondFactorType{
|
||||
settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP,
|
||||
settings.SecondFactorType_SECOND_FACTOR_TYPE_U2F,
|
||||
},
|
||||
MultiFactors: []settings.MultiFactorType{
|
||||
settings.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION,
|
||||
},
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
}
|
||||
|
||||
got := loginSettingsToPb(arg)
|
||||
allFieldsSet(t, got.ProtoReflect())
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("loginSettingsToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isDefaultToResourceOwnerTypePb(t *testing.T) {
|
||||
type args struct {
|
||||
isDefault bool
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want settings.ResourceOwnerType
|
||||
}{
|
||||
{
|
||||
args: args{false},
|
||||
want: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_ORG,
|
||||
},
|
||||
{
|
||||
args: args{true},
|
||||
want: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want.String(), func(t *testing.T) {
|
||||
got := isDefaultToResourceOwnerTypePb(tt.args.isDefault)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_passkeysTypeToPb(t *testing.T) {
|
||||
type args struct {
|
||||
passwordlessType domain.PasswordlessType
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want settings.PasskeysType
|
||||
}{
|
||||
{
|
||||
args: args{domain.PasswordlessTypeNotAllowed},
|
||||
want: settings.PasskeysType_PASSKEYS_TYPE_NOT_ALLOWED,
|
||||
},
|
||||
{
|
||||
args: args{domain.PasswordlessTypeAllowed},
|
||||
want: settings.PasskeysType_PASSKEYS_TYPE_ALLOWED,
|
||||
},
|
||||
{
|
||||
args: args{99},
|
||||
want: settings.PasskeysType_PASSKEYS_TYPE_NOT_ALLOWED,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want.String(), func(t *testing.T) {
|
||||
got := passkeysTypeToPb(tt.args.passwordlessType)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_secondFactorTypeToPb(t *testing.T) {
|
||||
type args struct {
|
||||
secondFactorType domain.SecondFactorType
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want settings.SecondFactorType
|
||||
}{
|
||||
{
|
||||
args: args{domain.SecondFactorTypeOTP},
|
||||
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_OTP,
|
||||
},
|
||||
{
|
||||
args: args{domain.SecondFactorTypeU2F},
|
||||
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_U2F,
|
||||
},
|
||||
{
|
||||
args: args{domain.SecondFactorTypeUnspecified},
|
||||
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED,
|
||||
},
|
||||
{
|
||||
args: args{99},
|
||||
want: settings.SecondFactorType_SECOND_FACTOR_TYPE_UNSPECIFIED,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want.String(), func(t *testing.T) {
|
||||
got := secondFactorTypeToPb(tt.args.secondFactorType)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_multiFactorTypeToPb(t *testing.T) {
|
||||
type args struct {
|
||||
typ domain.MultiFactorType
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want settings.MultiFactorType
|
||||
}{
|
||||
{
|
||||
args: args{domain.MultiFactorTypeU2FWithPIN},
|
||||
want: settings.MultiFactorType_MULTI_FACTOR_TYPE_U2F_WITH_VERIFICATION,
|
||||
},
|
||||
{
|
||||
args: args{domain.MultiFactorTypeUnspecified},
|
||||
want: settings.MultiFactorType_MULTI_FACTOR_TYPE_UNSPECIFIED,
|
||||
},
|
||||
{
|
||||
args: args{99},
|
||||
want: settings.MultiFactorType_MULTI_FACTOR_TYPE_UNSPECIFIED,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want.String(), func(t *testing.T) {
|
||||
got := multiFactorTypeToPb(tt.args.typ)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_passwordSettingsToPb(t *testing.T) {
|
||||
arg := &query.PasswordComplexityPolicy{
|
||||
MinLength: 12,
|
||||
HasUppercase: true,
|
||||
HasLowercase: true,
|
||||
HasNumber: true,
|
||||
HasSymbol: true,
|
||||
IsDefault: true,
|
||||
}
|
||||
want := &settings.PasswordComplexitySettings{
|
||||
MinLength: 12,
|
||||
RequiresUppercase: true,
|
||||
RequiresLowercase: true,
|
||||
RequiresNumber: true,
|
||||
RequiresSymbol: true,
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
}
|
||||
|
||||
got := passwordSettingsToPb(arg)
|
||||
allFieldsSet(t, got.ProtoReflect())
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("passwordSettingsToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_brandingSettingsToPb(t *testing.T) {
|
||||
arg := &query.LabelPolicy{
|
||||
Light: query.Theme{
|
||||
PrimaryColor: "red",
|
||||
WarnColor: "white",
|
||||
BackgroundColor: "blue",
|
||||
FontColor: "orange",
|
||||
LogoURL: "light-logo",
|
||||
IconURL: "light-icon",
|
||||
},
|
||||
Dark: query.Theme{
|
||||
PrimaryColor: "magenta",
|
||||
WarnColor: "pink",
|
||||
BackgroundColor: "black",
|
||||
FontColor: "white",
|
||||
LogoURL: "dark-logo",
|
||||
IconURL: "dark-icon",
|
||||
},
|
||||
ResourceOwner: "me",
|
||||
FontURL: "fonts",
|
||||
WatermarkDisabled: true,
|
||||
HideLoginNameSuffix: true,
|
||||
IsDefault: true,
|
||||
}
|
||||
want := &settings.BrandingSettings{
|
||||
LightTheme: &settings.Theme{
|
||||
PrimaryColor: "red",
|
||||
WarnColor: "white",
|
||||
BackgroundColor: "blue",
|
||||
FontColor: "orange",
|
||||
LogoUrl: "http://example.com/me/light-logo",
|
||||
IconUrl: "http://example.com/me/light-icon",
|
||||
},
|
||||
DarkTheme: &settings.Theme{
|
||||
PrimaryColor: "magenta",
|
||||
WarnColor: "pink",
|
||||
BackgroundColor: "black",
|
||||
FontColor: "white",
|
||||
LogoUrl: "http://example.com/me/dark-logo",
|
||||
IconUrl: "http://example.com/me/dark-icon",
|
||||
},
|
||||
FontUrl: "http://example.com/me/fonts",
|
||||
DisableWatermark: true,
|
||||
HideLoginNameSuffix: true,
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
}
|
||||
|
||||
got := brandingSettingsToPb(arg, "http://example.com")
|
||||
allFieldsSet(t, got.ProtoReflect())
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("brandingSettingsToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_domainSettingsToPb(t *testing.T) {
|
||||
arg := &query.DomainPolicy{
|
||||
UserLoginMustBeDomain: true,
|
||||
ValidateOrgDomains: true,
|
||||
SMTPSenderAddressMatchesInstanceDomain: true,
|
||||
IsDefault: true,
|
||||
}
|
||||
want := &settings.DomainSettings{
|
||||
LoginNameIncludesDomain: true,
|
||||
RequireOrgDomainVerification: true,
|
||||
SmtpSenderAddressMatchesInstanceDomain: true,
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
}
|
||||
got := domainSettingsToPb(arg)
|
||||
allFieldsSet(t, got.ProtoReflect())
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("domainSettingsToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_legalSettingsToPb(t *testing.T) {
|
||||
arg := &query.PrivacyPolicy{
|
||||
TOSLink: "http://example.com/tos",
|
||||
PrivacyLink: "http://example.com/pricacy",
|
||||
HelpLink: "http://example.com/help",
|
||||
SupportEmail: "support@zitadel.com",
|
||||
IsDefault: true,
|
||||
}
|
||||
want := &settings.LegalAndSupportSettings{
|
||||
TosLink: "http://example.com/tos",
|
||||
PrivacyPolicyLink: "http://example.com/pricacy",
|
||||
HelpLink: "http://example.com/help",
|
||||
SupportEmail: "support@zitadel.com",
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
}
|
||||
got := legalAndSupportSettingsToPb(arg)
|
||||
allFieldsSet(t, got.ProtoReflect())
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("legalSettingsToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_lockoutSettingsToPb(t *testing.T) {
|
||||
arg := &query.LockoutPolicy{
|
||||
MaxPasswordAttempts: 22,
|
||||
IsDefault: true,
|
||||
}
|
||||
want := &settings.LockoutSettings{
|
||||
MaxPasswordAttempts: 22,
|
||||
ResourceOwnerType: settings.ResourceOwnerType_RESOURCE_OWNER_TYPE_INSTANCE,
|
||||
}
|
||||
got := lockoutSettingsToPb(arg)
|
||||
allFieldsSet(t, got.ProtoReflect())
|
||||
if !proto.Equal(got, want) {
|
||||
t.Errorf("lockoutSettingsToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_identityProvidersToPb(t *testing.T) {
|
||||
arg := []*query.IDPLoginPolicyLink{
|
||||
{
|
||||
IDPID: "1",
|
||||
IDPName: "foo",
|
||||
IDPType: domain.IDPTypeOIDC,
|
||||
},
|
||||
{
|
||||
IDPID: "2",
|
||||
IDPName: "bar",
|
||||
IDPType: domain.IDPTypeGitHub,
|
||||
},
|
||||
}
|
||||
want := []*settings.IdentityProvider{
|
||||
{
|
||||
Id: "1",
|
||||
Name: "foo",
|
||||
Type: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OIDC,
|
||||
},
|
||||
{
|
||||
Id: "2",
|
||||
Name: "bar",
|
||||
Type: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITHUB,
|
||||
},
|
||||
}
|
||||
got := identityProvidersToPb(arg)
|
||||
require.Len(t, got, len(got))
|
||||
for i, v := range got {
|
||||
allFieldsSet(t, v.ProtoReflect())
|
||||
if !proto.Equal(v, want[i]) {
|
||||
t.Errorf("identityProvidersToPb() =\n%v\nwant\n%v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_idpTypeToPb(t *testing.T) {
|
||||
type args struct {
|
||||
idpType domain.IDPType
|
||||
}
|
||||
tests := []struct {
|
||||
args args
|
||||
want settings.IdentityProviderType
|
||||
}{
|
||||
{
|
||||
args: args{domain.IDPTypeUnspecified},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_UNSPECIFIED,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeOIDC},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OIDC,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeJWT},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_JWT,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeOAuth},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_OAUTH,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeLDAP},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_LDAP,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeAzureAD},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_AZURE_AD,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeGitHub},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITHUB,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeGitHubEnterprise},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITHUB_ES,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeGitLab},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITLAB,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeGitLabSelfHosted},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GITLAB_SELF_HOSTED,
|
||||
},
|
||||
{
|
||||
args: args{domain.IDPTypeGoogle},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_GOOGLE,
|
||||
},
|
||||
{
|
||||
args: args{99},
|
||||
want: settings.IdentityProviderType_IDENTITY_PROVIDER_TYPE_UNSPECIFIED,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.want.String(), func(t *testing.T) {
|
||||
if got := idpTypeToPb(tt.args.idpType); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("idpTypeToPb() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user