feat: Private label email policy (#813)

* Label Policy added

* save

* chore: update docs action

* Save

* Save

* Get colors from DB

* Variables inserted

* Get images from global directory.

* Add tests

* Add tests

* Corrections from mergerequest

* Corrections from mergerequest

* Test corrected.

* Added colors to all notifications.

* Added colors to
Corrected text and formatting.all notifications.

* Spelling error corrected.

* fix: tests

* Merge Branch corrected.

* Step6 added

* Corrections from mergerequest

* fix: generate management

* Formatted texts.

* fix: migrations

Co-authored-by: Florian Forster <florian@caos.ch>
Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Michael Waeger
2020-10-20 19:10:23 +02:00
committed by GitHub
parent cfd119924f
commit 42384763d1
65 changed files with 7143 additions and 13940 deletions

View File

@@ -10,11 +10,14 @@ import (
"github.com/caos/zitadel/internal/api/authz"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler"
"github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
"github.com/caos/zitadel/internal/notification/types"
"github.com/caos/zitadel/internal/user/repository/eventsourcing"
usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
@@ -32,8 +35,10 @@ type Notification struct {
}
const (
notificationTable = "notification.notifications"
NotifyUserID = "NOTIFICATION"
notificationTable = "notification.notifications"
NotifyUserID = "NOTIFICATION"
labelPolicyTableOrg = "management.label_policies"
labelPolicyTableDef = "adminapi.label_policies"
)
func (n *Notification) ViewModel() string {
@@ -78,13 +83,19 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
if err != nil || alreadyHandled {
return err
}
colors, err := n.getLabelPolicy(context.Background())
if err != nil {
return err
}
initCode := new(es_model.InitUserCode)
initCode.SetData(event)
user, err := n.view.NotifyUserByID(event.AggregateID)
if err != nil {
return err
}
err = types.SendUserInitCode(n.statikDir, n.i18n, user, initCode, n.systemDefaults, n.AesCrypto)
err = types.SendUserInitCode(n.statikDir, n.i18n, user, initCode, n.systemDefaults, n.AesCrypto, colors)
if err != nil {
return err
}
@@ -96,13 +107,19 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil || alreadyHandled {
return err
}
colors, err := n.getLabelPolicy(context.Background())
if err != nil {
return err
}
pwCode := new(es_model.PasswordCode)
pwCode.SetData(event)
user, err := n.view.NotifyUserByID(event.AggregateID)
if err != nil {
return err
}
err = types.SendPasswordCode(n.statikDir, n.i18n, user, pwCode, n.systemDefaults, n.AesCrypto)
err = types.SendPasswordCode(n.statikDir, n.i18n, user, pwCode, n.systemDefaults, n.AesCrypto, colors)
if err != nil {
return err
}
@@ -114,13 +131,19 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
if err != nil || alreadyHandled {
return nil
}
colors, err := n.getLabelPolicy(context.Background())
if err != nil {
return err
}
emailCode := new(es_model.EmailCode)
emailCode.SetData(event)
user, err := n.view.NotifyUserByID(event.AggregateID)
if err != nil {
return err
}
err = types.SendEmailVerificationCode(n.statikDir, n.i18n, user, emailCode, n.systemDefaults, n.AesCrypto)
err = types.SendEmailVerificationCode(n.statikDir, n.i18n, user, emailCode, n.systemDefaults, n.AesCrypto, colors)
if err != nil {
return err
}
@@ -196,3 +219,21 @@ func (n *Notification) OnError(event *models.Event, err error) error {
func getSetNotifyContextData(orgID string) context.Context {
return authz.SetCtxData(context.Background(), authz.CtxData{UserID: NotifyUserID, OrgID: orgID})
}
// Read organization specific colors
func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPolicyView, error) {
// read from Org
policy, err := n.view.LabelPolicyByAggregateID(authz.GetCtxData(ctx).OrgID, labelPolicyTableOrg)
if errors.IsNotFound(err) {
// read from default
policy, err = n.view.LabelPolicyByAggregateID(n.systemDefaults.IamID, labelPolicyTableDef)
if err != nil {
return nil, err
}
policy.Default = true
}
if err != nil {
return nil, err
}
return iam_es_model.LabelPolicyViewToModel(policy), err
}

View File

@@ -0,0 +1,10 @@
package view
import (
"github.com/caos/zitadel/internal/iam/repository/view"
"github.com/caos/zitadel/internal/iam/repository/view/model"
)
func (v *View) LabelPolicyByAggregateID(aggregateID string, labelPolicyTableVar string) (*model.LabelPolicyView, error) {
return view.GetLabelPolicyByAggregateID(v.Db, labelPolicyTableVar, aggregateID)
}

View File

@@ -3,28 +3,28 @@ InitCode:
PreHeader: User initialisieren
Subject: User initialisieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Mit dem Benutzernamen {{.PreferredLoginName}} kannst du dich anmelden. Nutze den untenstehenden Button, um die Initialisierung abzuschliesen. (Code {{.Code}}) Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren.
Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Mit dem Benutzernamen &lt;br&gt;&lt;strong&gt;{{.PreferredLoginName}}&lt;/strong&gt;&lt;br&gt; kannst du dich anmelden. Nutze den untenstehenden Button, um die Initialisierung abzuschliessen &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren.
ButtonText: Initialisierung abschliessen
PasswordReset:
Title: Zitadel - Passwort zurücksetzen
PreHeader: Passwort zurücksetzen
Subject: Passwort zurücksetzen
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Wir haben eine Anfrage für das Zurücksetzen deines Passwortes bekommen. Du kannst den untenstehenden Button verwenden, um dein Passwort zurückzusetzen. (Code {{.Code}}) Falls du dieses Mail nicht angefordert hast, kannst du es ignorieren.
Text: Wir haben eine Anfrage für das Zurücksetzen deines Passwortes bekommen. Du kannst den untenstehenden Button verwenden, um dein Passwort zurückzusetzen &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du dieses Mail nicht angefordert hast, kannst du es ignorieren.
ButtonText: Passwort zurücksetzen
VerifyEmail:
Title: Zitadel - Email verifizieren
PreHeader: Email verifizieren
Subject: Email verifizieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Eine neue E-Mail Adresse wurde hinzugefügt. Bitte verwende den untenstehenden Button um diese zu verifizieren. (Code {{.Code}}) Falls du deine E-Mail Adresse nicht selber hinzugefügt hast, kannst du dieses E-Mail ignorieren.
Text: Eine neue E-Mail Adresse wurde hinzugefügt. Bitte verwende den untenstehenden Button um diese zu verifizieren &lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt; Falls du deine E-Mail Adresse nicht selber hinzugefügt hast, kannst du dieses E-Mail ignorieren.
ButtonText: Email verifizieren
VerifyPhone:
Title: Zitadel - Telefonnummer verifizieren
PreHeader: Telefonnummer verifizieren
Subject: Telefonnummer verifizieren
Greeting: Hallo {{.FirstName}} {{.LastName}},
Text: Eine Telefonnummer wurde hinzugefügt. Bitte verifiziere diese in dem du folgenden Code eingibst {{.Code}}
Text: Eine Telefonnummer wurde hinzugefügt. Bitte verifiziere diese in dem du folgenden Code eingibst&lt;br&gt;(Code &lt;strong&gt;{{.Code}}&lt;/strong&gt;).&lt;br&gt;
ButtonText: Telefon verifizieren
DomainClaimed:
Title: Zitadel - Domain wurde beansprucht

View File

@@ -0,0 +1,49 @@
<mjml>
<mj-head>
<mj-attributes>
<mj-text align="center" color="#ffffff" font-family="Lato" />
<mj-section padding="0" full-width="full-width" />
<mj-body background-color="#222324" />
<mj-image padding="0" />
<mj-column padding="0" />
<mj-wrapper padding-left="0" />
<mj-wrapper full-width="full-width" />
<mj-class name="left" position="" />
</mj-attributes>
</mj-head>
<mj-body>
<mj-wrapper background-url="https://static.zitadel.ch/zitadel-logo-outline-light.png" border="0" background-repeat="no-repeat">
<mj-section>
<mj-group>
<mj-column>
<mj-image src="https://static.zitadel.ch/zitadel-logo-light.png" align="left" width="100px" padding="0" />
</mj-column>
</mj-group>
</mj-section>
<mj-section>
<mj-group>
<mj-column width="20%">
<mj-image src="https://static.zitadel.ch/flavor-spikes-small-opacity40.png" align="left" heigh="80%" width="80%" />
</mj-column>
<mj-column width="60%">
<mj-text font-size="2rem" font-weight="light">{{.Greeting}}</mj-text>
<mj-text font-size="1.25rem" font-weight="light">{{.Text}}</mj-text>
<mj-button href="{{.URL}}" background-color="#5282C1" font-size="16px">{{.ButtonText}}</mj-button>
<mj-text>
<a href="http://www.caos.ch" style="color:#e91e63; text-decoration: none;" target="_blank"> CAOS AG </a> | Teufener Strasse 19 | CH-9000 St. Gallen </mj-text>
</mj-column>
<mj-column width="20%">
<mj-image src="https://static.zitadel.ch/flavor-spikes-big-opacity40.png" align="right" />
</mj-column>
</mj-group>
</mj-section>
<mj-section>
<mj-group>
<mj-column>
<mj-image src="https://static.zitadel.ch/logo_whitefont_transparentbg.png" align="right" heigh="65%" width="65%" padding-right="20px" />
</mj-column>
</mj-group>
</mj-section>
</mj-wrapper>
</mj-body>
</mjml>

View File

@@ -0,0 +1,49 @@
<mjml>
<mj-head>
<mj-attributes>
<mj-text align="center" color="#555" font-family="Lato" />
<mj-section padding="0" full-width="full-width" />
<mj-body />
<mj-image padding="0" />
<mj-column padding="0" />
<mj-wrapper padding-left="0" />
<mj-wrapper full-width="full-width" />
<mj-class name="left" position="" />
</mj-attributes>
</mj-head>
<mj-body>
<mj-wrapper background-url="https://static.zitadel.ch/zitadel-logo-outline-dark.png" border="0" background-repeat="no-repeat">
<mj-section>
<mj-group>
<mj-column>
<mj-image src="https://static.zitadel.ch/zitadel-logo-dark.png" align="left" width="100px" padding="0" />
</mj-column>
</mj-group>
</mj-section>
<mj-section>
<mj-group>
<mj-column width="20%">
<mj-image src="https://static.zitadel.ch/flavor-spikes-small-opacity40.png" align="left" />
</mj-column>
<mj-column width="60%">
<mj-text font-size="2rem" font-weight="light">{{.Greeting}}</mj-text>
<mj-text font-size="1.25rem" font-weight="light">{{.Text}}</mj-text>
<mj-button href="{{.URL}}" background-color="#5282C1" font-size="16px">{{.ButtonText}}</mj-button>
<mj-text>
<a href="http://www.caos.ch" style="color:#e91e63; text-decoration: none;" target="_blank"> CAOS AG </a> | Teufener Strasse 19 | CH-9000 St. Gallen </mj-text>
</mj-column>
<mj-column width="20%">
<mj-image src="https://static.zitadel.ch/flavor-spikes-big-opacity40.png" align="right" />
</mj-column>
</mj-group>
</mj-section>
<mj-section>
<mj-group>
<mj-column>
<mj-image src="https://static.zitadel.ch/logo-gsuite-light.png" align="right" width="100px" />
</mj-column>
</mj-group>
</mj-section>
</mj-wrapper>
</mj-body>
</mjml>

View File

@@ -0,0 +1,32 @@
<mjml>
<mj-head>
<mj-attributes>
<mj-text align="center" color="#555" font-family="Lato" />
<mj-section full-width="full-width" />
<mj-body />
<mj-image padding="0" />
<mj-column />
<mj-class name="left" align="left" position="absolute" width="50%" />
<mj-class name="right" align="right" />
<mj-class name="position" position="absolute" />
<mj-class name="body" background-image="url('https://static.zitadel.ch/logo-gsuite-light.png')" />
</mj-attributes>
</mj-head>
<mj-body mj-class="body">
<mj-section background-url="https://static.zitadel.ch/zitadel-logo-outline-dark.png" border="0" background-repeat="no-repeat">
<mj-group>
<mj-column>
<mj-image src="https://static.zitadel.ch/zitadel-logo-dark.png" width="100px" padding="0" mj-class="left" />
<mj-image src="https://static.zitadel.ch/flavor-spikes-small-opacity40.png" mj-class="left position" />
<mj-text font-size="2rem" font-weight="light">{{.Greeting}}</mj-text>
<mj-text font-size="1.25rem" font-weight="light">{{.Text}}</mj-text>
<mj-button href="{{.URL}}" background-color="#5282C1" font-size="16px">{{.ButtonText}}</mj-button>
<mj-text>
<a href="http://www.caos.ch" style="color:#e91e63; text-decoration: none;" target="_blank"> CAOS AG </a> | Teufener Strasse 19 | CH-9000 St. Gallen </mj-text>
<mj-image src="https://static.zitadel.ch/flavor-spikes-big-opacity40.png" width="50%" mj-class="right position" />
<mj-image src="https://static.zitadel.ch/logo-gsuite-light.png" width="100px" padding="0" mj-class="right" />
</mj-column>
</mj-group>
</mj-section>
</mj-body>
</mjml>

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,21 @@
package templates
import (
"html"
"github.com/caos/zitadel/internal/i18n"
)
type TemplateData struct {
Title string
PreHeader string
Subject string
Greeting string
Text string
Href string
ButtonText string
Title string
PreHeader string
Subject string
Greeting string
Text string
Href string
ButtonText string
PrimaryColor string
SecondaryColor string
}
func (data *TemplateData) Translate(i18n *i18n.Translator, args map[string]interface{}, langs ...string) {
@@ -19,7 +23,7 @@ func (data *TemplateData) Translate(i18n *i18n.Translator, args map[string]inter
data.PreHeader = i18n.Localize(data.PreHeader, nil, langs...)
data.Subject = i18n.Localize(data.Subject, nil, langs...)
data.Greeting = i18n.Localize(data.Greeting, args, langs...)
data.Text = i18n.Localize(data.Text, args, langs...)
data.Text = html.UnescapeString(i18n.Localize(data.Text, args, langs...))
data.Href = i18n.Localize(data.Href, nil, langs...)
data.ButtonText = i18n.Localize(data.ButtonText, nil, langs...)
}

View File

@@ -1,13 +1,15 @@
package types
import (
"net/http"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
"net/http"
)
type EmailVerificationCodeData struct {
@@ -15,7 +17,7 @@ type EmailVerificationCodeData struct {
URL string
}
func SendEmailVerificationCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendEmailVerificationCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -32,6 +34,9 @@ func SendEmailVerificationCode(dir http.FileSystem, i18n *i18n.Translator, user
systemDefaults.Notifications.TemplateData.VerifyEmail.Translate(i18n, args, user.PreferredLanguage)
emailCodeData := &EmailVerificationCodeData{TemplateData: systemDefaults.Notifications.TemplateData.VerifyEmail, URL: url}
// Set the color in initCodeData
emailCodeData.PrimaryColor = colors.PrimaryColor
emailCodeData.SecondaryColor = colors.SecondaryColor
template, err := templates.GetParsedTemplate(dir, emailCodeData)
if err != nil {
return err

View File

@@ -1,13 +1,15 @@
package types
import (
"net/http"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
"net/http"
)
type InitCodeEmailData struct {
@@ -21,7 +23,7 @@ type UrlData struct {
PasswordSet bool
}
func SendUserInitCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendUserInitCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -39,6 +41,9 @@ func SendUserInitCode(dir http.FileSystem, i18n *i18n.Translator, user *view_mod
systemDefaults.Notifications.TemplateData.InitCode.Translate(i18n, args, user.PreferredLanguage)
initCodeData := &InitCodeEmailData{TemplateData: systemDefaults.Notifications.TemplateData.InitCode, URL: url}
// Set the color in initCodeData
initCodeData.PrimaryColor = colors.PrimaryColor
initCodeData.SecondaryColor = colors.SecondaryColor
template, err := templates.GetParsedTemplate(dir, initCodeData)
if err != nil {
return err

View File

@@ -1,13 +1,15 @@
package types
import (
"net/http"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/i18n"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/notification/templates"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
"net/http"
)
type PasswordCodeData struct {
@@ -17,7 +19,7 @@ type PasswordCodeData struct {
URL string
}
func SendPasswordCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendPasswordCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@@ -34,6 +36,9 @@ func SendPasswordCode(dir http.FileSystem, i18n *i18n.Translator, user *view_mod
systemDefaults.Notifications.TemplateData.PasswordReset.Translate(i18n, args, user.PreferredLanguage)
passwordCodeData := &PasswordCodeData{TemplateData: systemDefaults.Notifications.TemplateData.PasswordReset, FirstName: user.FirstName, LastName: user.LastName, URL: url}
// Set the color in initCodeData
passwordCodeData.PrimaryColor = colors.PrimaryColor
passwordCodeData.SecondaryColor = colors.SecondaryColor
template, err := templates.GetParsedTemplate(dir, passwordCodeData)
if err != nil {
return err

View File

@@ -1,6 +1,8 @@
package types
import (
"html"
"github.com/caos/zitadel/internal/config/systemdefaults"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/providers"
@@ -14,6 +16,7 @@ func generateEmail(user *view_model.NotifyUser, subject, content string, config
if err != nil {
return err
}
content = html.UnescapeString(content)
message := &email.EmailMessage{
SenderEmail: config.Providers.Email.From,
Recipients: []string{user.VerifiedEmail},