feat: add Help/Support e-mail for instance/org (#5445)

feat: help and support email in privacy policy
This commit is contained in:
Miguel Cabrerizo 2023-03-28 21:36:52 +02:00 committed by GitHub
parent 12a7c4b994
commit 1b9cea0e0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 572 additions and 187 deletions

View File

@ -499,6 +499,7 @@ DefaultInstance:
TOSLink: https://zitadel.com/docs/legal/terms-of-service
PrivacyLink: https://zitadel.com/docs/legal/privacy-policy
HelpLink: ""
SupportEmail: ""
NotificationPolicy:
PasswordChange: true
LabelPolicy:

View File

@ -73,6 +73,7 @@ export function mapRequestValues(map: Partial<Map>, req: Req): Req {
r3.setHelp(map.footerText?.help ?? '');
r3.setPrivacyPolicy(map.footerText?.privacyPolicy ?? '');
r3.setTos(map.footerText?.tos ?? '');
r3.setSupportEmail(map.footerText?.supportEmail ?? '');
req.setFooterText(r3);
const r4 = new InitMFADoneScreenText();

View File

@ -36,6 +36,12 @@
<input cnslInput name="helpLink" formControlName="helpLink" />
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{ key: 'helpLink' }"></template>
</cnsl-form-field>
<cnsl-form-field class="privacy-policy-formfield">
<cnsl-label>{{ 'POLICY.PRIVACY_POLICY.SUPPORTEMAIL' | translate }}</cnsl-label>
<input cnslInput name="supportEmail" formControlName="supportEmail" />
<template [ngTemplateOutlet]="templateRef" [ngTemplateOutletContext]="{ key: 'supportEmail' }"></template>
</cnsl-form-field>
</form>
</div>

View File

@ -7,9 +7,10 @@
display: grid;
grid-template-columns: 1fr;
column-gap: 1rem;
width: 75%;
@media only screen and (min-width: 800px) {
grid-template-columns: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr;
}
}

View File

@ -60,6 +60,7 @@ export class PrivacyPolicyComponent implements OnInit, OnDestroy {
tosLink: ['', []],
privacyLink: ['', []],
helpLink: ['', []],
supportEmail: ['', []],
});
this.canWrite$.pipe(take(1)).subscribe((canWrite) => {
@ -105,6 +106,7 @@ export class PrivacyPolicyComponent implements OnInit, OnDestroy {
tosLink: '',
privacyLink: '',
helpLink: '',
supportEmail: '',
});
}
})
@ -114,6 +116,7 @@ export class PrivacyPolicyComponent implements OnInit, OnDestroy {
tosLink: '',
privacyLink: '',
helpLink: '',
supportEmail: '',
});
});
}
@ -125,6 +128,7 @@ export class PrivacyPolicyComponent implements OnInit, OnDestroy {
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
req.setHelpLink(this.form.get('helpLink')?.value);
req.setSupportEmail(this.form.get('supportEmail')?.value);
(this.service as ManagementService)
.addCustomPrivacyPolicy(req)
.then(() => {
@ -137,6 +141,7 @@ export class PrivacyPolicyComponent implements OnInit, OnDestroy {
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
req.setHelpLink(this.form.get('helpLink')?.value);
req.setSupportEmail(this.form.get('supportEmail')?.value);
(this.service as ManagementService)
.updateCustomPrivacyPolicy(req)
@ -151,6 +156,7 @@ export class PrivacyPolicyComponent implements OnInit, OnDestroy {
req.setPrivacyLink(this.form.get('privacyLink')?.value);
req.setTosLink(this.form.get('tosLink')?.value);
req.setHelpLink(this.form.get('helpLink')?.value);
req.setSupportEmail(this.form.get('supportEmail')?.value);
(this.service as AdminService)
.updatePrivacyPolicy(req)

View File

@ -53,6 +53,6 @@
<cnsl-login-texts [serviceType]="serviceType"></cnsl-login-texts>
</ng-container>
<ng-container *ngIf="currentSetting === 'privacypolicy'">
<cnsl-privacy-policy [serviceType]="PolicyComponentServiceType.ADMIN"></cnsl-privacy-policy>
<cnsl-privacy-policy [serviceType]="serviceType"></cnsl-privacy-policy>
</ng-container>
</cnsl-sidenav>

View File

@ -1185,6 +1185,7 @@
"TOSLINK": "Link zu den Allgemeinen Geschäftsbedingungen",
"POLICYLINK": "Link zur den Datenschutzrichtlinien",
"HELPLINK": "Link zur Hilfestellung",
"SUPPORTEMAIL": "Support E-Mail",
"SAVED": "Saved successfully!",
"RESET_TITLE": "Standardwerte wiederherstellen",
"RESET_DESCRIPTION": "Sie sind im Begriff die Standardlinks für die AGBs und Datenschutzrichtlinie wiederherzustellen. Wollen Sie fortfahren?"

View File

@ -1186,6 +1186,7 @@
"TOSLINK": "Link to Terms of Service",
"POLICYLINK": "Link to Privacy Policy",
"HELPLINK": "Link to Help",
"SUPPORTEMAIL": "Support Email",
"SAVED": "Saved successfully!",
"RESET_TITLE": "Restore Default Values",
"RESET_DESCRIPTION": "You are about to restore the default Links for TOS and Privacy Policy. Do you really want to continue?"

View File

@ -1185,6 +1185,7 @@
"TOSLINK": "Lien vers les conditions d'utilisation",
"POLICYLINK": "Lien vers la politique de confidentialité",
"HELPLINK": "Lien vers l'aide",
"SUPPORTEMAIL": "E-mail d'assistance",
"SAVED": "Enregistré avec succès !",
"RESET_TITLE": "Restaurer les valeurs par défaut",
"RESET_DESCRIPTION": "Vous êtes sur le point de restaurer les liens par défaut pour les CGS et la politique de confidentialité. Voulez-vous vraiment continuer ?"

View File

@ -8,7 +8,7 @@
},
"FOOTER": {
"LINKS": {
"CONTACT": "Kontakt",
"CONTACT": "Contatto",
"TOS": "Terms of Service",
"PP": "Privacy Policy"
},
@ -1186,6 +1186,7 @@
"TOSLINK": "Link ai termini di servizio",
"POLICYLINK": "Link all'informativa sulla privacy",
"HELPLINK": "link per l'aiuto",
"SUPPORTEMAIL": "e-mail di supporto",
"SAVED": "Salvato con successo!",
"RESET_TITLE": "Ripristina i valori predefiniti",
"RESET_DESCRIPTION": "Stai per ripristinare i link predefiniti per i TOS e l'informativa sulla privacy. Vuoi davvero continuare?"

View File

@ -1185,6 +1185,7 @@
"TOSLINK": "Link do warunków korzystania",
"POLICYLINK": "Link do polityki prywatności",
"HELPLINK": "Link do pomocy",
"SUPPORTEMAIL": "E-mail wsparcia",
"SAVED": "Pomyślnie zapisano!",
"RESET_TITLE": "Przywróć wartości domyślne",
"RESET_DESCRIPTION": "Masz zamiar przywrócić domyślne linki dla TOS i polityki prywatności. Czy na pewno chcesz kontynuować?"

View File

@ -1184,6 +1184,7 @@
"TOSLINK": "链接到服务条款",
"POLICYLINK": "链接到隐私政策",
"HELPLINK": "链接到帮助",
"SUPPORTEMAIL": "支持邮箱",
"SAVED": "保存成功!",
"RESET_TITLE": "恢复默认值",
"RESET_DESCRIPTION": "您即将恢复 TOS 和隐私政策的默认链接。你真的要继续吗?"

View File

@ -57,7 +57,11 @@ At the moment Twilio is available as SMS provider.
You can configure on which changes the users will be notified. The text of the message can be changed in the [Message texts](#message-texts)
<img src="/docs/img/guides/console/notification.png" alt="Notification" width="400px" />
<img
src="/docs/img/guides/console/notification.png"
alt="Notification"
width="400px"
/>
### SMTP
@ -132,7 +136,6 @@ Configure the different lifetimes checks for the login process:
- **Second Factor Check Lifetime** specifies after which period a user has to revalidate the 2-Factor during the login process
- **External Login Check Lifetime** specifies after which period a user has to revalidate the Multi Factor during the login process
## Identity Providers
You can configure all kinds of external identity providers for identity brokering, which support OIDC (OpenID Connect).
@ -190,7 +193,7 @@ You can either set this attribute on your whole ZITADEL instance or just on some
## Privacy Policy and TOS
With this setting you are able to configure your privacy policy, terms of service and help links.
With this setting you are able to configure your privacy policy, terms of service, help links and help/support email address.
On register each user has to accept these policies.
This policy can be also be overriden by your organizations.
@ -231,7 +234,11 @@ You can set the locale of the translations on the right.
These are the texts for the login. Just like for message texts, you can select the locale on the right.
<img src="/docs/img/guides/console/logintexts.png" alt="Login texts" width="600px" />
<img
src="/docs/img/guides/console/logintexts.png"
alt="Login texts"
width="600px"
/>
## OIDC token lifetimes and expiration

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -521,9 +521,10 @@ func (s *Server) getPrivacyPolicy(ctx context.Context, orgID string) (_ *managem
}
if !queriedPrivacy.IsDefault {
return &management_pb.AddCustomPrivacyPolicyRequest{
TosLink: queriedPrivacy.TOSLink,
PrivacyLink: queriedPrivacy.PrivacyLink,
HelpLink: queriedPrivacy.HelpLink,
TosLink: queriedPrivacy.TOSLink,
PrivacyLink: queriedPrivacy.PrivacyLink,
HelpLink: queriedPrivacy.HelpLink,
SupportEmail: string(queriedPrivacy.SupportEmail),
}, nil
}
return nil, nil

View File

@ -7,8 +7,9 @@ import (
func UpdatePrivacyPolicyToDomain(req *admin_pb.UpdatePrivacyPolicyRequest) *domain.PrivacyPolicy {
return &domain.PrivacyPolicy{
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
SupportEmail: domain.EmailAddress(req.SupportEmail),
}
}

View File

@ -7,16 +7,18 @@ import (
func AddPrivacyPolicyToDomain(req *mgmt_pb.AddCustomPrivacyPolicyRequest) *domain.PrivacyPolicy {
return &domain.PrivacyPolicy{
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
SupportEmail: domain.EmailAddress(req.SupportEmail),
}
}
func UpdatePrivacyPolicyToDomain(req *mgmt_pb.UpdateCustomPrivacyPolicyRequest) *domain.PrivacyPolicy {
return &domain.PrivacyPolicy{
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
TOSLink: req.TosLink,
PrivacyLink: req.PrivacyLink,
HelpLink: req.HelpLink,
SupportEmail: domain.EmailAddress(req.SupportEmail),
}
}

View File

@ -8,10 +8,11 @@ import (
func ModelPrivacyPolicyToPb(policy *query.PrivacyPolicy) *policy_pb.PrivacyPolicy {
return &policy_pb.PrivacyPolicy{
IsDefault: policy.IsDefault,
TosLink: policy.TOSLink,
PrivacyLink: policy.PrivacyLink,
HelpLink: policy.HelpLink,
IsDefault: policy.IsDefault,
TosLink: policy.TOSLink,
PrivacyLink: policy.PrivacyLink,
HelpLink: policy.HelpLink,
SupportEmail: string(policy.SupportEmail),
Details: object.ToViewDetailsPb(
policy.Sequence,
policy.CreationDate,

View File

@ -458,6 +458,7 @@ func FooterTextToPb(text domain.FooterText) *text_pb.FooterText {
Tos: text.TOS,
PrivacyPolicy: text.PrivacyPolicy,
Help: text.Help,
SupportEmail: text.SupportEmail,
}
}
@ -949,5 +950,6 @@ func FooterTextPbToDomain(text *text_pb.FooterText) domain.FooterText {
TOS: text.Tos,
PrivacyPolicy: text.PrivacyPolicy,
Help: text.Help,
SupportEmail: text.SupportEmail,
}
}

View File

@ -442,6 +442,9 @@ func (l *Login) setLinksOnBaseData(baseData baseData, privacyPolicy *domain.Priv
if link, err := templates.ParseTemplateText(privacyPolicy.HelpLink, lang); err == nil {
baseData.HelpLink = link
}
if link, err := templates.ParseTemplateText(string(privacyPolicy.SupportEmail), lang); err == nil {
baseData.SupportEmail = link
}
return baseData
}
@ -602,6 +605,7 @@ type baseData struct {
TOSLink string
PrivacyLink string
HelpLink string
SupportEmail string
AuthReqID string
CSRF template.HTML
Nonce string

View File

@ -172,6 +172,7 @@ PasswordChange:
NewPasswordConfirmLabel: Passwort Bestätigung
CancelButtonText: abbrechen
NextButtonText: weiter
Footer: Fusszeile
PasswordChangeDone:
Title: Passwort ändern
@ -318,6 +319,7 @@ Footer:
Tos: AGB
PrivacyPolicy: Datenschutzerklärung
Help: Hilfe
SupportEmail: Support E-Mail
Errors:
Internal: Es ist ein interner Fehler aufgetreten

View File

@ -172,6 +172,7 @@ PasswordChange:
NewPasswordConfirmLabel: Password confirmation
CancelButtonText: cancel
NextButtonText: next
Footer: Footer
PasswordChangeDone:
Title: Change Password
@ -318,6 +319,7 @@ Footer:
Tos: TOS
PrivacyPolicy: Privacy policy
Help: Help
SupportEmail: Support E-mail
Errors:
Internal: An internal error occurred

View File

@ -172,6 +172,7 @@ PasswordChange:
NewPasswordConfirmLabel: Confirmation du mot de passe
CancelButtonText: annuler
NextButtonText: suivant
Footer: Bas de page
PasswordChangeDone:
Title: Changer le mot de passe
@ -318,6 +319,7 @@ Footer:
Tos: TOS
PrivacyPolicy: Politique de confidentialité
Help: Aide
SupportEmail: E-mail d'assistance
Errors:
Internal: Une erreur interne s'est produite
@ -408,8 +410,8 @@ Errors:
ExternalUserIDEmpty: L'ID de l'utilisateur externe est vide
UserDisplayNameEmpty: Le nom d'affichage de l'utilisateur est vide
NoExternalUserData: Aucune donnée d'utilisateur externe reçue
CreationNotAllowed : La création d'un nouvel utilisateur n'est pas autorisée sur ce fournisseur.
LinkingNotAllowed : La création d'un lien vers un utilisateur n'est pas autorisée pour ce fournisseur.
CreationNotAllowed: La création d'un nouvel utilisateur n'est pas autorisée sur ce fournisseur.
LinkingNotAllowed: La création d'un lien vers un utilisateur n'est pas autorisée pour ce fournisseur.
GrantRequired: Connexion impossible. L'utilisateur doit avoir au moins une subvention sur l'application. Veuillez contacter votre administrateur.
ProjectRequired: Connexion impossible. L'organisation de l'utilisateur doit être accordée au projet. Veuillez contacter votre administrateur.
IdentityProvider:

View File

@ -172,6 +172,7 @@ PasswordChange:
NewPasswordConfirmLabel: Conferma della password
CancelButtonText: annulla
NextButtonText: Avanti
Footer: Piè di pagina
PasswordChangeDone:
Title: Reimposta password
@ -318,6 +319,7 @@ Footer:
Tos: Termini di servizio
PrivacyPolicy: l'informativa sulla privacy
Help: Aiuto
SupportEmail: E-mail di supporto
Errors:
Internal: Si è verificato un errore interno

View File

@ -172,6 +172,7 @@ PasswordChange:
NewPasswordConfirmLabel: Potwierdzenie hasła
CancelButtonText: anuluj
NextButtonText: dalej
Footer: Stopka
PasswordChangeDone:
Title: Zmiana hasła
@ -318,6 +319,7 @@ Footer:
Tos: TOS
PrivacyPolicy: Polityka prywatności
Help: Pomoc
SupportEmail: E-mail wsparcia
Errors:
Internal: Wewnętrzny błąd

View File

@ -172,6 +172,7 @@ PasswordChange:
NewPasswordConfirmLabel: 确认密码
CancelButtonText: 取消
NextButtonText: 继续
Footer: 页脚
PasswordChangeDone:
Title: 更改密码
@ -318,6 +319,7 @@ Footer:
Tos: 服务条款
PrivacyPolicy: 隐私政策
Help: 帮助
SupportEmail: 支持邮箱
Errors:
Internal: 发生了内部错误

View File

@ -1,20 +1,41 @@
{{define "footer"}}
<footer>
{{ if hasWatermark .LabelPolicy }}
<span class="watermark" >
<span class="powered">{{t "Footer.PoweredBy"}}</span>
<span class="lgn-logo-watermark" sourcelight="logo-light.svg" sourcedark="logo-dark.svg" alt="logo"></span>
</span>
{{end}}
<span class="fill-space"></span>
{{ if .TOSLink }}
<a href="{{.TOSLink}}" rel="noopener noreferrer" target="_blank" alt="TOS">{{t "Footer.Tos"}}</a>
{{ end }}
{{ if .PrivacyLink }}
<a href="{{.PrivacyLink}}" rel="noopener noreferrer" target="_blank" alt="Privacy Policy">{{t "Footer.PrivacyPolicy"}}</a>
{{end}}
{{ if .HelpLink }}
<a href="{{.HelpLink}}" rel="noopener noreferrer" target="_blank" alt="Help">{{t "Footer.Help"}}</a>
{{end}}
{{ if hasWatermark .LabelPolicy }}
<span class="watermark">
<span class="powered">{{t "Footer.PoweredBy"}}</span>
<span
class="lgn-logo-watermark"
sourcelight="logo-light.svg"
sourcedark="logo-dark.svg"
alt="logo"
></span>
</span>
{{end}}
<span class="fill-space"></span>
{{ if .TOSLink }}
<a href="{{.TOSLink}}" rel="noopener noreferrer" target="_blank" alt="TOS"
>{{t "Footer.Tos"}}</a
>
{{ end }} {{ if .PrivacyLink }}
<a
href="{{.PrivacyLink}}"
rel="noopener noreferrer"
target="_blank"
alt="Privacy Policy"
>{{t "Footer.PrivacyPolicy"}}</a
>
{{end}} {{ if .HelpLink }}
<a href="{{.HelpLink}}" rel="noopener noreferrer" target="_blank" alt="Help"
>{{t "Footer.Help"}}</a
>
{{end}} {{ if .SupportEmail }}
<a
href="mailto:{{.SupportEmail}}"
rel="noopener noreferrer"
target="_blank"
alt="Help"
>{{t "Footer.SupportEmail"}}</a
>
{{end}}
</footer>
{{end}}

View File

@ -1158,6 +1158,7 @@ func privacyPolicyToDomain(p *query.PrivacyPolicy) *domain.PrivacyPolicy {
TOSLink: p.TOSLink,
PrivacyLink: p.PrivacyLink,
HelpLink: p.HelpLink,
SupportEmail: p.SupportEmail,
}
}

View File

@ -1093,6 +1093,10 @@ func (c *Commands) createFooterTextEvents(ctx context.Context, agg *eventstore.A
if event != nil {
events = append(events, event)
}
event = c.createCustomLoginTextEvent(ctx, agg, domain.LoginKeyFooterSupportEmail, existingText.FooterSupportEmail, text.Footer.SupportEmail, text.Language, defaultText)
if event != nil {
events = append(events, event)
}
return events
}

View File

@ -289,7 +289,7 @@ type CustomLoginTextReadModel struct {
FooterTOS string
FooterPrivacyPolicy string
FooterHelp string
FooterHelpLink string
FooterSupportEmail string
}
func (wm *CustomLoginTextReadModel) Reduce() error {
@ -2515,6 +2515,10 @@ func (wm *CustomLoginTextReadModel) handleFooterTextSetEvent(e *policy.CustomTex
wm.FooterHelp = e.Text
return
}
if e.Key == domain.LoginKeyFooterSupportEmail {
wm.FooterSupportEmail = e.Text
return
}
}
func (wm *CustomLoginTextReadModel) handleFooterTextRemoveEvent(e *policy.CustomTextRemovedEvent) {
@ -2530,4 +2534,8 @@ func (wm *CustomLoginTextReadModel) handleFooterTextRemoveEvent(e *policy.Custom
wm.FooterHelp = ""
return
}
if e.Key == domain.LoginKeyFooterSupportEmail {
wm.FooterSupportEmail = ""
return
}
}

View File

@ -87,9 +87,10 @@ type InstanceSetup struct {
PasswordChange bool
}
PrivacyPolicy struct {
TOSLink string
PrivacyLink string
HelpLink string
TOSLink string
PrivacyLink string
HelpLink string
SupportEmail domain.EmailAddress
}
LabelPolicy struct {
PrimaryColor string
@ -242,7 +243,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeU2F),
prepareAddMultiFactorToDefaultLoginPolicy(instanceAgg, domain.MultiFactorTypeU2FWithPIN),
prepareAddDefaultPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink),
prepareAddDefaultPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink, setup.PrivacyPolicy.SupportEmail),
prepareAddDefaultNotificationPolicy(instanceAgg, setup.NotificationPolicy.PasswordChange),
prepareAddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure),

View File

@ -127,6 +127,7 @@ func writeModelToPrivacyPolicy(wm *PrivacyPolicyWriteModel) *domain.PrivacyPolic
TOSLink: wm.TOSLink,
PrivacyLink: wm.PrivacyLink,
HelpLink: wm.HelpLink,
SupportEmail: wm.SupportEmail,
}
}

View File

@ -56,7 +56,6 @@ func (c *Commands) setCustomInstanceLoginText(ctx context.Context, instanceAgg *
if !text.IsValid() {
return nil, nil, caos_errs.ThrowInvalidArgument(nil, "Instance-kd9fs", "Errors.CustomText.Invalid")
}
existingLoginText, err := c.defaultLoginTextWriteModelByID(ctx, text.Language)
if err != nil {
return nil, nil, err

View File

@ -1370,6 +1370,12 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextSetEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
},
),
),
@ -1664,6 +1670,7 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
SupportEmail: "Support Email",
},
},
},
@ -2993,6 +3000,12 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextSetEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -4310,6 +4323,12 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English,
),
),
},
),
),
@ -5681,6 +5700,12 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextSetEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextRemovedEvent(context.Background(),
@ -6996,6 +7021,12 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextRemovedEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -8313,6 +8344,12 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewCustomTextSetEvent(context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
},
),
),
@ -8607,6 +8644,7 @@ func TestCommandSide_SetCustomIAMLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
SupportEmail: "Support Email",
},
},
},

