feat: e-mail templates (#1158)

* View definition added

* Get templates and texts from the database.

* Fill in texts in templates

* Fill in texts in templates

* Client API added

* Weekly backup

* Weekly backup

* Daily backup

* Weekly backup

* Tests added

* Corrections from merge branch

* Fixes from pull request review
This commit is contained in:
Michael Waeger
2021-01-18 14:17:22 +01:00
committed by GitHub
parent e7540e5e05
commit f2a32871a7
88 changed files with 5325 additions and 155 deletions

View File

@@ -964,3 +964,118 @@ func (es *IAMEventstore) ChangeOrgIAMPolicy(ctx context.Context, policy *iam_mod
es.iamCache.cacheIAM(repoIam)
return model.OrgIAMPolicyToModel(repoIam.DefaultOrgIAMPolicy), nil
}
func (es *IAMEventstore) PrepareAddMailTemplate(ctx context.Context, template *iam_model.MailTemplate) (*model.IAM, *models.Aggregate, error) {
if template == nil || !template.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-j9l18", "Errors.IAM.MailTemplate.Empty")
}
iam, err := es.IAMByID(ctx, template.AggregateID)
if err != nil {
return nil, nil, err
}
repoIam := model.IAMFromModel(iam)
mailTemplate := model.MailTemplateFromModel(template)
addAggregate := MailTemplateAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, mailTemplate)
aggregate, err := addAggregate(ctx)
if err != nil {
return nil, nil, err
}
return repoIam, aggregate, nil
}
func (es *IAMEventstore) AddMailTemplate(ctx context.Context, template *iam_model.MailTemplate) (*iam_model.MailTemplate, error) {
repoIam, addAggregate, err := es.PrepareAddMailTemplate(ctx, template)
if err != nil {
return nil, err
}
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate)
if err != nil {
return nil, err
}
es.iamCache.cacheIAM(repoIam)
return model.MailTemplateToModel(repoIam.DefaultMailTemplate), nil
}
func (es *IAMEventstore) ChangeMailTemplate(ctx context.Context, template *iam_model.MailTemplate) (*iam_model.MailTemplate, error) {
if template == nil || !template.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-gCnCs", "Errors.IAM.MailTemplateInvalid")
}
iam, err := es.IAMByID(ctx, template.AggregateID)
if err != nil {
return nil, err
}
repoIam := model.IAMFromModel(iam)
repoMailTemplate := model.MailTemplateFromModel(template)
addAggregate := MailTemplateChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMailTemplate)
err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate)
if err != nil {
return nil, err
}
es.iamCache.cacheIAM(repoIam)
return model.MailTemplateToModel(repoIam.DefaultMailTemplate), nil
}
func (es *IAMEventstore) PrepareAddMailText(ctx context.Context, text *iam_model.MailText) (*model.IAM, *models.Aggregate, error) {
if text == nil || !text.IsValid() {
return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-j9l18", "Errors.IAM.MailText.Empty")
}
iam, err := es.IAMByID(ctx, text.AggregateID)
if err != nil {
return nil, nil, err
}
repoIam := model.IAMFromModel(iam)
mailText := model.MailTextFromModel(text)
addAggregate := MailTextAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, mailText)
aggregate, err := addAggregate(ctx)
if err != nil {
return nil, nil, err
}
return repoIam, aggregate, nil
}
func (es *IAMEventstore) AddMailText(ctx context.Context, text *iam_model.MailText) (*iam_model.MailText, error) {
repoIam, addAggregate, err := es.PrepareAddMailText(ctx, text)
if err != nil {
return nil, err
}
err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate)
if err != nil {
return nil, err
}
es.iamCache.cacheIAM(repoIam)
if _, m := model.GetMailText(repoIam.DefaultMailTexts, text.MailTextType, text.Language); m != nil {
return model.MailTextToModel(m), nil
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-9AwUm", "Errors.Internal")
}
func (es *IAMEventstore) ChangeMailText(ctx context.Context, text *iam_model.MailText) (*iam_model.MailText, error) {
if !text.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-J5xbB", "Errors.IAM.MailTextInvalid")
}
existing, err := es.IAMByID(ctx, text.AggregateID)
if err != nil {
return nil, err
}
if _, m := existing.GetDefaultMailText(text.MailTextType, text.Language); m == nil {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-0CTV3", "Errors.IAM.MailTextNotExisting")
}
repoIam := model.IAMFromModel(existing)
repoMember := model.MailTextFromModel(text)
projectAggregate := MailTextChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMember)
err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, projectAggregate)
es.iamCache.cacheIAM(repoIam)
if _, m := model.GetMailText(repoIam.DefaultMailTexts, text.MailTextType, text.Language); m != nil {
return model.MailTextToModel(m), nil
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-HawVx", "Errors.Internal")
}

View File

@@ -216,3 +216,29 @@ func GetMockManipulateIAMWithLabelPolicy(ctrl *gomock.Controller) *IAMEventstore
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstore(ctrl, mockEs)
}
func GetMockManipulateIAMWithMailTemplate(ctrl *gomock.Controller) *IAMEventstore {
mailTemplate, _ := json.Marshal(model.MailTemplate{Template: []byte("<!doctype htm>")})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.IAMSetupStarted},
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.MailTemplateAdded, Data: mailTemplate},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstore(ctrl, mockEs)
}
func GetMockManipulateIAMWithMailText(ctrl *gomock.Controller) *IAMEventstore {
mailText, _ := json.Marshal(model.MailText{MailTextType: "Type", Language: "DE"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.IAMSetupStarted},
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.MailTextAdded, Data: mailText},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstore(ctrl, mockEs)
}

View File

