From f4fa3ecef3ad064cefcebdde109dff9ceb6fba13 Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Wed, 18 Aug 2021 12:58:57 +0200 Subject: [PATCH] fix: Custom text split features (#2225) * fix: separate tier of custom text (message and login) * fix: add migration * fix: build problems * fix: tests * Update internal/api/grpc/admin/features.go Co-authored-by: Livio Amstutz * Update internal/api/grpc/admin/features.go Co-authored-by: Livio Amstutz * fix: rename sql file * fix: change sql files * fix: console * fix: console Co-authored-by: Livio Amstutz --- .../login-texts/login-texts.component.html | 10 +- .../message-texts.component.html | 10 +- console/src/assets/i18n/de.json | 2 +- docs/docs/apis/proto/admin.md | 4 + internal/api/grpc/admin/features.go | 6 +- internal/api/grpc/features/features.go | 4 +- .../eventstore/token_verifier.go | 10 +- internal/command/custom_login_text_model.go | 22 +++++ internal/command/features_model.go | 12 ++- internal/command/iam_features.go | 3 +- internal/command/iam_features_model.go | 14 ++- internal/command/org_custom_login_text.go | 15 +++ .../command/org_custom_login_text_model.go | 65 +++++++++++++ internal/command/org_custom_message_model.go | 10 ++ internal/command/org_features.go | 20 +++- internal/command/org_features_model.go | 14 ++- internal/command/org_features_test.go | 93 ++++++++++++++++++- internal/domain/custom_message_text.go | 9 ++ internal/domain/features.go | 7 +- internal/features/model/features_view.go | 12 ++- .../repository/view/model/features.go | 6 +- internal/repository/features/features.go | 22 +++-- .../cockroach/V1.62__custom_text_feature.sql | 9 ++ proto/zitadel/admin.proto | 4 + proto/zitadel/features.proto | 2 + proto/zitadel/management.proto | 14 +-- 26 files changed, 339 insertions(+), 60 deletions(-) create mode 100644 migrations/cockroach/V1.62__custom_text_feature.sql diff --git a/console/src/app/modules/policies/login-texts/login-texts.component.html b/console/src/app/modules/policies/login-texts/login-texts.component.html index 042335c50..1f24a6386 100644 --- a/console/src/app/modules/policies/login-texts/login-texts.component.html +++ b/console/src/app/modules/policies/login-texts/login-texts.component.html @@ -36,23 +36,23 @@ {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'custom_text'})}} + 'custom_text.login'})}}
-
- -
diff --git a/console/src/app/modules/policies/message-texts/message-texts.component.html b/console/src/app/modules/policies/message-texts/message-texts.component.html index 7193de77e..be77861f6 100644 --- a/console/src/app/modules/policies/message-texts/message-texts.component.html +++ b/console/src/app/modules/policies/message-texts/message-texts.component.html @@ -20,21 +20,21 @@ {{'FEATURES.NOTAVAILABLE' | translate: ({value: - 'custom_text'})}} + 'custom_text.message'})}}
-
- -
diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index ed2a6f501..02054a8d4 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -660,7 +660,7 @@ "2": "Annuliert", "3": "Durch IAM Owner gesetzt" }, - "NOTAVAILABLE": "Feature {{value}} ist auf Ihrer organisation nicht freigeschaltet!", + "NOTAVAILABLE": "Feature {{value}} ist auf Ihrer Organisation nicht freigeschaltet!", "RETENTIONDAYS": "Tage" }, "POLICY": { diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md index 1397afb0c..1b2c76665 100644 --- a/docs/docs/apis/proto/admin.md +++ b/docs/docs/apis/proto/admin.md @@ -2505,6 +2505,8 @@ This is an empty request | custom_text | bool | - | | | privacy_policy | bool | - | | | metadata_user | bool | - | | +| custom_text_message | bool | - | | +| custom_text_login | bool | - | | @@ -2691,6 +2693,8 @@ This is an empty request | custom_text | bool | - | | | privacy_policy | bool | - | | | metadata_user | bool | - | | +| custom_text_message | bool | - | | +| custom_text_login | bool | - | | diff --git a/internal/api/grpc/admin/features.go b/internal/api/grpc/admin/features.go index 55ba275d2..ae74ec734 100644 --- a/internal/api/grpc/admin/features.go +++ b/internal/api/grpc/admin/features.go @@ -74,9 +74,10 @@ func setDefaultFeaturesRequestToDomain(req *admin_pb.SetDefaultFeaturesRequest) LabelPolicyPrivateLabel: req.LabelPolicy || req.LabelPolicyPrivateLabel, LabelPolicyWatermark: req.LabelPolicyWatermark, CustomDomain: req.CustomDomain, - CustomText: req.CustomText, PrivacyPolicy: req.PrivacyPolicy, MetadataUser: req.MetadataUser, + CustomTextLogin: req.CustomTextLogin || req.CustomText, + CustomTextMessage: req.CustomTextMessage, } } @@ -97,8 +98,9 @@ func setOrgFeaturesRequestToDomain(req *admin_pb.SetOrgFeaturesRequest) *domain. LabelPolicyPrivateLabel: req.LabelPolicy || req.LabelPolicyPrivateLabel, LabelPolicyWatermark: req.LabelPolicyWatermark, CustomDomain: req.CustomDomain, - CustomText: req.CustomText, PrivacyPolicy: req.PrivacyPolicy, MetadataUser: req.MetadataUser, + CustomTextLogin: req.CustomTextLogin || req.CustomText, + CustomTextMessage: req.CustomTextMessage, } } diff --git a/internal/api/grpc/features/features.go b/internal/api/grpc/features/features.go index 3b9a78dcd..d2692a908 100644 --- a/internal/api/grpc/features/features.go +++ b/internal/api/grpc/features/features.go @@ -27,8 +27,10 @@ func FeaturesFromModel(features *features_model.FeaturesView) *features_pb.Featu CustomDomain: features.CustomDomain, LabelPolicyPrivateLabel: features.LabelPolicyPrivateLabel, LabelPolicyWatermark: features.LabelPolicyWatermark, - CustomText: features.CustomText, PrivacyPolicy: features.PrivacyPolicy, + CustomText: features.CustomTextMessage, + CustomTextMessage: features.CustomTextMessage, + CustomTextLogin: features.CustomTextLogin, } } diff --git a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go index 06d042297..123c95c16 100644 --- a/internal/authz/repository/eventsourcing/eventstore/token_verifier.go +++ b/internal/authz/repository/eventsourcing/eventstore/token_verifier.go @@ -151,8 +151,14 @@ func checkFeatures(features *features_view_model.FeaturesView, requiredFeatures } continue } - if requiredFeature == domain.FeatureCustomText { - if !features.CustomText { + if requiredFeature == domain.FeatureCustomTextMessage { + if !features.CustomTextMessage { + return MissingFeatureErr(requiredFeature) + } + continue + } + if requiredFeature == domain.FeatureCustomTextLogin { + if !features.CustomTextLogin { return MissingFeatureErr(requiredFeature) } continue diff --git a/internal/command/custom_login_text_model.go b/internal/command/custom_login_text_model.go index 2a5381402..9869e15de 100644 --- a/internal/command/custom_login_text_model.go +++ b/internal/command/custom_login_text_model.go @@ -10,6 +10,25 @@ import ( "github.com/caos/zitadel/internal/repository/policy" ) +type CustomLoginTextsReadModel struct { + eventstore.WriteModel + CustomLoginTexts map[string]*CustomText +} + +func (wm *CustomLoginTextsReadModel) Reduce() error { + for _, event := range wm.Events { + switch e := event.(type) { + case *policy.CustomTextSetEvent: + wm.CustomLoginTexts[e.Template+e.Language.String()] = &CustomText{Language: e.Language, Template: e.Template} + case *policy.CustomTextTemplateRemovedEvent: + if _, ok := wm.CustomLoginTexts[e.Template+e.Language.String()]; ok { + delete(wm.CustomLoginTexts, e.Template) + } + } + } + return wm.WriteModel.Reduce() +} + type CustomLoginTextReadModel struct { eventstore.WriteModel @@ -558,6 +577,9 @@ func (wm *CustomLoginTextReadModel) Reduce() error { continue } case *policy.CustomTextTemplateRemovedEvent: + if e.Template != domain.LoginCustomText { + continue + } wm.State = domain.PolicyStateRemoved } } diff --git a/internal/command/features_model.go b/internal/command/features_model.go index 82df49d78..d8471a59c 100644 --- a/internal/command/features_model.go +++ b/internal/command/features_model.go @@ -26,9 +26,10 @@ type FeaturesWriteModel struct { LabelPolicyPrivateLabel bool LabelPolicyWatermark bool CustomDomain bool - CustomText bool PrivacyPolicy bool MetadataUser bool + CustomTextMessage bool + CustomTextLogin bool } func (wm *FeaturesWriteModel) Reduce() error { @@ -84,12 +85,15 @@ func (wm *FeaturesWriteModel) Reduce() error { if e.CustomDomain != nil { wm.CustomDomain = *e.CustomDomain } - if e.CustomText != nil { - wm.CustomText = *e.CustomText - } if e.MetadataUser != nil { wm.MetadataUser = *e.MetadataUser } + if e.CustomTextMessage != nil { + wm.CustomTextMessage = *e.CustomTextMessage + } + if e.CustomTextLogin != nil { + wm.CustomTextLogin = *e.CustomTextLogin + } case *features.FeaturesRemovedEvent: wm.State = domain.FeaturesStateRemoved } diff --git a/internal/command/iam_features.go b/internal/command/iam_features.go index fcfdc3bbf..526005533 100644 --- a/internal/command/iam_features.go +++ b/internal/command/iam_features.go @@ -49,9 +49,10 @@ func (c *Commands) setDefaultFeatures(ctx context.Context, existingFeatures *IAM features.LabelPolicyPrivateLabel, features.LabelPolicyWatermark, features.CustomDomain, - features.CustomText, features.PrivacyPolicy, features.MetadataUser, + features.CustomTextMessage, + features.CustomTextLogin, ) if !hasChanged { return nil, caos_errs.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged") diff --git a/internal/command/iam_features_model.go b/internal/command/iam_features_model.go index 38718c86b..4e90206e7 100644 --- a/internal/command/iam_features_model.go +++ b/internal/command/iam_features_model.go @@ -67,9 +67,10 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent( labelPolicyPrivateLabel, labelPolicyWatermark, customDomain, - customText, privacyPolicy, - metadataUser bool, + metadataUser, + customTextMessage, + customTextLogin bool, ) (*iam.FeaturesSetEvent, bool) { changes := make([]features.FeaturesChanges, 0) @@ -116,15 +117,18 @@ func (wm *IAMFeaturesWriteModel) NewSetEvent( if wm.CustomDomain != customDomain { changes = append(changes, features.ChangeCustomDomain(customDomain)) } - if wm.CustomText != customText { - changes = append(changes, features.ChangeCustomText(customText)) - } if wm.PrivacyPolicy != privacyPolicy { changes = append(changes, features.ChangePrivacyPolicy(privacyPolicy)) } if wm.MetadataUser != metadataUser { changes = append(changes, features.ChangeMetadataUser(metadataUser)) } + if wm.CustomTextMessage != customTextMessage { + changes = append(changes, features.ChangeCustomTextMessage(customTextMessage)) + } + if wm.CustomTextLogin != customTextLogin { + changes = append(changes, features.ChangeCustomTextLogin(customTextLogin)) + } if len(changes) == 0 { return nil, false } diff --git a/internal/command/org_custom_login_text.go b/internal/command/org_custom_login_text.go index eafdf01c9..1b02668c7 100644 --- a/internal/command/org_custom_login_text.go +++ b/internal/command/org_custom_login_text.go @@ -67,6 +67,21 @@ func (c *Commands) RemoveOrgLoginTexts(ctx context.Context, resourceOwner string return writeModelToObjectDetails(&customText.WriteModel), nil } +func (c *Commands) removeOrgLoginTextsIfExists(ctx context.Context, orgID string) ([]eventstore.EventPusher, error) { + msgTemplates := NewOrgCustomLoginTextsReadModel(orgID) + err := c.eventstore.FilterToQueryReducer(ctx, msgTemplates) + if err != nil { + return nil, err + } + + orgAgg := OrgAggregateFromWriteModel(&msgTemplates.WriteModel) + events := make([]eventstore.EventPusher, 0, len(msgTemplates.CustomLoginTexts)) + for _, tmpl := range msgTemplates.CustomLoginTexts { + events = append(events, org.NewCustomTextTemplateRemovedEvent(ctx, orgAgg, tmpl.Template, tmpl.Language)) + } + return events, nil +} + func (c *Commands) orgCustomLoginTextWriteModelByID(ctx context.Context, orgID string, lang language.Tag) (*OrgCustomLoginTextReadModel, error) { writeModel := NewOrgCustomLoginTextReadModel(orgID, lang) err := c.eventstore.FilterToQueryReducer(ctx, writeModel) diff --git a/internal/command/org_custom_login_text_model.go b/internal/command/org_custom_login_text_model.go index a7fdc6516..15ca17af5 100644 --- a/internal/command/org_custom_login_text_model.go +++ b/internal/command/org_custom_login_text_model.go @@ -3,6 +3,7 @@ package command import ( "golang.org/x/text/language" + "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/repository/org" ) @@ -27,10 +28,19 @@ func (wm *OrgCustomLoginTextReadModel) AppendEvents(events ...eventstore.EventRe for _, event := range events { switch e := event.(type) { case *org.CustomTextSetEvent: + if e.Template != domain.LoginCustomText { + continue + } wm.CustomLoginTextReadModel.AppendEvents(&e.CustomTextSetEvent) case *org.CustomTextRemovedEvent: + if e.Template != domain.LoginCustomText { + continue + } wm.CustomLoginTextReadModel.AppendEvents(&e.CustomTextRemovedEvent) case *org.CustomTextTemplateRemovedEvent: + if e.Template != domain.LoginCustomText { + continue + } wm.CustomLoginTextReadModel.AppendEvents(&e.CustomTextTemplateRemovedEvent) } } @@ -52,3 +62,58 @@ func (wm *OrgCustomLoginTextReadModel) Query() *eventstore.SearchQueryBuilder { org.CustomTextTemplateRemovedEventType). Builder() } + +type OrgCustomLoginTextsReadModel struct { + CustomLoginTextsReadModel +} + +func NewOrgCustomLoginTextsReadModel(orgID string) *OrgCustomLoginTextsReadModel { + return &OrgCustomLoginTextsReadModel{ + CustomLoginTextsReadModel{ + WriteModel: eventstore.WriteModel{ + AggregateID: orgID, + ResourceOwner: orgID, + }, + CustomLoginTexts: make(map[string]*CustomText), + }, + } +} + +func (wm *OrgCustomLoginTextsReadModel) AppendEvents(events ...eventstore.EventReader) { + for _, event := range events { + switch e := event.(type) { + case *org.CustomTextSetEvent: + if e.Template != domain.LoginCustomText { + continue + } + wm.CustomLoginTextsReadModel.AppendEvents(&e.CustomTextSetEvent) + case *org.CustomTextRemovedEvent: + if e.Template != domain.LoginCustomText { + continue + } + wm.CustomLoginTextsReadModel.AppendEvents(&e.CustomTextRemovedEvent) + case *org.CustomTextTemplateRemovedEvent: + if e.Template != domain.LoginCustomText { + continue + } + wm.CustomLoginTextsReadModel.AppendEvents(&e.CustomTextTemplateRemovedEvent) + } + } +} + +func (wm *OrgCustomLoginTextsReadModel) Reduce() error { + return wm.CustomLoginTextsReadModel.Reduce() +} + +func (wm *OrgCustomLoginTextsReadModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). + ResourceOwner(wm.ResourceOwner). + AddQuery(). + AggregateIDs(wm.CustomLoginTextsReadModel.AggregateID). + AggregateTypes(org.AggregateType). + EventTypes( + org.CustomTextSetEventType, + org.CustomTextRemovedEventType, + org.CustomTextTemplateRemovedEventType). + Builder() +} diff --git a/internal/command/org_custom_message_model.go b/internal/command/org_custom_message_model.go index 6ee0faa41..86f2766ae 100644 --- a/internal/command/org_custom_message_model.go +++ b/internal/command/org_custom_message_model.go @@ -3,6 +3,7 @@ package command import ( "golang.org/x/text/language" + "github.com/caos/zitadel/internal/domain" "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/repository/org" ) @@ -74,10 +75,19 @@ func (wm *OrgCustomMessageTemplatesReadModel) AppendEvents(events ...eventstore. for _, event := range events { switch e := event.(type) { case *org.CustomTextSetEvent: + if !domain.IsMessageTextType(e.Template) { + continue + } wm.CustomMessageTemplatesReadModel.AppendEvents(&e.CustomTextSetEvent) case *org.CustomTextRemovedEvent: + if !domain.IsMessageTextType(e.Template) { + continue + } wm.CustomMessageTemplatesReadModel.AppendEvents(&e.CustomTextRemovedEvent) case *org.CustomTextTemplateRemovedEvent: + if !domain.IsMessageTextType(e.Template) { + continue + } wm.CustomMessageTemplatesReadModel.AppendEvents(&e.CustomTextTemplateRemovedEvent) } } diff --git a/internal/command/org_features.go b/internal/command/org_features.go index ba2602635..7ce095358 100644 --- a/internal/command/org_features.go +++ b/internal/command/org_features.go @@ -40,9 +40,10 @@ func (c *Commands) SetOrgFeatures(ctx context.Context, resourceOwner string, fea features.LabelPolicyPrivateLabel, features.LabelPolicyWatermark, features.CustomDomain, - features.CustomText, features.PrivacyPolicy, features.MetadataUser, + features.CustomTextMessage, + features.CustomTextLogin, ) if !hasChanged { return nil, caos_errs.ThrowPreconditionFailed(nil, "Features-GE4h2", "Errors.Features.NotChanged") @@ -129,13 +130,22 @@ func (c *Commands) ensureOrgSettingsToFeatures(ctx context.Context, orgID string events = append(events, removeCustomDomainsEvents...) } } - if !features.CustomText { - removeCustomTextEvents, err := c.removeOrgMessageTextsIfExists(ctx, orgID) + if !features.CustomTextMessage { + removeCustomMessageTextEvents, err := c.removeOrgMessageTextsIfExists(ctx, orgID) if err != nil { return nil, err } - if removeCustomTextEvents != nil { - events = append(events, removeCustomTextEvents...) + if removeCustomMessageTextEvents != nil { + events = append(events, removeCustomMessageTextEvents...) + } + } + if !features.CustomTextLogin { + removeCustomLoginTextEvents, err := c.removeOrgLoginTextsIfExists(ctx, orgID) + if err != nil { + return nil, err + } + if removeCustomLoginTextEvents != nil { + events = append(events, removeCustomLoginTextEvents...) } } if !features.PrivacyPolicy { diff --git a/internal/command/org_features_model.go b/internal/command/org_features_model.go index 0bda8172c..2086f897a 100644 --- a/internal/command/org_features_model.go +++ b/internal/command/org_features_model.go @@ -74,9 +74,10 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent( labelPolicyPrivateLabel, labelPolicyWatermark, customDomain, - customText, privacyPolicy, - metadataUser bool, + metadataUser, + customTextMessage, + customTextLogin bool, ) (*org.FeaturesSetEvent, bool) { changes := make([]features.FeaturesChanges, 0) @@ -126,15 +127,18 @@ func (wm *OrgFeaturesWriteModel) NewSetEvent( if wm.CustomDomain != customDomain { changes = append(changes, features.ChangeCustomDomain(customDomain)) } - if wm.CustomText != customText { - changes = append(changes, features.ChangeCustomText(customText)) - } if wm.PrivacyPolicy != privacyPolicy { changes = append(changes, features.ChangePrivacyPolicy(privacyPolicy)) } if wm.MetadataUser != metadataUser { changes = append(changes, features.ChangeMetadataUser(metadataUser)) } + if wm.CustomTextMessage != customTextMessage { + changes = append(changes, features.ChangeCustomTextMessage(customTextMessage)) + } + if wm.CustomTextLogin != customTextLogin { + changes = append(changes, features.ChangeCustomTextLogin(customTextLogin)) + } if len(changes) == 0 { return nil, false diff --git a/internal/command/org_features_test.go b/internal/command/org_features_test.go index 53f004b29..405d0caf6 100644 --- a/internal/command/org_features_test.go +++ b/internal/command/org_features_test.go @@ -242,6 +242,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + iam.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( iam.NewPrivacyPolicyAddedEvent( @@ -280,7 +292,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { LabelPolicyPrivateLabel: false, LabelPolicyWatermark: false, CustomDomain: false, - CustomText: false, + CustomTextMessage: false, + CustomTextLogin: false, PrivacyPolicy: false, MetadataUser: false, }, @@ -415,6 +428,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + iam.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( iam.NewPrivacyPolicyAddedEvent( @@ -600,6 +625,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + iam.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( iam.NewPrivacyPolicyAddedEvent( @@ -795,6 +832,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + iam.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( iam.NewPrivacyPolicyAddedEvent( @@ -1045,6 +1094,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + org.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( org.NewPrivacyPolicyAddedEvent( @@ -1079,6 +1140,9 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { eventFromEventPusher( org.NewCustomTextTemplateRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.InitCodeMessageType, language.English), ), + eventFromEventPusher( + org.NewCustomTextTemplateRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate, domain.LoginCustomText, language.English), + ), eventFromEventPusher( org.NewPrivacyPolicyRemovedEvent(context.Background(), &org.NewAggregate("org1", "org1").Aggregate), ), @@ -1219,6 +1283,18 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + iam.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( iam.NewPrivacyPolicyAddedEvent( @@ -1269,7 +1345,8 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) { LabelPolicyPrivateLabel: false, LabelPolicyWatermark: false, CustomDomain: false, - CustomText: false, + CustomTextMessage: false, + CustomTextLogin: false, PrivacyPolicy: false, MetadataUser: false, }, @@ -1452,6 +1529,18 @@ func TestCommandSide_RemoveOrgFeatures(t *testing.T) { ), ), ), + expectFilter( + eventFromEventPusher( + iam.NewCustomTextSetEvent( + context.Background(), + &iam.NewAggregate().Aggregate, + domain.LoginCustomText, + domain.LoginKeyExternalRegistrationUserOverviewTitle, + "text", + language.English, + ), + ), + ), expectFilter( eventFromEventPusher( iam.NewPrivacyPolicyAddedEvent( diff --git a/internal/domain/custom_message_text.go b/internal/domain/custom_message_text.go index 0309d5f74..d2a14b28d 100644 --- a/internal/domain/custom_message_text.go +++ b/internal/domain/custom_message_text.go @@ -68,3 +68,12 @@ func (m *MessageTexts) GetMessageTextByType(msgType string) *CustomMessageText { } return nil } + +func IsMessageTextType(textType string) bool { + return textType == InitCodeMessageType || + textType == PasswordResetMessageType || + textType == VerifyEmailMessageType || + textType == VerifyPhoneMessageType || + textType == DomainClaimedMessageType || + textType == PasswordlessRegistrationMessageType +} diff --git a/internal/domain/features.go b/internal/domain/features.go index 1ca4eb71e..8ffddd04f 100644 --- a/internal/domain/features.go +++ b/internal/domain/features.go @@ -18,10 +18,12 @@ const ( FeatureLabelPolicy = "label_policy" FeatureLabelPolicyPrivateLabel = FeatureLabelPolicy + ".private_label" FeatureLabelPolicyWatermark = FeatureLabelPolicy + ".watermark" - FeatureCustomText = "custom_text" FeatureCustomDomain = "custom_domain" FeaturePrivacyPolicy = "privacy_policy" FeatureMetadata = "metadata" + FeatureCustomText = "custom_text" + FeatureCustomTextMessage = FeatureCustomText + ".message" + FeatureCustomTextLogin = FeatureCustomText + ".login" FeatureMetadataUser = FeatureMetadata + ".user" ) @@ -45,7 +47,8 @@ type Features struct { LabelPolicyPrivateLabel bool LabelPolicyWatermark bool CustomDomain bool - CustomText bool + CustomTextMessage bool + CustomTextLogin bool PrivacyPolicy bool MetadataUser bool } diff --git a/internal/features/model/features_view.go b/internal/features/model/features_view.go index cd0f2f191..4a435cdcf 100644 --- a/internal/features/model/features_view.go +++ b/internal/features/model/features_view.go @@ -28,9 +28,10 @@ type FeaturesView struct { LabelPolicyPrivateLabel bool LabelPolicyWatermark bool CustomDomain bool - CustomText bool PrivacyPolicy bool MetadataUser bool + CustomTextMessage bool + CustomTextLogin bool } func (f *FeaturesView) FeatureList() []string { @@ -65,15 +66,18 @@ func (f *FeaturesView) FeatureList() []string { if f.CustomDomain { list = append(list, domain.FeatureCustomDomain) } - if f.CustomText { - list = append(list, domain.FeatureCustomText) - } if f.PrivacyPolicy { list = append(list, domain.FeaturePrivacyPolicy) } if f.MetadataUser { list = append(list, domain.FeatureMetadataUser) } + if f.CustomTextMessage { + list = append(list, domain.FeatureCustomTextMessage) + } + if f.CustomTextLogin { + list = append(list, domain.FeatureCustomTextLogin) + } return list } diff --git a/internal/features/repository/view/model/features.go b/internal/features/repository/view/model/features.go index e1779d004..b3efde1ff 100644 --- a/internal/features/repository/view/model/features.go +++ b/internal/features/repository/view/model/features.go @@ -42,9 +42,10 @@ type FeaturesView struct { LabelPolicyPrivateLabel bool `json:"labelPolicyPrivateLabel" gorm:"column:label_policy_private_label"` LabelPolicyWatermark bool `json:"labelPolicyWatermark" gorm:"column:label_policy_watermark"` CustomDomain bool `json:"customDomain" gorm:"column:custom_domain"` - CustomText bool `json:"customText" gorm:"column:custom_text"` PrivacyPolicy bool `json:"privacyPolicy" gorm:"column:privacy_policy"` MetadataUser bool `json:"metadataUser" gorm:"column:metadata_user"` + CustomTextMessage bool `json:"customTextMessage" gorm:"column:custom_text_message"` + CustomTextLogin bool `json:"customTextLogin" gorm:"column:custom_text_login"` } func FeaturesToModel(features *FeaturesView) *features_model.FeaturesView { @@ -69,9 +70,10 @@ func FeaturesToModel(features *FeaturesView) *features_model.FeaturesView { LabelPolicyPrivateLabel: features.LabelPolicyPrivateLabel, LabelPolicyWatermark: features.LabelPolicyWatermark, CustomDomain: features.CustomDomain, - CustomText: features.CustomText, PrivacyPolicy: features.PrivacyPolicy, MetadataUser: features.MetadataUser, + CustomTextMessage: features.CustomTextMessage, + CustomTextLogin: features.CustomTextLogin, } } diff --git a/internal/repository/features/features.go b/internal/repository/features/features.go index 32dd8f3d7..1355b8e31 100644 --- a/internal/repository/features/features.go +++ b/internal/repository/features/features.go @@ -35,9 +35,10 @@ type FeaturesSetEvent struct { LabelPolicyPrivateLabel *bool `json:"labelPolicyPrivateLabel,omitempty"` LabelPolicyWatermark *bool `json:"labelPolicyWatermark,omitempty"` CustomDomain *bool `json:"customDomain,omitempty"` - CustomText *bool `json:"customText,omitempty"` PrivacyPolicy *bool `json:"privacyPolicy,omitempty"` MetadataUser *bool `json:"metadataUser,omitempty"` + CustomTextMessage *bool `json:"customTextMessage,omitempty"` + CustomTextLogin *bool `json:"customTextLogin,omitempty"` } func (e *FeaturesSetEvent) Data() interface{} { @@ -156,12 +157,6 @@ func ChangeCustomDomain(customDomain bool) func(event *FeaturesSetEvent) { } } -func ChangeCustomText(customText bool) func(event *FeaturesSetEvent) { - return func(e *FeaturesSetEvent) { - e.CustomText = &customText - } -} - func ChangePrivacyPolicy(privacyPolicy bool) func(event *FeaturesSetEvent) { return func(e *FeaturesSetEvent) { e.PrivacyPolicy = &privacyPolicy @@ -173,6 +168,19 @@ func ChangeMetadataUser(metadataUser bool) func(event *FeaturesSetEvent) { e.MetadataUser = &metadataUser } } + +func ChangeCustomTextMessage(customTextMessage bool) func(event *FeaturesSetEvent) { + return func(e *FeaturesSetEvent) { + e.CustomTextMessage = &customTextMessage + } +} + +func ChangeCustomTextLogin(customTextLogin bool) func(event *FeaturesSetEvent) { + return func(e *FeaturesSetEvent) { + e.CustomTextLogin = &customTextLogin + } +} + func FeaturesSetEventMapper(event *repository.Event) (eventstore.EventReader, error) { e := &FeaturesSetEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), diff --git a/migrations/cockroach/V1.62__custom_text_feature.sql b/migrations/cockroach/V1.62__custom_text_feature.sql new file mode 100644 index 000000000..51256c4fb --- /dev/null +++ b/migrations/cockroach/V1.62__custom_text_feature.sql @@ -0,0 +1,9 @@ +ALTER TABLE adminapi.features RENAME COLUMN custom_text TO custom_text_message; +ALTER TABLE auth.features RENAME COLUMN custom_text TO custom_text_message; +ALTER TABLE authz.features RENAME COLUMN custom_text TO custom_text_message; +ALTER TABLE management.features RENAME COLUMN custom_text TO custom_text_message; + +ALTER TABLE adminapi.features ADD COLUMN custom_text_login BOOLEAN; +ALTER TABLE auth.features ADD COLUMN custom_text_login BOOLEAN; +ALTER TABLE authz.features ADD COLUMN custom_text_login BOOLEAN; +ALTER TABLE management.features ADD COLUMN custom_text_login BOOLEAN; \ No newline at end of file diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto index 3143979de..5800b523b 100644 --- a/proto/zitadel/admin.proto +++ b/proto/zitadel/admin.proto @@ -2613,6 +2613,8 @@ message SetDefaultFeaturesRequest { bool custom_text = 17; bool privacy_policy = 18; bool metadata_user = 19; + bool custom_text_message = 20; + bool custom_text_login = 21; } message SetDefaultFeaturesResponse { @@ -2649,6 +2651,8 @@ message SetOrgFeaturesRequest { bool custom_text = 18; bool privacy_policy = 19; bool metadata_user = 20; + bool custom_text_message = 21; + bool custom_text_login = 22; } message SetOrgFeaturesResponse { diff --git a/proto/zitadel/features.proto b/proto/zitadel/features.proto index 589193b06..92cdc4469 100644 --- a/proto/zitadel/features.proto +++ b/proto/zitadel/features.proto @@ -27,6 +27,8 @@ message Features { bool custom_text = 16; bool privacy_policy = 17; bool metadata_user = 18; + bool custom_text_message = 19; + bool custom_text_login = 20; } message FeatureTier { diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto index 0ced5cc62..23fcf9203 100644 --- a/proto/zitadel/management.proto +++ b/proto/zitadel/management.proto @@ -2271,7 +2271,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.message" }; } @@ -2319,7 +2319,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.message" }; } @@ -2368,7 +2368,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.message" }; } @@ -2417,7 +2417,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.message" }; } @@ -2466,7 +2466,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.message" }; } @@ -2515,7 +2515,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.message" }; } @@ -2563,7 +2563,7 @@ service ManagementService { option (zitadel.v1.auth_option) = { permission: "policy.write"; - feature: "custom_text" + feature: "custom_text.login" }; }