View File

@ -12,9 +12,9 @@ import (
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddDefaultPrivacyPolicy(ctx context.Context, tosLink, privacyLink, helpLink string) (*domain.ObjectDetails, error) {
func (c *Commands) AddDefaultPrivacyPolicy(ctx context.Context, tosLink, privacyLink, helpLink string, supportEmail domain.EmailAddress) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultPrivacyPolicy(instanceAgg, tosLink, privacyLink, helpLink))
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultPrivacyPolicy(instanceAgg, tosLink, privacyLink, helpLink, supportEmail))
if err != nil {
return nil, err
}
@ -26,6 +26,13 @@ func (c *Commands) AddDefaultPrivacyPolicy(ctx context.Context, tosLink, privacy
}
func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) {
if policy.SupportEmail != "" {
if err := policy.SupportEmail.Validate(); err != nil {
return nil, err
}
policy.SupportEmail = policy.SupportEmail.Normalize()
}
existingPolicy, err := c.defaultPrivacyPolicyWriteModelByID(ctx)
if err != nil {
return nil, err
@ -35,7 +42,7 @@ func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domai
}
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.PrivacyPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, instanceAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, instanceAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink, policy.SupportEmail)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "INSTANCE-9jJfs", "Errors.IAM.PrivacyPolicy.NotChanged")
}
@ -81,8 +88,15 @@ func prepareAddDefaultPrivacyPolicy(
tosLink,
privacyLink,
helpLink string,
supportEmail domain.EmailAddress,
) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if supportEmail != "" {
if err := supportEmail.Validate(); err != nil {
return nil, err
}
supportEmail = supportEmail.Normalize()
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
writeModel := NewInstancePrivacyPolicyWriteModel(ctx)
events, err := filter(ctx, writeModel.Query())
@ -97,7 +111,7 @@ func prepareAddDefaultPrivacyPolicy(
return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-M00rJ", "Errors.Instance.PrivacyPolicy.AlreadyExists")
}
return []eventstore.Command{
instance.NewPrivacyPolicyAddedEvent(ctx, &a.Aggregate, tosLink, privacyLink, helpLink),
instance.NewPrivacyPolicyAddedEvent(ctx, &a.Aggregate, tosLink, privacyLink, helpLink, supportEmail),
}, nil
}, nil
}

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/policy"
@ -57,6 +58,7 @@ func (wm *InstancePrivacyPolicyWriteModel) NewChangedEvent(
tosLink,
privacyLink,
helpLink string,
supportEmail domain.EmailAddress,
) (*instance.PrivacyPolicyChangedEvent, bool) {
changes := make([]policy.PrivacyPolicyChanges, 0)
@ -69,6 +71,9 @@ func (wm *InstancePrivacyPolicyWriteModel) NewChangedEvent(
if wm.HelpLink != helpLink {
changes = append(changes, policy.ChangeHelpLink(helpLink))
}
if wm.SupportEmail != supportEmail {
changes = append(changes, policy.ChangeSupportEmail(supportEmail))
}
if len(changes) == 0 {
return nil, false
}

View File

@ -22,10 +22,11 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
tosLink string
privacyLink string
helpLink string
ctx context.Context
tosLink string
privacyLink string
helpLink string
supportEmail domain.EmailAddress
}
type res struct {
want *domain.ObjectDetails
@ -49,16 +50,18 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
),
},
args: args{
ctx: context.Background(),
tosLink: "TOSLink",
privacyLink: "PrivacyLink",
helpLink: "HelpLink",
ctx: context.Background(),
tosLink: "TOSLink",
privacyLink: "PrivacyLink",
helpLink: "HelpLink",
supportEmail: "support@example.com",
},
res: res{
err: caos_errs.IsErrorAlreadyExists,
@ -79,6 +82,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
},
@ -86,10 +90,11 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
tosLink: "TOSLink",
privacyLink: "PrivacyLink",
helpLink: "HelpLink",
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
tosLink: "TOSLink",
privacyLink: "PrivacyLink",
helpLink: "HelpLink",
supportEmail: "support@example.com",
},
res: res{
want: &domain.ObjectDetails{
@ -97,6 +102,24 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
},
},
},
{
name: "wrong email, can't add policy",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
tosLink: "TOSLink",
privacyLink: "PrivacyLink",
helpLink: "HelpLink",
supportEmail: "wrong email",
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "add empty policy,ok",
fields: fields{
@ -112,6 +135,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
"",
"",
"",
"",
),
),
},
@ -119,10 +143,11 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
),
},
args: args{
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
tosLink: "",
privacyLink: "",
helpLink: "",
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
tosLink: "",
privacyLink: "",
helpLink: "",
supportEmail: "",
},
res: res{
want: &domain.ObjectDetails{
@ -136,7 +161,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.AddDefaultPrivacyPolicy(tt.args.ctx, tt.args.tosLink, tt.args.privacyLink, tt.args.helpLink)
got, err := r.AddDefaultPrivacyPolicy(tt.args.ctx, tt.args.tosLink, tt.args.privacyLink, tt.args.helpLink, tt.args.supportEmail)
if tt.res.err == nil {
assert.NoError(t, err)
}
@ -179,9 +204,10 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
@ -200,6 +226,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
@ -208,15 +235,36 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "wrong email, can't change policy",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "wrong email",
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "change, ok",
fields: fields{
@ -229,6 +277,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
@ -239,6 +288,7 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
"TOSLinkChanged",
"PrivacyLinkChanged",
"HelpLinkChanged",
"support2@example.com",
),
),
},
@ -248,9 +298,10 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLinkChanged",
PrivacyLink: "PrivacyLinkChanged",
HelpLink: "HelpLinkChanged",
TOSLink: "TOSLinkChanged",
PrivacyLink: "PrivacyLinkChanged",
HelpLink: "HelpLinkChanged",
SupportEmail: "support2@example.com",
},
},
res: res{
@ -259,9 +310,10 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
AggregateID: "INSTANCE",
ResourceOwner: "INSTANCE",
},
TOSLink: "TOSLinkChanged",
PrivacyLink: "PrivacyLinkChanged",
HelpLink: "HelpLinkChanged",
TOSLink: "TOSLinkChanged",
PrivacyLink: "PrivacyLinkChanged",
HelpLink: "HelpLinkChanged",
SupportEmail: "support2@example.com",
},
},
},
@ -285,13 +337,14 @@ func TestCommandSide_ChangeDefaultPrivacyPolicy(t *testing.T) {
}
}
func newDefaultPrivacyPolicyChangedEvent(ctx context.Context, tosLink, privacyLink, helpLink string) *instance.PrivacyPolicyChangedEvent {
func newDefaultPrivacyPolicyChangedEvent(ctx context.Context, tosLink, privacyLink, helpLink, supportEmail string) *instance.PrivacyPolicyChangedEvent {
event, _ := instance.NewPrivacyPolicyChangedEvent(ctx,
&instance.NewAggregate("INSTANCE").Aggregate,
[]policy.PrivacyPolicyChanges{
policy.ChangeTOSLink(tosLink),
policy.ChangePrivacyLink(privacyLink),
policy.ChangeHelpLink(helpLink),
policy.ChangeSupportEmail(domain.EmailAddress(supportEmail)),
},
)
return event

View File

@ -50,5 +50,6 @@ func orgWriteModelToPrivacyPolicy(wm *OrgPrivacyPolicyWriteModel) *domain.Privac
TOSLink: wm.TOSLink,
PrivacyLink: wm.PrivacyLink,
HelpLink: wm.HelpLink,
SupportEmail: wm.SupportEmail,
}
}