@@ -2815,3 +2815,321 @@ func TestChangeOrgIAMPolicy(t *testing.T) {
})
}
}
func TestAddMailTemplate(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *IAMEventstore
ctx context.Context
policy *iam_model.MailTemplate
}
type res struct {
result *iam_model.MailTemplate
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "add mailtemplate, ok",
args: args{
es: GetMockManipulateIAM(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
Template: []byte("<!doctype html>"),
},
},
res: res{
result: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
Template: []byte("<!doctype html>"),
},
},
},
{
name: "invalid policy",
args: args{
es: GetMockManipulateIAM(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing iam not found",
args: args{
es: GetMockManipulateIAMNotExisting(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.AddMailTemplate(tt.args.ctx, tt.args.policy)
if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) {
t.Errorf("got wrong err: %v ", err)
return
}
if tt.res.wantErr && tt.res.errFunc(err) {
return
}
if string(result.Template) != string(tt.res.result.Template) {
t.Errorf("got wrong result Template: expected: %v, actual: %v ", tt.res.result.Template, result.Template)
}
})
}
}
func TestChangeMailTemplate(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *IAMEventstore
ctx context.Context
template *iam_model.MailTemplate
}
type res struct {
result *iam_model.MailTemplate
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "add mail template, ok",
args: args{
es: GetMockManipulateIAMWithMailTemplate(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
template: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
Template: []byte("<!doctype html>"),
},
},
res: res{
result: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
Template: []byte("<!doctype html>"),
},
},
},
{
name: "invalid mail template",
args: args{
es: GetMockManipulateIAM(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
template: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing iam not found",
args: args{
es: GetMockManipulateIAMNotExisting(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
template: &iam_model.MailTemplate{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ChangeMailTemplate(tt.args.ctx, tt.args.template)
if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) {
t.Errorf("got wrong err: %v ", err)
return
}
if tt.res.wantErr && tt.res.errFunc(err) {
return
}
if string(result.Template) != string(tt.res.result.Template) {
t.Errorf("got wrong result Template: expected: %v, actual: %v ", tt.res.result.Template, result.Template)
}
})
}
}
func TestAddMailText(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *IAMEventstore
ctx context.Context
policy *iam_model.MailText
}
type res struct {
result *iam_model.MailText
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "add mailtemplate, ok",
args: args{
es: GetMockManipulateIAM(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
MailTextType: "Type", Language: "DE",
},
},
res: res{
result: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
MailTextType: "Type", Language: "DE",
},
},
},
{
name: "invalid policy",
args: args{
es: GetMockManipulateIAM(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing iam not found",
args: args{
es: GetMockManipulateIAMNotExisting(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.AddMailText(tt.args.ctx, tt.args.policy)
if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) {
t.Errorf("got wrong err: %v ", err)
return
}
if tt.res.wantErr && tt.res.errFunc(err) {
return
}
if string(result.MailTextType) != string(tt.res.result.MailTextType) {
t.Errorf("got wrong result MailTextType: expected: %v, actual: %v ", tt.res.result.MailTextType, result.MailTextType)
}
})
}
}
func TestChangeMailText(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *IAMEventstore
ctx context.Context
policy *iam_model.MailText
}
type res struct {
result *iam_model.MailText
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "change mailtemplate, ok",
args: args{
es: GetMockManipulateIAMWithMailText(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
MailTextType: "Type", Language: "DE",
},
},
res: res{
result: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
MailTextType: "Type", Language: "DE",
},
},
},
{
name: "invalid policy",
args: args{
es: GetMockManipulateIAM(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing iam not found",
args: args{
es: GetMockManipulateIAMNotExisting(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
policy: &iam_model.MailText{
ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 0},
},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ChangeMailText(tt.args.ctx, tt.args.policy)
if (tt.res.wantErr && !tt.res.errFunc(err)) || (err != nil && !tt.res.wantErr) {
t.Errorf("got wrong err: %v ", err)
return
}
if tt.res.wantErr && tt.res.errFunc(err) {
return
}
if string(result.MailTextType) != string(tt.res.result.MailTextType) {
t.Errorf("got wrong result MailTextType: expected: %v, actual: %v ", tt.res.result.MailTextType, result.MailTextType)
}
})
}
}

View File