View File

@ -1161,6 +1161,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
},
),
),
@ -1455,6 +1460,7 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
SupportEmail: "Support Email",
},
},
},
@ -2560,6 +2566,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -3653,6 +3664,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusher(
org.NewCustomTextRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English,
),
),
},
),
),
@ -4800,6 +4816,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeySelectAccountTitle, language.English,
@ -5890,6 +5911,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, language.English,
),
),
eventFromEventPusher(
org.NewCustomTextRemovedEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, language.English,
),
),
),
expectPush(
[]*repository.Event{
@ -6983,6 +7009,11 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterHelp, "Help", language.English,
),
),
eventFromEventPusher(
org.NewCustomTextSetEvent(context.Background(),
&org.NewAggregate("org1").Aggregate, domain.LoginCustomText, domain.LoginKeyFooterSupportEmail, "Support Email", language.English,
),
),
},
),
),
@ -7277,6 +7308,7 @@ func TestCommandSide_SetCustomOrgLoginText(t *testing.T) {
TOS: "TOS",
PrivacyPolicy: "PrivacyPolicy",
Help: "Help",
SupportEmail: "Support Email",
},
},
},

View File

@ -29,6 +29,14 @@ func (c *Commands) orgPrivacyPolicyWriteModelByID(ctx context.Context, orgID str
}
func (c *Commands) AddPrivacyPolicy(ctx context.Context, resourceOwner string, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) {
if policy.SupportEmail != "" {
if err := policy.SupportEmail.Validate(); err != nil {
return nil, err
}
policy.SupportEmail = policy.SupportEmail.Normalize()
}
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-MMk9fs", "Errors.ResourceOwnerMissing")
}
@ -49,7 +57,8 @@ func (c *Commands) AddPrivacyPolicy(ctx context.Context, resourceOwner string, p
orgAgg,
policy.TOSLink,
policy.PrivacyLink,
policy.HelpLink))
policy.HelpLink,
policy.SupportEmail))
if err != nil {
return nil, err
}
@ -61,12 +70,19 @@ func (c *Commands) AddPrivacyPolicy(ctx context.Context, resourceOwner string, p
}
func (c *Commands) ChangePrivacyPolicy(ctx context.Context, resourceOwner string, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) {
if policy.SupportEmail != "" {
if err := policy.SupportEmail.Validate(); err != nil {
return nil, err
}
policy.SupportEmail = policy.SupportEmail.Normalize()
}
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-22N89f", "Errors.ResourceOwnerMissing")
}
existingPolicy := NewOrgPrivacyPolicyWriteModel(resourceOwner)
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
existingPolicy, err := c.orgPrivacyPolicyWriteModelByID(ctx, resourceOwner)
if err != nil {
return nil, err
}
@ -75,7 +91,7 @@ func (c *Commands) ChangePrivacyPolicy(ctx context.Context, resourceOwner string
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PrivacyPolicyWriteModel.WriteModel)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink)
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink, policy.SupportEmail)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4N9fs", "Errors.Org.PrivacyPolicy.NotChanged")
}