@@ -232,6 +232,7 @@ func OIDCIDPConfigChangedAggregate(aggCreator *es_models.AggregateCreator, exist
return agg.AppendEvent(model.OIDCIDPConfigChanged, changes)
}
}
func LabelPolicyAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.IAM, policy *model.LabelPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if policy == nil {
@@ -678,6 +679,101 @@ func checkExistingLoginPolicyIDPProviderValidation(idpConfigID string) func(...*
}
}
func MailTemplateAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.IAM, template *model.MailTemplate) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if template == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-ZCfDS", "Errors.Internal")
}
agg, err := IAMAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
validationQuery := es_models.NewSearchQuery().
AggregateTypeFilter(model.IAMAggregate).
EventTypesFilter(model.MailTemplateAdded).
AggregateIDFilter(existing.AggregateID)
validation := checkExistingMailTemplateValidation()
agg.SetPrecondition(validationQuery, validation)
return agg.AppendEvent(model.MailTemplateAdded, template)
}
}
func checkExistingMailTemplateValidation() func(...*es_models.Event) error {
return func(events ...*es_models.Event) error {
for _, event := range events {
switch event.Type {
case model.MailTemplateAdded:
return errors.ThrowPreconditionFailed(nil, "EVENT-uKPiJ", "Errors.IAM.MailTemplate.AlreadyExists")
}
}
return nil
}
}
func MailTemplateChangedAggregate(aggCreator *es_models.AggregateCreator, existing *model.IAM, template *model.MailTemplate) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if template == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-s4PVD", "Errors.Internal")
}
agg, err := IAMAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
changes := existing.DefaultMailTemplate.Changes(template)
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-hxxSm", "Errors.NoChangesFound")
}
return agg.AppendEvent(model.MailTemplateChanged, changes)
}
}
func MailTextAddedAggregate(aggCreator *es_models.AggregateCreator, existing *model.IAM, text *model.MailText) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if text == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-ZCfDS", "Errors.Internal")
}
agg, err := IAMAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
validationQuery := es_models.NewSearchQuery().
AggregateTypeFilter(model.IAMAggregate).
EventTypesFilter(model.MailTextAdded).
AggregateIDFilter(existing.AggregateID)
validation := checkExistingMailTextValidation()
agg.SetPrecondition(validationQuery, validation)
return agg.AppendEvent(model.MailTextAdded, text)
}
}
func checkExistingMailTextValidation() func(...*es_models.Event) error {
return func(events ...*es_models.Event) error {
for _, event := range events {
switch event.Type {
case model.MailTextAdded:
return errors.ThrowPreconditionFailed(nil, "EVENT-ijzeq", "Errors.IAM.MailText.AlreadyExists")
}
}
return nil
}
}
func MailTextChangedAggregate(aggCreator *es_models.AggregateCreator, existingIAM *model.IAM, text *model.MailText) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if text == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-mgYpV", "Errors.Internal")
}
agg, err := IAMAggregate(ctx, aggCreator, existingIAM)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.MailTextChanged, text)
}
}
func checkExistingLoginPolicySecondFactorValidation(mfaType int32) func(...*es_models.Event) error {
return func(events ...*es_models.Event) error {
mfas := make([]int32, 0)

View File

@@ -31,6 +31,8 @@ type IAM struct {
IDPs []*IDPConfig `json:"-"`
DefaultLoginPolicy *LoginPolicy `json:"-"`
DefaultLabelPolicy *LabelPolicy `json:"-"`
DefaultMailTemplate *MailTemplate `json:"-"`
DefaultMailTexts []*MailText `json:"-"`
DefaultOrgIAMPolicy *OrgIAMPolicy `json:"-"`
DefaultPasswordComplexityPolicy *PasswordComplexityPolicy `json:"-"`
DefaultPasswordAgePolicy *PasswordAgePolicy `json:"-"`
@@ -40,14 +42,16 @@ type IAM struct {
func IAMFromModel(iam *model.IAM) *IAM {
members := IAMMembersFromModel(iam.Members)
idps := IDPConfigsFromModel(iam.IDPs)
mailTexts := MailTextsFromModel(iam.DefaultMailTexts)
converted := &IAM{
ObjectRoot: iam.ObjectRoot,
SetUpStarted: Step(iam.SetUpStarted),
SetUpDone: Step(iam.SetUpDone),
GlobalOrgID: iam.GlobalOrgID,
IAMProjectID: iam.IAMProjectID,
Members: members,
IDPs: idps,
ObjectRoot: iam.ObjectRoot,
SetUpStarted: Step(iam.SetUpStarted),
SetUpDone: Step(iam.SetUpDone),
GlobalOrgID: iam.GlobalOrgID,
IAMProjectID: iam.IAMProjectID,
Members: members,
IDPs: idps,
DefaultMailTexts: mailTexts,
}
if iam.DefaultLoginPolicy != nil {
converted.DefaultLoginPolicy = LoginPolicyFromModel(iam.DefaultLoginPolicy)
@@ -55,6 +59,9 @@ func IAMFromModel(iam *model.IAM) *IAM {
if iam.DefaultLabelPolicy != nil {
converted.DefaultLabelPolicy = LabelPolicyFromModel(iam.DefaultLabelPolicy)
}
if iam.DefaultMailTemplate != nil {
converted.DefaultMailTemplate = MailTemplateFromModel(iam.DefaultMailTemplate)
}
if iam.DefaultPasswordComplexityPolicy != nil {
converted.DefaultPasswordComplexityPolicy = PasswordComplexityPolicyFromModel(iam.DefaultPasswordComplexityPolicy)
}
@@ -73,14 +80,16 @@ func IAMFromModel(iam *model.IAM) *IAM {
func IAMToModel(iam *IAM) *model.IAM {
members := IAMMembersToModel(iam.Members)
idps := IDPConfigsToModel(iam.IDPs)
mailTexts := MailTextsToModel(iam.DefaultMailTexts)
converted := &model.IAM{
ObjectRoot: iam.ObjectRoot,
SetUpStarted: model.Step(iam.SetUpStarted),
SetUpDone: model.Step(iam.SetUpDone),
GlobalOrgID: iam.GlobalOrgID,
IAMProjectID: iam.IAMProjectID,
Members: members,
IDPs: idps,
ObjectRoot: iam.ObjectRoot,
SetUpStarted: model.Step(iam.SetUpStarted),
SetUpDone: model.Step(iam.SetUpDone),
GlobalOrgID: iam.GlobalOrgID,
IAMProjectID: iam.IAMProjectID,
Members: members,
IDPs: idps,
DefaultMailTexts: mailTexts,
}
if iam.DefaultLoginPolicy != nil {
converted.DefaultLoginPolicy = LoginPolicyToModel(iam.DefaultLoginPolicy)
@@ -88,6 +97,9 @@ func IAMToModel(iam *IAM) *model.IAM {
if iam.DefaultLabelPolicy != nil {
converted.DefaultLabelPolicy = LabelPolicyToModel(iam.DefaultLabelPolicy)
}
if iam.DefaultMailTemplate != nil {
converted.DefaultMailTemplate = MailTemplateToModel(iam.DefaultMailTemplate)
}
if iam.DefaultPasswordComplexityPolicy != nil {
converted.DefaultPasswordComplexityPolicy = PasswordComplexityPolicyToModel(iam.DefaultPasswordComplexityPolicy)
}
@@ -180,6 +192,14 @@ func (i *IAM) AppendEvent(event *es_models.Event) (err error) {
return i.appendAddLabelPolicyEvent(event)
case LabelPolicyChanged:
return i.appendChangeLabelPolicyEvent(event)
case MailTemplateAdded:
return i.appendAddMailTemplateEvent(event)
case MailTemplateChanged:
return i.appendChangeMailTemplateEvent(event)
case MailTextAdded:
return i.appendAddMailTextEvent(event)
case MailTextChanged:
return i.appendChangeMailTextEvent(event)
case PasswordComplexityPolicyAdded:
return i.appendAddPasswordComplexityPolicyEvent(event)
case PasswordComplexityPolicyChanged:

View File

@@ -0,0 +1,64 @@
package model
import (
b64 "encoding/base64"
"encoding/json"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type MailTemplate struct {
models.ObjectRoot
State int32 `json:"-"`
Template []byte
}
func MailTemplateToModel(template *MailTemplate) *iam_model.MailTemplate {
return &iam_model.MailTemplate{
ObjectRoot: template.ObjectRoot,
State: iam_model.PolicyState(template.State),
Template: template.Template,
}
}
func MailTemplateFromModel(template *iam_model.MailTemplate) *MailTemplate {
return &MailTemplate{
ObjectRoot: template.ObjectRoot,
State: int32(template.State),
Template: template.Template,
}
}
func (p *MailTemplate) Changes(changed *MailTemplate) map[string]interface{} {
changes := make(map[string]interface{}, 1)
if b64.StdEncoding.EncodeToString(changed.Template) != b64.StdEncoding.EncodeToString(p.Template) {
changes["template"] = b64.StdEncoding.EncodeToString(changed.Template)
}
return changes
}
func (i *IAM) appendAddMailTemplateEvent(event *es_models.Event) error {
i.DefaultMailTemplate = new(MailTemplate)
err := i.DefaultMailTemplate.SetDataLabel(event)
if err != nil {
return err
}
i.DefaultMailTemplate.ObjectRoot.CreationDate = event.CreationDate
return nil
}
func (i *IAM) appendChangeMailTemplateEvent(event *es_models.Event) error {
return i.DefaultMailTemplate.SetDataLabel(event)
}
func (p *MailTemplate) SetDataLabel(event *es_models.Event) error {
err := json.Unmarshal(event.Data, p)
if err != nil {
return errors.ThrowInternal(err, "MODEL-ikjhf", "unable to unmarshal data")
}
return nil
}

View File

@@ -0,0 +1,126 @@
package model
import (
"encoding/json"
"testing"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
func TestMailTemplateChanges(t *testing.T) {
type args struct {
existing *MailTemplate
new *MailTemplate
}
type res struct {
changesLen int
}
tests := []struct {
name string
args args
res res
}{
{
name: "mailtemplate all attributes change",
args: args{
existing: &MailTemplate{Template: []byte("<doctype html>")},
new: &MailTemplate{Template: []byte("<!doctype html>")},
},
res: res{
changesLen: 1,
},
},
{
name: "no changes",
args: args{
existing: &MailTemplate{Template: []byte("<!doctype html>")},
new: &MailTemplate{Template: []byte("<!doctype html>")},
},
res: res{
changesLen: 0,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
changes := tt.args.existing.Changes(tt.args.new)
if len(changes) != tt.res.changesLen {
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
}
})
}
}
func TestAppendAddMailTemplateEvent(t *testing.T) {
type args struct {
iam *IAM
policy *MailTemplate
event *es_models.Event
}
tests := []struct {
name string
args args
result *IAM
}{
{
name: "append add label policy event",
args: args{
iam: new(IAM),
policy: &MailTemplate{Template: []byte("<!doctype html>")},
event: new(es_models.Event),
},
result: &IAM{DefaultMailTemplate: &MailTemplate{Template: []byte("<!doctype html>")}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.policy != nil {
data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data
}
tt.args.iam.appendAddMailTemplateEvent(tt.args.event)
if string(tt.result.DefaultMailTemplate.Template) != string(tt.args.iam.DefaultMailTemplate.Template) {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultMailTemplate.Template, tt.args.iam.DefaultMailTemplate.Template)
}
})
}
}
func TestAppendChangeMailTemplateEvent(t *testing.T) {
type args struct {
iam *IAM
policy *MailTemplate
event *es_models.Event
}
tests := []struct {
name string
args args
result *IAM
}{
{
name: "append change label policy event",
args: args{
iam: &IAM{DefaultMailTemplate: &MailTemplate{
Template: []byte("<doctype html>"),
}},
policy: &MailTemplate{Template: []byte("<!doctype html>")},
event: &es_models.Event{},
},
result: &IAM{DefaultMailTemplate: &MailTemplate{
Template: []byte("<!doctype html>"),
}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.policy != nil {
data, _ := json.Marshal(tt.args.policy)
tt.args.event.Data = data
}
tt.args.iam.appendChangeMailTemplateEvent(tt.args.event)
if string(tt.result.DefaultMailTemplate.Template) != string(tt.args.iam.DefaultMailTemplate.Template) {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultMailTemplate.Template, tt.args.iam.DefaultMailTemplate.Template)
}
})
}
}

View File

@@ -0,0 +1,157 @@
package model
import (
"encoding/json"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
type MailText struct {
models.ObjectRoot
State int32 `json:"-"`
MailTextType string
Language string
Title string
PreHeader string
Subject string
Greeting string
Text string
ButtonText string
}
func GetMailText(mailTexts []*MailText, mailTextType string, language string) (int, *MailText) {
for i, m := range mailTexts {
if m.MailTextType == mailTextType && m.Language == language {
return i, m
}
}
return -1, nil
}
func MailTextsToModel(mailTexts []*MailText) []*iam_model.MailText {
convertedMailTexts := make([]*iam_model.MailText, len(mailTexts))
for i, m := range mailTexts {
convertedMailTexts[i] = MailTextToModel(m)
}
return convertedMailTexts
}
func MailTextToModel(mailText *MailText) *iam_model.MailText {
return &iam_model.MailText{
ObjectRoot: mailText.ObjectRoot,
State: iam_model.PolicyState(mailText.State),
MailTextType: mailText.MailTextType,
Language: mailText.Language,
Title: mailText.Title,
PreHeader: mailText.PreHeader,
Subject: mailText.Subject,
Greeting: mailText.Greeting,
Text: mailText.Text,
ButtonText: mailText.ButtonText,
}
}
func MailTextsFromModel(mailTexts []*iam_model.MailText) []*MailText {
convertedMailTexts := make([]*MailText, len(mailTexts))
for i, m := range mailTexts {
convertedMailTexts[i] = MailTextFromModel(m)
}
return convertedMailTexts
}
func MailTextFromModel(mailText *iam_model.MailText) *MailText {
return &MailText{
ObjectRoot: mailText.ObjectRoot,
State: int32(mailText.State),
MailTextType: mailText.MailTextType,
Language: mailText.Language,
Title: mailText.Title,
PreHeader: mailText.PreHeader,
Subject: mailText.Subject,
Greeting: mailText.Greeting,
Text: mailText.Text,
ButtonText: mailText.ButtonText,
}
}
func (p *MailText) Changes(changed *MailText) map[string]interface{} {
changes := make(map[string]interface{}, 8)
changes["mailTextType"] = changed.MailTextType
changes["language"] = changed.Language
if changed.Title != p.Title {
changes["title"] = changed.Title
}
if changed.PreHeader != p.PreHeader {
changes["preHeader"] = changed.PreHeader
}
if changed.Subject != p.Subject {
changes["subject"] = changed.Subject
}
if changed.Greeting != p.Greeting {
changes["greeting"] = changed.Greeting
}
if changed.Text != p.Text {
changes["text"] = changed.Text
}
if changed.ButtonText != p.ButtonText {
changes["buttonText"] = changed.ButtonText
}
return changes
}
func (i *IAM) appendAddMailTextEvent(event *es_models.Event) error {
mailText := &MailText{}
err := mailText.SetDataLabel(event)
if err != nil {
return err
}
mailText.ObjectRoot.CreationDate = event.CreationDate
i.DefaultMailTexts = append(i.DefaultMailTexts, mailText)
return nil
}
func (i *IAM) appendChangeMailTextEvent(event *es_models.Event) error {
mailText := &MailText{}
err := mailText.SetDataLabel(event)
if err != nil {
return err
}
if n, m := GetMailText(i.DefaultMailTexts, mailText.MailTextType, mailText.Language); m != nil {
i.DefaultMailTexts[n] = mailText
}
return nil
}
func (i *IAM) appendRemoveMailTextEvent(event *es_models.Event) error {
mailText := &MailText{}
err := mailText.SetDataLabel(event)
if err != nil {
return err
}
if n, m := GetMailText(i.DefaultMailTexts, mailText.MailTextType, mailText.Language); m != nil {
i.DefaultMailTexts[n] = i.DefaultMailTexts[len(i.DefaultMailTexts)-1]
i.DefaultMailTexts[len(i.DefaultMailTexts)-1] = nil
i.DefaultMailTexts = i.DefaultMailTexts[:len(i.DefaultMailTexts)-1]
}
return nil
}
func (p *MailText) SetDataLabel(event *es_models.Event) error {
err := json.Unmarshal(event.Data, p)
if err != nil {
return errors.ThrowInternal(err, "MODEL-3FUV5", "unable to unmarshal data")
}
return nil
}

View File

@@ -0,0 +1,134 @@
package model
import (
"encoding/json"
"testing"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
func TestAppendAddMailTextEvent(t *testing.T) {
type args struct {
iam *IAM
mailText *MailText
event *es_models.Event
}
tests := []struct {
name string
args args
result *IAM
}{
{
name: "append add mailText event",
args: args{
iam: &IAM{},
mailText: &MailText{
MailTextType: "PasswordReset",
Language: "DE"},
event: &es_models.Event{},
},
result: &IAM{DefaultMailTexts: []*MailText{&MailText{
MailTextType: "PasswordReset",
Language: "DE"}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.mailText != nil {
data, _ := json.Marshal(tt.args.mailText)
tt.args.event.Data = data
}
tt.args.iam.appendAddMailTextEvent(tt.args.event)
if len(tt.args.iam.DefaultMailTexts) != 1 {
t.Errorf("got wrong result should have one mailText actual: %v ", len(tt.args.iam.DefaultMailTexts))
}
if tt.args.iam.DefaultMailTexts[0] == tt.result.DefaultMailTexts[0] {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultMailTexts[0], tt.args.iam.DefaultMailTexts[0])
}
})
}
}
func TestAppendChangeMailTextEvent(t *testing.T) {
type args struct {
iam *IAM
mailText *MailText
event *es_models.Event
}
tests := []struct {
name string
args args
result *IAM
}{
{
name: "append change mailText event",
args: args{
iam: &IAM{DefaultMailTexts: []*MailText{&MailText{
MailTextType: "PasswordReset",
Language: "DE"}}},
mailText: &MailText{
MailTextType: "ChangedPasswordReset",
Language: "DE"},
event: &es_models.Event{},
},
result: &IAM{DefaultMailTexts: []*MailText{&MailText{
MailTextType: "PasswordReset",
Language: "ChangedDE"}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.mailText != nil {
data, _ := json.Marshal(tt.args.mailText)
tt.args.event.Data = data
}
tt.args.iam.appendChangeMailTextEvent(tt.args.event)
if len(tt.args.iam.DefaultMailTexts) != 1 {
t.Errorf("got wrong result should have one mailText actual: %v ", len(tt.args.iam.DefaultMailTexts))
}
if tt.args.iam.DefaultMailTexts[0] == tt.result.DefaultMailTexts[0] {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.DefaultMailTexts[0], tt.args.iam.DefaultMailTexts[0])
}
})
}
}
func TestAppendRemoveMailTextEvent(t *testing.T) {
type args struct {
iam *IAM
mailText *MailText
event *es_models.Event
}
tests := []struct {
name string
args args
result *IAM
}{
{
name: "append remove mailText event",
args: args{
iam: &IAM{DefaultMailTexts: []*MailText{&MailText{
MailTextType: "PasswordReset",
Language: "DE",
Subject: "Subject"}}},
mailText: &MailText{
MailTextType: "PasswordReset",
Language: "DE"},
event: &es_models.Event{},
},
result: &IAM{DefaultMailTexts: []*MailText{}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.mailText != nil {
data, _ := json.Marshal(tt.args.mailText)
tt.args.event.Data = data
}
tt.args.iam.appendRemoveMailTextEvent(tt.args.event)
if len(tt.args.iam.DefaultMailTexts) != 0 {
t.Errorf("got wrong result should have no mailText actual: %v ", len(tt.args.iam.DefaultMailTexts))
}
})
}
}

View File

@@ -38,6 +38,11 @@ const (
LabelPolicyAdded models.EventType = "iam.policy.label.added"
LabelPolicyChanged models.EventType = "iam.policy.label.changed"
MailTemplateAdded models.EventType = "iam.mail.template.added"
MailTemplateChanged models.EventType = "iam.mail.template.changed"
MailTextAdded models.EventType = "iam.mail.text.added"
MailTextChanged models.EventType = "iam.mail.text.changed"
PasswordComplexityPolicyAdded models.EventType = "iam.policy.password.complexity.added"
PasswordComplexityPolicyChanged models.EventType = "iam.policy.password.complexity.changed"

View File

@@ -11,8 +11,8 @@ import (
func IDPByID(db *gorm.DB, table, idpID string) (*model.IDPConfigView, error) {
idp := new(model.IDPConfigView)
userIDQuery := &model.IDPConfigSearchQuery{Key: iam_model.IDPConfigSearchKeyIdpConfigID, Value: idpID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
idpIDQuery := &model.IDPConfigSearchQuery{Key: iam_model.IDPConfigSearchKeyIdpConfigID, Value: idpID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, idpIDQuery)
err := query(db, idp)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Ahq2s", "Errors.IAM.IdpNotExisting")

View File

@@ -11,8 +11,8 @@ import (
func GetLabelPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.LabelPolicyView, error) {
policy := new(model.LabelPolicyView)
userIDQuery := &model.LabelPolicySearchQuery{Key: iam_model.LabelPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
aggregateIDQuery := &model.LabelPolicySearchQuery{Key: iam_model.LabelPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-68G11", "Errors.IAM.LabelPolicy.NotExisting")

View File

@@ -11,8 +11,8 @@ import (
func GetLoginPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.LoginPolicyView, error) {
policy := new(model.LoginPolicyView)
userIDQuery := &model.LoginPolicySearchQuery{Key: iam_model.LoginPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
aggregateIDQuery := &model.LoginPolicySearchQuery{Key: iam_model.LoginPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Lso0cs", "Errors.IAM.LoginPolicy.NotExisting")

View File

@@ -0,0 +1,32 @@
package view
import (
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/view/repository"
"github.com/jinzhu/gorm"
)
func GetMailTemplateByAggregateID(db *gorm.DB, table, aggregateID string) (*model.MailTemplateView, error) {
template := new(model.MailTemplateView)
aggregateIDQuery := &model.MailTemplateSearchQuery{Key: iam_model.MailTemplateSearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, template)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-iPnmU", "Errors.IAM.MailTemplate.NotExisting")
}
return template, err
}
func PutMailTemplate(db *gorm.DB, table string, template *model.MailTemplateView) error {
save := repository.PrepareSave(table)
return save(db, template)
}
func DeleteMailTemplate(db *gorm.DB, table, aggregateID string) error {
delete := repository.PrepareDeleteByKey(table, model.MailTemplateSearchKey(iam_model.MailTemplateSearchKeyAggregateID), aggregateID)
return delete(db)
}

View File

@@ -0,0 +1,53 @@
package view
import (
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/iam/repository/view/model"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/view/repository"
"github.com/jinzhu/gorm"
)
func GetMailTexts(db *gorm.DB, table string, aggregateID string) ([]*model.MailTextView, error) {
texts := make([]*model.MailTextView, 0)
queries := []*iam_model.MailTextSearchQuery{
{
Key: iam_model.MailTextSearchKeyAggregateID,
Value: aggregateID,
Method: global_model.SearchMethodEquals,
},
}
query := repository.PrepareSearchQuery(table, model.MailTextSearchRequest{Queries: queries})
_, err := query(db, &texts)
if err != nil {
return nil, err
}
return texts, nil
}
func GetMailTextByIDs(db *gorm.DB, table, aggregateID string, textType string, language string) (*model.MailTextView, error) {
mailText := new(model.MailTextView)
aggregateIDQuery := &model.MailTextSearchQuery{Key: iam_model.MailTextSearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
textTypeQuery := &model.MailTextSearchQuery{Key: iam_model.MailTextSearchKeyMailTextType, Value: textType, Method: global_model.SearchMethodEquals}
languageQuery := &model.MailTextSearchQuery{Key: iam_model.MailTextSearchKeyLanguage, Value: language, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery, textTypeQuery, languageQuery)
err := query(db, mailText)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-IiJjm", "Errors.IAM.MailText.NotExisting")
}
return mailText, err
}
func PutMailText(db *gorm.DB, table string, mailText *model.MailTextView) error {
save := repository.PrepareSave(table)
return save(db, mailText)
}
func DeleteMailText(db *gorm.DB, table, aggregateID string, textType string, language string) error {
aggregateIDSearch := repository.Key{Key: model.MailTextSearchKey(iam_model.MailTextSearchKeyAggregateID), Value: aggregateID}
textTypeSearch := repository.Key{Key: model.MailTextSearchKey(iam_model.MailTextSearchKeyMailTextType), Value: textType}
languageSearch := repository.Key{Key: model.MailTextSearchKey(iam_model.MailTextSearchKeyLanguage), Value: language}
delete := repository.PrepareDeleteByKeys(table, aggregateIDSearch, textTypeSearch, languageSearch)
return delete(db)
}

View File

@@ -0,0 +1,80 @@
package model
import (
"encoding/json"
"time"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/iam/model"
)
const (
MailTemplateKeyAggregateID = "aggregate_id"
)
type MailTemplateView struct {
AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
State int32 `json:"-" gorm:"column:mail_template_state"`
Template []byte `json:"template" gorm:"column:template"`
Default bool `json:"-" gorm:"-"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
func MailTemplateViewFromModel(template *model.MailTemplateView) *MailTemplateView {
return &MailTemplateView{
AggregateID: template.AggregateID,
Sequence: template.Sequence,
CreationDate: template.CreationDate,
ChangeDate: template.ChangeDate,
Template: template.Template,
Default: template.Default,
}
}
func MailTemplateViewToModel(template *MailTemplateView) *model.MailTemplateView {
return &model.MailTemplateView{
AggregateID: template.AggregateID,
Sequence: template.Sequence,
CreationDate: template.CreationDate,
ChangeDate: template.ChangeDate,
Template: template.Template,
Default: template.Default,
}
}
func (i *MailTemplateView) AppendEvent(event *models.Event) (err error) {
i.Sequence = event.Sequence
i.ChangeDate = event.CreationDate
switch event.Type {
case es_model.MailTemplateAdded, org_es_model.MailTemplateAdded:
i.setRootData(event)
i.CreationDate = event.CreationDate
err = i.SetData(event)
case es_model.MailTemplateChanged, org_es_model.MailTemplateChanged:
i.ChangeDate = event.CreationDate
err = i.SetData(event)
}
return err
}
func (r *MailTemplateView) setRootData(event *models.Event) {
r.AggregateID = event.AggregateID
}
func (r *MailTemplateView) SetData(event *models.Event) error {
if err := json.Unmarshal(event.Data, r); err != nil {
logging.Log("MODEL-YDZmZ").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-sKWwO", "Could not unmarshal data")
}
return nil
}

View File

@@ -0,0 +1,59 @@
package model
import (
iam_model "github.com/caos/zitadel/internal/iam/model"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/view/repository"
)
type MailTemplateSearchRequest iam_model.MailTemplateSearchRequest
type MailTemplateSearchQuery iam_model.MailTemplateSearchQuery
type MailTemplateSearchKey iam_model.MailTemplateSearchKey
func (req MailTemplateSearchRequest) GetLimit() uint64 {
return req.Limit
}
func (req MailTemplateSearchRequest) GetOffset() uint64 {
return req.Offset
}
func (req MailTemplateSearchRequest) GetSortingColumn() repository.ColumnKey {
if req.SortingColumn == iam_model.MailTemplateSearchKeyUnspecified {
return nil
}
return MailTemplateSearchKey(req.SortingColumn)
}
func (req MailTemplateSearchRequest) GetAsc() bool {
return req.Asc
}
func (req MailTemplateSearchRequest) GetQueries() []repository.SearchQuery {
result := make([]repository.SearchQuery, len(req.Queries))
for i, q := range req.Queries {
result[i] = MailTemplateSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
}
return result
}
func (req MailTemplateSearchQuery) GetKey() repository.ColumnKey {
return MailTemplateSearchKey(req.Key)
}
func (req MailTemplateSearchQuery) GetMethod() global_model.SearchMethod {
return req.Method
}
func (req MailTemplateSearchQuery) GetValue() interface{} {
return req.Value
}
func (key MailTemplateSearchKey) ToColumnName() string {
switch iam_model.MailTemplateSearchKey(key) {
case iam_model.MailTemplateSearchKeyAggregateID:
return MailTemplateKeyAggregateID
default:
return ""
}
}

View File

@@ -0,0 +1,117 @@
package model
import (
"encoding/json"
"time"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/iam/model"
)
const (
MailTextKeyAggregateID = "aggregate_id"
MailTextKeyMailTextType = "mail_text_type"
MailTextKeyLanguage = "language"
)
type MailTextView struct {
AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
State int32 `json:"-" gorm:"column:mail_text_state"`
MailTextType string `json:"mailTextType" gorm:"column:mail_text_type;primary_key"`
Language string `json:"language" gorm:"column:language;primary_key"`
Title string `json:"title" gorm:"column:title"`
PreHeader string `json:"preHeader" gorm:"column:pre_header"`
Subject string `json:"subject" gorm:"column:subject"`
Greeting string `json:"greeting" gorm:"column:greeting"`
Text string `json:"text" gorm:"column:text"`
ButtonText string `json:"buttonText" gorm:"column:button_text"`
Default bool `json:"-" gorm:"-"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
func MailTextViewFromModel(template *model.MailTextView) *MailTextView {
return &MailTextView{
AggregateID: template.AggregateID,
Sequence: template.Sequence,
CreationDate: template.CreationDate,
ChangeDate: template.ChangeDate,
MailTextType: template.MailTextType,
Language: template.Language,
Title: template.Title,
PreHeader: template.PreHeader,
Subject: template.Subject,
Greeting: template.Greeting,
Text: template.Text,
ButtonText: template.ButtonText,
Default: template.Default,
}
}
func MailTextsViewToModel(textsIn []*MailTextView, defaultIn bool) *model.MailTextsView {
return &model.MailTextsView{
Texts: mailTextsViewToModelArr(textsIn, defaultIn),
}
}
func mailTextsViewToModelArr(texts []*MailTextView, defaultIn bool) []*model.MailTextView {
result := make([]*model.MailTextView, len(texts))
for i, r := range texts {
r.Default = defaultIn
result[i] = MailTextViewToModel(r)
}
return result
}
func MailTextViewToModel(template *MailTextView) *model.MailTextView {
return &model.MailTextView{
AggregateID: template.AggregateID,
Sequence: template.Sequence,
CreationDate: template.CreationDate,
ChangeDate: template.ChangeDate,
MailTextType: template.MailTextType,
Language: template.Language,
Title: template.Title,
PreHeader: template.PreHeader,
Subject: template.Subject,
Greeting: template.Greeting,
Text: template.Text,
ButtonText: template.ButtonText,
Default: template.Default,
}
}
func (i *MailTextView) AppendEvent(event *models.Event) (err error) {
i.Sequence = event.Sequence
switch event.Type {
case es_model.MailTextAdded, org_es_model.MailTextAdded:
i.setRootData(event)
i.CreationDate = event.CreationDate
err = i.SetData(event)
case es_model.MailTextChanged, org_es_model.MailTextChanged:
i.ChangeDate = event.CreationDate
err = i.SetData(event)
}
return err
}
func (r *MailTextView) setRootData(event *models.Event) {
r.AggregateID = event.AggregateID
}
func (r *MailTextView) SetData(event *models.Event) error {
if err := json.Unmarshal(event.Data, r); err != nil {
logging.Log("MODEL-UFqAG").WithError(err).Error("could not unmarshal event data")
return caos_errs.ThrowInternal(err, "MODEL-5CVaR", "Could not unmarshal data")
}
return nil
}

View File

@@ -0,0 +1,63 @@
package model
import (
iam_model "github.com/caos/zitadel/internal/iam/model"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/view/repository"
)
type MailTextSearchRequest iam_model.MailTextSearchRequest
type MailTextSearchQuery iam_model.MailTextSearchQuery
type MailTextSearchKey iam_model.MailTextSearchKey
func (req MailTextSearchRequest) GetLimit() uint64 {
return req.Limit
}
func (req MailTextSearchRequest) GetOffset() uint64 {
return req.Offset
}
func (req MailTextSearchRequest) GetSortingColumn() repository.ColumnKey {
if req.SortingColumn == iam_model.MailTextSearchKeyUnspecified {
return nil
}
return MailTextSearchKey(req.SortingColumn)
}
func (req MailTextSearchRequest) GetAsc() bool {
return req.Asc
}
func (req MailTextSearchRequest) GetQueries() []repository.SearchQuery {
result := make([]repository.SearchQuery, len(req.Queries))
for i, q := range req.Queries {
result[i] = MailTextSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method}
}
return result
}
func (req MailTextSearchQuery) GetKey() repository.ColumnKey {
return MailTextSearchKey(req.Key)
}
func (req MailTextSearchQuery) GetMethod() global_model.SearchMethod {
return req.Method
}
func (req MailTextSearchQuery) GetValue() interface{} {
return req.Value
}
func (key MailTextSearchKey) ToColumnName() string {
switch iam_model.MailTextSearchKey(key) {
case iam_model.MailTextSearchKeyAggregateID:
return MailTextKeyAggregateID
case iam_model.MailTextSearchKeyMailTextType:
return MailTextKeyMailTextType
case iam_model.MailTextSearchKeyLanguage:
return MailTextKeyLanguage
default:
return ""
}
}

View File

@@ -11,8 +11,8 @@ import (
func GetOrgIAMPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.OrgIAMPolicyView, error) {
policy := new(model.OrgIAMPolicyView)
userIDQuery := &model.OrgIAMPolicySearchQuery{Key: iam_model.OrgIAMPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
aggregateIDQuery := &model.OrgIAMPolicySearchQuery{Key: iam_model.OrgIAMPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-5fi9s", "Errors.IAM.OrgIAMPolicy.NotExisting")

View File

@@ -11,8 +11,8 @@ import (
func GetPasswordAgePolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.PasswordAgePolicyView, error) {
policy := new(model.PasswordAgePolicyView)
userIDQuery := &model.PasswordAgePolicySearchQuery{Key: iam_model.PasswordAgePolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
aggregateIDQuery := &model.PasswordAgePolicySearchQuery{Key: iam_model.PasswordAgePolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Lso0cs", "Errors.IAM.PasswordAgePolicy.NotExisting")

View File

@@ -11,8 +11,8 @@ import (
func GetPasswordComplexityPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.PasswordComplexityPolicyView, error) {
policy := new(model.PasswordComplexityPolicyView)
userIDQuery := &model.PasswordComplexityPolicySearchQuery{Key: iam_model.PasswordComplexityPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
aggregateIDQuery := &model.PasswordComplexityPolicySearchQuery{Key: iam_model.PasswordComplexityPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Lso0cs", "Errors.IAM.PasswordComplexityPolicy.NotExisting")

View File

@@ -11,8 +11,8 @@ import (
func GetPasswordLockoutPolicyByAggregateID(db *gorm.DB, table, aggregateID string) (*model.PasswordLockoutPolicyView, error) {
policy := new(model.PasswordLockoutPolicyView)
userIDQuery := &model.PasswordLockoutPolicySearchQuery{Key: iam_model.PasswordLockoutPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, userIDQuery)
aggregateIDQuery := &model.PasswordLockoutPolicySearchQuery{Key: iam_model.PasswordLockoutPolicySearchKeyAggregateID, Value: aggregateID, Method: global_model.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, aggregateIDQuery)
err := query(db, policy)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Lso0cs", "Errors.IAM.PasswordLockoutPolicy.NotExisting")