View File

@ -3,8 +3,8 @@ package command
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/org"
"github.com/zitadel/zitadel/internal/repository/policy"
)
@ -59,6 +59,7 @@ func (wm *OrgPrivacyPolicyWriteModel) NewChangedEvent(
tosLink,
privacyLink,
helpLink string,
supportEmail domain.EmailAddress,
) (*org.PrivacyPolicyChangedEvent, bool) {
changes := make([]policy.PrivacyPolicyChanges, 0)
@ -71,6 +72,9 @@ func (wm *OrgPrivacyPolicyWriteModel) NewChangedEvent(
if wm.HelpLink != helpLink {
changes = append(changes, policy.ChangeHelpLink(helpLink))
}
if wm.SupportEmail != supportEmail {
changes = append(changes, policy.ChangeSupportEmail(supportEmail))
}
if len(changes) == 0 {
return nil, false
}

View File

@ -44,9 +44,10 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
@ -65,6 +66,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
@ -74,9 +76,10 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
@ -97,6 +100,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
},
@ -107,9 +111,10 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
@ -118,14 +123,36 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
},
{
name: "add policy empty links, ok",
name: "wrong email, can't add policy",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "wrong email",
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "add policy empty links and empty support email, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
@ -138,6 +165,7 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
"",
"",
"",
"",
),
),
},
@ -148,9 +176,10 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
SupportEmail: "",
},
},
res: res{
@ -159,9 +188,10 @@ func TestCommandSide_AddPrivacyPolicy(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
SupportEmail: "",
},
},
},
@ -214,9 +244,10 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
args: args{
ctx: context.Background(),
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
@ -235,9 +266,10 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
@ -256,6 +288,7 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
@ -265,15 +298,32 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
TOSLink: "TOSLink",
PrivacyLink: "PrivacyLink",
HelpLink: "HelpLink",
SupportEmail: "support@example.com",
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "wrong email, can't change policy",
args: args{
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
SupportEmail: "wrong email",
},
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "change, ok",
fields: fields{
@ -286,13 +336,14 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange", "HelpLinkChange"),
newPrivacyPolicyChangedEvent(context.Background(), "org1", "TOSLinkChange", "PrivacyLinkChange", "HelpLinkChange", "support2@example.com"),
),
},
),
@ -302,9 +353,10 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
SupportEmail: "support2@example.com",
},
},
res: res{
@ -313,9 +365,10 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
TOSLink: "TOSLinkChange",
PrivacyLink: "PrivacyLinkChange",
HelpLink: "HelpLinkChange",
SupportEmail: "support2@example.com",
},
},
},
@ -331,13 +384,14 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newPrivacyPolicyChangedEvent(context.Background(), "org1", "", "", ""),
newPrivacyPolicyChangedEvent(context.Background(), "org1", "", "", "", ""),
),
},
),
@ -347,9 +401,10 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
ctx: context.Background(),
orgID: "org1",
policy: &domain.PrivacyPolicy{
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
SupportEmail: "",
},
},
res: res{
@ -358,9 +413,10 @@ func TestCommandSide_ChangePrivacyPolicy(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
TOSLink: "",
PrivacyLink: "",
HelpLink: "",
SupportEmail: "",
},
},
},
@ -444,6 +500,7 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) {
"TOSLink",
"PrivacyLink",
"HelpLink",
"support@example.com",
),
),
),
@ -487,13 +544,14 @@ func TestCommandSide_RemovePrivacyPolicy(t *testing.T) {
}
}
func newPrivacyPolicyChangedEvent(ctx context.Context, orgID string, tosLink, privacyLink, helpLink string) *org.PrivacyPolicyChangedEvent {
func newPrivacyPolicyChangedEvent(ctx context.Context, orgID string, tosLink, privacyLink, helpLink, supportEmail string) *org.PrivacyPolicyChangedEvent {
event, _ := org.NewPrivacyPolicyChangedEvent(ctx,
&org.NewAggregate(orgID).Aggregate,
[]policy.PrivacyPolicyChanges{
policy.ChangeTOSLink(tosLink),
policy.ChangePrivacyLink(privacyLink),
policy.ChangeHelpLink(helpLink),
policy.ChangeSupportEmail(domain.EmailAddress(supportEmail)),
},
)
return event

View File

@ -9,10 +9,11 @@ import (
type PrivacyPolicyWriteModel struct {
eventstore.WriteModel
TOSLink string
PrivacyLink string
HelpLink string
State domain.PolicyState
TOSLink string
PrivacyLink string
HelpLink string
SupportEmail domain.EmailAddress
State domain.PolicyState
}
func (wm *PrivacyPolicyWriteModel) Reduce() error {
@ -22,6 +23,7 @@ func (wm *PrivacyPolicyWriteModel) Reduce() error {
wm.TOSLink = e.TOSLink
wm.PrivacyLink = e.PrivacyLink
wm.HelpLink = e.HelpLink
wm.SupportEmail = e.SupportEmail
wm.State = domain.PolicyStateActive
case *policy.PrivacyPolicyChangedEvent:
if e.PrivacyLink != nil {
@ -33,6 +35,9 @@ func (wm *PrivacyPolicyWriteModel) Reduce() error {
if e.HelpLink != nil {
wm.HelpLink = *e.HelpLink
}
if e.SupportEmail != nil {
wm.SupportEmail = *e.SupportEmail
}
case *policy.PrivacyPolicyRemovedEvent:
wm.State = domain.PolicyStateRemoved
}

View File

@ -296,6 +296,7 @@ const (
LoginKeyFooterTOS = LoginKeyFooter + "Tos"
LoginKeyFooterPrivacyPolicy = LoginKeyFooter + "PrivacyPolicy"
LoginKeyFooterHelp = LoginKeyFooter + "Help"
LoginKeyFooterSupportEmail = LoginKeyFooter + "SupportEmail"
)
type CustomLoginText struct {
@ -639,6 +640,7 @@ type FooterText struct {
TOS string
PrivacyPolicy string
Help string
SupportEmail string
}
type PasswordlessPromptScreenText struct {

View File

@ -10,7 +10,8 @@ type PrivacyPolicy struct {
State PolicyState
Default bool
TOSLink string
PrivacyLink string
HelpLink string
TOSLink string
PrivacyLink string
HelpLink string
SupportEmail EmailAddress
}

View File

@ -10,6 +10,7 @@ type PrivacyPolicyView struct {
AggregateID string
TOSLink string
PrivacyLink string
SupportEmail string
Default bool
CreationDate time.Time

View File

@ -1181,4 +1181,7 @@ func footerKeyToDomain(text *CustomText, result *domain.CustomLoginText) {
if text.Key == domain.LoginKeyFooterHelp {
result.Footer.Help = text.Text
}
if text.Key == domain.LoginKeyFooterSupportEmail {
result.Footer.SupportEmail = text.Text
}
}

View File

@ -24,9 +24,10 @@ type PrivacyPolicy struct {
ResourceOwner string
State domain.PolicyState
TOSLink string
PrivacyLink string
HelpLink string
TOSLink string
PrivacyLink string
HelpLink string
SupportEmail domain.EmailAddress
IsDefault bool
}
@ -72,6 +73,10 @@ var (
name: projection.PrivacyPolicyHelpLinkCol,
table: privacyTable,
}
PrivacyColSupportEmail = Column{
name: projection.PrivacyPolicySupportEmailCol,
table: privacyTable,
}
PrivacyColIsDefault = Column{
name: projection.PrivacyPolicyIsDefaultCol,
table: privacyTable,
@ -148,6 +153,7 @@ func preparePrivacyPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Sele
PrivacyColPrivacyLink.identifier(),
PrivacyColTOSLink.identifier(),
PrivacyColHelpLink.identifier(),
PrivacyColSupportEmail.identifier(),
PrivacyColIsDefault.identifier(),
PrivacyColState.identifier(),
).
@ -164,6 +170,7 @@ func preparePrivacyPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Sele
&policy.PrivacyLink,
&policy.TOSLink,
&policy.HelpLink,
&policy.SupportEmail,
&policy.IsDefault,
&policy.State,
)
@ -179,9 +186,10 @@ func preparePrivacyPolicyQuery(ctx context.Context, db prepareDatabase) (sq.Sele
func (p *PrivacyPolicy) ToDomain() *domain.PrivacyPolicy {
return &domain.PrivacyPolicy{
TOSLink: p.TOSLink,
PrivacyLink: p.PrivacyLink,
HelpLink: p.HelpLink,
Default: p.IsDefault,
TOSLink: p.TOSLink,
PrivacyLink: p.PrivacyLink,
HelpLink: p.HelpLink,
SupportEmail: p.SupportEmail,
Default: p.IsDefault,
}
}

View File

@ -13,17 +13,18 @@ import (
)
var (
preparePrivacyPolicyStmt = `SELECT projections.privacy_policies2.id,` +
` projections.privacy_policies2.sequence,` +
` projections.privacy_policies2.creation_date,` +
` projections.privacy_policies2.change_date,` +
` projections.privacy_policies2.resource_owner,` +
` projections.privacy_policies2.privacy_link,` +
` projections.privacy_policies2.tos_link,` +
` projections.privacy_policies2.help_link,` +
` projections.privacy_policies2.is_default,` +
` projections.privacy_policies2.state` +
` FROM projections.privacy_policies2` +
preparePrivacyPolicyStmt = `SELECT projections.privacy_policies3.id,` +
` projections.privacy_policies3.sequence,` +
` projections.privacy_policies3.creation_date,` +
` projections.privacy_policies3.change_date,` +
` projections.privacy_policies3.resource_owner,` +
` projections.privacy_policies3.privacy_link,` +
` projections.privacy_policies3.tos_link,` +
` projections.privacy_policies3.help_link,` +
` projections.privacy_policies3.support_email,` +
` projections.privacy_policies3.is_default,` +
` projections.privacy_policies3.state` +
` FROM projections.privacy_policies3` +
` AS OF SYSTEM TIME '-1 ms'`
preparePrivacyPolicyCols = []string{
"id",
@ -34,6 +35,7 @@ var (
"privacy_link",
"tos_link",
"help_link",
"support_email",
"is_default",
"state",
}
@ -84,6 +86,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
"privacy.ch",
"tos.ch",
"help.ch",
"support@example.com",
true,
domain.PolicyStateActive,
},
@ -99,6 +102,7 @@ func Test_PrivacyPolicyPrepares(t *testing.T) {
PrivacyLink: "privacy.ch",
TOSLink: "tos.ch",
HelpLink: "help.ch",
SupportEmail: "support@example.com",
IsDefault: true,
},
},

View File

@ -14,7 +14,7 @@ import (
)
const (
PrivacyPolicyTable = "projections.privacy_policies2"
PrivacyPolicyTable = "projections.privacy_policies3"
PrivacyPolicyIDCol = "id"
PrivacyPolicyCreationDateCol = "creation_date"
@ -27,6 +27,7 @@ const (
PrivacyPolicyPrivacyLinkCol = "privacy_link"
PrivacyPolicyTOSLinkCol = "tos_link"
PrivacyPolicyHelpLinkCol = "help_link"
PrivacyPolicySupportEmailCol = "support_email"
PrivacyPolicyOwnerRemovedCol = "owner_removed"
)
@ -51,6 +52,7 @@ func newPrivacyPolicyProjection(ctx context.Context, config crdb.StatementHandle
crdb.NewColumn(PrivacyPolicyPrivacyLinkCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicyTOSLinkCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicyHelpLinkCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicySupportEmailCol, crdb.ColumnTypeText),
crdb.NewColumn(PrivacyPolicyOwnerRemovedCol, crdb.ColumnTypeBool, crdb.Default(false)),
},
crdb.NewPrimaryKey(PrivacyPolicyInstanceIDCol, PrivacyPolicyIDCol),
@ -128,6 +130,7 @@ func (p *privacyPolicyProjection) reduceAdded(event eventstore.Event) (*handler.
handler.NewCol(PrivacyPolicyPrivacyLinkCol, policyEvent.PrivacyLink),
handler.NewCol(PrivacyPolicyTOSLinkCol, policyEvent.TOSLink),
handler.NewCol(PrivacyPolicyHelpLinkCol, policyEvent.HelpLink),
handler.NewCol(PrivacyPolicySupportEmailCol, policyEvent.SupportEmail),
handler.NewCol(PrivacyPolicyIsDefaultCol, isDefault),
handler.NewCol(PrivacyPolicyResourceOwnerCol, policyEvent.Aggregate().ResourceOwner),
handler.NewCol(PrivacyPolicyInstanceIDCol, policyEvent.Aggregate().InstanceID),
@ -157,6 +160,9 @@ func (p *privacyPolicyProjection) reduceChanged(event eventstore.Event) (*handle
if policyEvent.HelpLink != nil {
cols = append(cols, handler.NewCol(PrivacyPolicyHelpLinkCol, *policyEvent.HelpLink))
}
if policyEvent.SupportEmail != nil {
cols = append(cols, handler.NewCol(PrivacyPolicySupportEmailCol, *policyEvent.SupportEmail))
}
return crdb.NewUpdateStatement(
&policyEvent,
cols,

View File

@ -31,8 +31,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
"helpLink": "http://help.link",
"supportEmail": "support@example.com"}`),
), org.PrivacyPolicyAddedEventMapper),
},
reduce: (&privacyPolicyProjection{}).reduceAdded,
@ -43,7 +43,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.privacy_policies2 (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedStmt: "INSERT INTO projections.privacy_policies3 (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, support_email, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@ -53,6 +53,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
"http://privacy.link",
"http://tos.link",
"http://help.link",
domain.EmailAddress("support@example.com"),
false,
"ro-id",
"instance-id",
@ -72,8 +73,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
"helpLink": "http://help.link",
"supportEmail": "support@example.com"}`),
), org.PrivacyPolicyChangedEventMapper),
},
want: wantReduce{
@ -83,13 +84,14 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.privacy_policies2 SET (change_date, sequence, privacy_link, tos_link, help_link) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)",
expectedStmt: "UPDATE projections.privacy_policies3 SET (change_date, sequence, privacy_link, tos_link, help_link, support_email) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"http://privacy.link",
"http://tos.link",
"http://help.link",
domain.EmailAddress("support@example.com"),
"agg-id",
"instance-id",
},
@ -115,7 +117,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.privacy_policies2 WHERE (id = $1) AND (instance_id = $2)",
expectedStmt: "DELETE FROM projections.privacy_policies3 WHERE (id = $1) AND (instance_id = $2)",
expectedArgs: []interface{}{
"agg-id",
"instance-id",
@ -141,7 +143,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM projections.privacy_policies2 WHERE (instance_id = $1)",
expectedStmt: "DELETE FROM projections.privacy_policies3 WHERE (instance_id = $1)",
expectedArgs: []interface{}{
"agg-id",
},
@ -160,8 +162,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
"helpLink": "http://help.link",
"supportEmail": "support@example.com"}`),
), instance.PrivacyPolicyAddedEventMapper),
},
want: wantReduce{
@ -171,7 +173,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.privacy_policies2 (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedStmt: "INSERT INTO projections.privacy_policies3 (creation_date, change_date, sequence, id, state, privacy_link, tos_link, help_link, support_email, is_default, resource_owner, instance_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
expectedArgs: []interface{}{
anyArg{},
anyArg{},
@ -181,6 +183,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
"http://privacy.link",
"http://tos.link",
"http://help.link",
domain.EmailAddress("support@example.com"),
true,
"ro-id",
"instance-id",
@ -200,8 +203,8 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
[]byte(`{
"tosLink": "http://tos.link",
"privacyLink": "http://privacy.link",
"helpLink": "http://help.link"
}`),
"helpLink": "http://help.link",
"supportEmail": "support@example.com"}`),
), instance.PrivacyPolicyChangedEventMapper),
},
want: wantReduce{
@ -211,13 +214,14 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.privacy_policies2 SET (change_date, sequence, privacy_link, tos_link, help_link) = ($1, $2, $3, $4, $5) WHERE (id = $6) AND (instance_id = $7)",
expectedStmt: "UPDATE projections.privacy_policies3 SET (change_date, sequence, privacy_link, tos_link, help_link, support_email) = ($1, $2, $3, $4, $5, $6) WHERE (id = $7) AND (instance_id = $8)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"http://privacy.link",
"http://tos.link",
"http://help.link",
domain.EmailAddress("support@example.com"),
"agg-id",
"instance-id",
},
@ -243,7 +247,7 @@ func TestPrivacyPolicyProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.privacy_policies2 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)",
expectedStmt: "UPDATE projections.privacy_policies3 SET (change_date, sequence, owner_removed) = ($1, $2, $3) WHERE (instance_id = $4) AND (resource_owner = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),

View File

@ -3,8 +3,8 @@ package instance
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/repository/policy"
)
@ -24,6 +24,7 @@ func NewPrivacyPolicyAddedEvent(
tosLink,
privacyLink,
helpLink string,
supportEmail domain.EmailAddress,
) *PrivacyPolicyAddedEvent {
return &PrivacyPolicyAddedEvent{
PrivacyPolicyAddedEvent: *policy.NewPrivacyPolicyAddedEvent(
@ -33,7 +34,8 @@ func NewPrivacyPolicyAddedEvent(
PrivacyPolicyAddedEventType),
tosLink,
privacyLink,
helpLink),
helpLink,
supportEmail),
}
}

View File

@ -3,8 +3,8 @@ package org
import (
"context"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
"github.com/zitadel/zitadel/internal/repository/policy"
)
@ -25,6 +25,7 @@ func NewPrivacyPolicyAddedEvent(
tosLink,
privacyLink,
helpLink string,
supportEmail domain.EmailAddress,
) *PrivacyPolicyAddedEvent {
return &PrivacyPolicyAddedEvent{
PrivacyPolicyAddedEvent: *policy.NewPrivacyPolicyAddedEvent(
@ -34,7 +35,8 @@ func NewPrivacyPolicyAddedEvent(
PrivacyPolicyAddedEventType),
tosLink,
privacyLink,
helpLink),
helpLink,
supportEmail),
}
}

View File

@ -3,9 +3,9 @@ package policy
import (
"encoding/json"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
)
@ -18,9 +18,10 @@ const (
type PrivacyPolicyAddedEvent struct {
eventstore.BaseEvent `json:"-"`
TOSLink string `json:"tosLink,omitempty"`
PrivacyLink string `json:"privacyLink,omitempty"`
HelpLink string `json:"helpLink,omitempty"`
TOSLink string `json:"tosLink,omitempty"`
PrivacyLink string `json:"privacyLink,omitempty"`
HelpLink string `json:"helpLink,omitempty"`
SupportEmail domain.EmailAddress `json:"supportEmail,omitempty"`
}
func (e *PrivacyPolicyAddedEvent) Data() interface{} {
@ -36,12 +37,14 @@ func NewPrivacyPolicyAddedEvent(
tosLink,
privacyLink,
helpLink string,
supportEmail domain.EmailAddress,
) *PrivacyPolicyAddedEvent {
return &PrivacyPolicyAddedEvent{
BaseEvent: *base,
TOSLink: tosLink,
PrivacyLink: privacyLink,
HelpLink: helpLink,
BaseEvent: *base,
TOSLink: tosLink,
PrivacyLink: privacyLink,
HelpLink: helpLink,
SupportEmail: supportEmail,
}
}
@ -60,9 +63,10 @@ func PrivacyPolicyAddedEventMapper(event *repository.Event) (eventstore.Event, e
type PrivacyPolicyChangedEvent struct {
eventstore.BaseEvent `json:"-"`
TOSLink *string `json:"tosLink,omitempty"`
PrivacyLink *string `json:"privacyLink,omitempty"`
HelpLink *string `json:"helpLink,omitempty"`
TOSLink *string `json:"tosLink,omitempty"`
PrivacyLink *string `json:"privacyLink,omitempty"`
HelpLink *string `json:"helpLink,omitempty"`
SupportEmail *domain.EmailAddress `json:"supportEmail,omitempty"`
}
func (e *PrivacyPolicyChangedEvent) Data() interface{} {
@ -109,6 +113,12 @@ func ChangeHelpLink(helpLink string) func(*PrivacyPolicyChangedEvent) {
}
}
func ChangeSupportEmail(supportEmail domain.EmailAddress) func(*PrivacyPolicyChangedEvent) {
return func(e *PrivacyPolicyChangedEvent) {
e.SupportEmail = &supportEmail
}
}
func PrivacyPolicyChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &PrivacyPolicyChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@ -5460,6 +5460,13 @@ message UpdatePrivacyPolicyRequest {
example: "\"https://zitadel.com/docs/manuals/introduction\"";
}
];
string support_email = 4 [
(validate.rules).string = {ignore_empty: true, max_len: 320, email: true},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"support-email@test.com\"";
description: "help / support email address."
}
];
}
message UpdatePrivacyPolicyResponse {

View File

@ -9834,6 +9834,13 @@ message AddCustomPrivacyPolicyRequest {
example: "\"https://zitadel.com/docs/manuals/introduction\"";
}
];
string support_email = 4 [
(validate.rules).string = {ignore_empty: true, max_len: 320, email: true},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"support-email@test.com\"";
description: "help / support email address."
}
];
}
message AddCustomPrivacyPolicyResponse {
@ -9859,6 +9866,13 @@ message UpdateCustomPrivacyPolicyRequest {
example: "\"https://zitadel.com/docs/manuals/introduction\"";
}
];
string support_email = 4 [
(validate.rules).string = {ignore_empty: true, max_len: 320, email: true},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"support-email@test.com\"";
description: "help / support email address."
}
];
}
message UpdateCustomPrivacyPolicyResponse {

View File

@ -4,6 +4,7 @@ import "zitadel/object.proto";
import "zitadel/idp.proto";
import "google/protobuf/duration.proto";
import "protoc-gen-openapiv2/options/annotations.proto";
import "validate/validate.proto";
package zitadel.policy.v1;
@ -345,6 +346,13 @@ message PrivacyPolicy {
example: "\"https://zitadel.com/docs/manuals/introduction\"";
}
];
string support_email = 6 [
(validate.rules).string = {ignore_empty: true, max_len: 320, email: true},
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "\"support-email@test.com\"";
description: "help / support email address."
}
];
}
message NotificationPolicy {

View File

@ -394,11 +394,12 @@ message LogoutDoneScreenText {
}
message FooterText {
reserved 2, 4, 6;
reserved 2, 4, 6, 8;
reserved "tos_link", "privacy_policy_link", "help_link";
string tos = 1 [(validate.rules).string = {max_len: 200}];
string privacy_policy = 3 [(validate.rules).string = {max_len: 200}];
string help = 5 [(validate.rules).string = {max_len: 200}];
string support_email = 7 [(validate.rules).string = {max_len: 200}];
}
message PasswordlessPromptScreenText {