feat: Notification providers config (#3212)

* feat: add login check lifetimes to login policy

* feat: org features test

* feat: debug notificatiaon events

* feat: debug notification file/log commands

* feat: add requests to proto

* feat: add api for debug notification providers file/log

* feat: add projection for debug notifiication providers

* feat: requests

* feat: merge v2

* feat: add settings proto to generate

* feat: notifiaction providers

* fix: remove unused code

* Update iam_converter.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Fabi 2022-03-07 14:22:37 +01:00 committed by GitHub
parent 7d6c933485
commit 7899a0b851
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2489 additions and 200 deletions

View File

@ -152,6 +152,10 @@ protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,user.md \
${PROTO_PATH}/user.proto
protoc \
-I=/proto/include \
--doc_out=${DOCS_PATH} --doc_opt=${PROTO_PATH}/docs/zitadel-md.tmpl,settings.md \
${PROTO_PATH}/settings.proto
echo "done generating grpc"

View File

@ -171,7 +171,7 @@ func startZitadel(config *startConfig) error {
return fmt.Errorf("cannot start commands: %w", err)
}
notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, smtpPasswordCrypto)
notification.Start(config.Notification, config.SystemDefaults, commands, queries, dbClient, assets.HandlerPrefix, smtpPasswordCrypto, smsCrypto)
router := mux.NewRouter()
err = startAPIs(ctx, router, commands, queries, eventstoreClient, dbClient, keyChan, config, storage, authZRepo)

View File

@ -188,6 +188,30 @@ Update twilio sms provider token
PUT: /sms/twilio/{id}/token
### GetFileSystemNotificationProvider
> **rpc** GetFileSystemNotificationProvider([GetFileSystemNotificationProviderRequest](#getfilesystemnotificationproviderrequest))
[GetFileSystemNotificationProviderResponse](#getfilesystemnotificationproviderresponse)
Get file system notification provider
GET: /notification/provider/file
### GetLogNotificationProvider
> **rpc** GetLogNotificationProvider([GetLogNotificationProviderRequest](#getlognotificationproviderrequest))
[GetLogNotificationProviderResponse](#getlognotificationproviderresponse)
Get log notification provider
GET: /notification/provider/log
### GetOIDCSettings
> **rpc** GetOIDCSettings([GetOIDCSettingsRequest](#getoidcsettingsrequest))
@ -1997,6 +2021,23 @@ This is an empty request
### GetFileSystemNotificationProviderRequest
This is an empty request
### GetFileSystemNotificationProviderResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| provider | zitadel.settings.v1.DebugNotificationProvider | - | |
### GetIDPByIDRequest
@ -2053,6 +2094,23 @@ This is an empty request
### GetLogNotificationProviderRequest
This is an empty request
### GetLogNotificationProviderResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| provider | zitadel.settings.v1.DebugNotificationProvider | - | |
### GetLoginPolicyRequest
This is an empty request

View File

@ -0,0 +1,31 @@
package admin
import (
"context"
"github.com/caos/zitadel/internal/api/grpc/settings"
"github.com/caos/zitadel/internal/domain"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
)
func (s *Server) GetFileSystemNotificationProvider(ctx context.Context, req *admin_pb.GetFileSystemNotificationProviderRequest) (*admin_pb.GetFileSystemNotificationProviderResponse, error) {
result, err := s.query.NotificationProviderByIDAndType(ctx, domain.IAMID, domain.NotificationProviderTypeFile)
if err != nil {
return nil, err
}
return &admin_pb.GetFileSystemNotificationProviderResponse{
Provider: settings.NotificationProviderToPb(result),
}, nil
}
func (s *Server) GetLogNotificationProvider(ctx context.Context, req *admin_pb.GetLogNotificationProviderRequest) (*admin_pb.GetLogNotificationProviderResponse, error) {
result, err := s.query.NotificationProviderByIDAndType(ctx, domain.IAMID, domain.NotificationProviderTypeLog)
if err != nil {
return nil, err
}
return &admin_pb.GetLogNotificationProviderResponse{
Provider: settings.NotificationProviderToPb(result),
}, nil
}

View File

@ -0,0 +1,15 @@
package settings
import (
obj_pb "github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/query"
settings_pb "github.com/caos/zitadel/pkg/grpc/settings"
)
func NotificationProviderToPb(provider *query.DebugNotificationProvider) *settings_pb.DebugNotificationProvider {
mapped := &settings_pb.DebugNotificationProvider{
Compact: provider.Compact,
Details: obj_pb.ToViewDetailsPb(provider.Sequence, provider.CreationDate, provider.ChangeDate, provider.AggregateID),
}
return mapped
}

View File

@ -0,0 +1,31 @@
package command
import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/settings"
)
type DebugNotificationWriteModel struct {
eventstore.WriteModel
Compact bool
State domain.NotificationProviderState
}
func (wm *DebugNotificationWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *settings.DebugNotificationProviderAddedEvent:
wm.Compact = e.Compact
wm.State = domain.NotificationProviderStateActive
case *settings.DebugNotificationProviderChangedEvent:
if e.Compact != nil {
wm.Compact = *e.Compact
}
case *settings.DebugNotificationProviderRemovedEvent:
wm.State = domain.NotificationProviderStateRemoved
}
}
return wm.WriteModel.Reduce()
}

View File

@ -0,0 +1,120 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/notification/channels/fs"
iam_repo "github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddDebugNotificationProviderFile(ctx context.Context, fileSystemProvider *fs.FSConfig) (*domain.ObjectDetails, error) {
writeModel := NewIAMDebugNotificationFileWriteModel()
iamAgg := IAMAggregateFromWriteModel(&writeModel.WriteModel)
events, err := c.addDefaultDebugNotificationFile(ctx, iamAgg, writeModel, fileSystemProvider)
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(writeModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.DebugNotificationWriteModel.WriteModel), nil
}
func (c *Commands) addDefaultDebugNotificationFile(ctx context.Context, iamAgg *eventstore.Aggregate, addedWriteModel *IAMDebugNotificationFileWriteModel, fileSystemProvider *fs.FSConfig) ([]eventstore.Command, error) {
err := c.eventstore.FilterToQueryReducer(ctx, addedWriteModel)
if err != nil {
return nil, err
}
if addedWriteModel.State.Exists() {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-d93nfs", "Errors.IAM.DebugNotificationProvider.AlreadyExists")
}
events := []eventstore.Command{
iam_repo.NewDebugNotificationProviderFileAddedEvent(ctx,
iamAgg,
fileSystemProvider.Compact),
}
return events, nil
}
func (c *Commands) ChangeDefaultNotificationFile(ctx context.Context, fileSystemProvider *fs.FSConfig) (*domain.ObjectDetails, error) {
writeModel := NewIAMDebugNotificationFileWriteModel()
iamAgg := IAMAggregateFromWriteModel(&writeModel.WriteModel)
events, err := c.changeDefaultDebugNotificationProviderFile(ctx, iamAgg, writeModel, fileSystemProvider)
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(writeModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.DebugNotificationWriteModel.WriteModel), nil
}
func (c *Commands) changeDefaultDebugNotificationProviderFile(ctx context.Context, iamAgg *eventstore.Aggregate, existingProvider *IAMDebugNotificationFileWriteModel, fileSystemProvider *fs.FSConfig) ([]eventstore.Command, error) {
err := c.defaultDebugNotificationProviderFileWriteModelByID(ctx, existingProvider)
if err != nil {
return nil, err
}
if !existingProvider.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "IAM-fm9wl", "Errors.IAM.DebugNotificationProvider.NotFound")
}
events := make([]eventstore.Command, 0)
changedEvent, hasChanged := existingProvider.NewChangedEvent(ctx,
iamAgg,
fileSystemProvider.Compact)
if hasChanged {
events = append(events, changedEvent)
}
if len(events) == 0 {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}
return events, nil
}
func (c *Commands) RemoveDefaultNotificationFile(ctx context.Context) (*domain.ObjectDetails, error) {
existingProvider := NewIAMDebugNotificationFileWriteModel()
iamAgg := IAMAggregateFromWriteModel(&existingProvider.WriteModel)
err := c.defaultDebugNotificationProviderFileWriteModelByID(ctx, existingProvider)
if err != nil {
return nil, err
}
if !existingProvider.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "IAM-dj9ew", "Errors.IAM.DebugNotificationProvider.NotFound")
}
events, err := c.eventstore.Push(ctx, iam_repo.NewDebugNotificationProviderFileRemovedEvent(ctx, iamAgg))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingProvider, events...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingProvider.DebugNotificationWriteModel.WriteModel), nil
}
func (c *Commands) defaultDebugNotificationProviderFileWriteModelByID(ctx context.Context, writeModel *IAMDebugNotificationFileWriteModel) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,79 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/repository/iam"
)
type IAMDebugNotificationFileWriteModel struct {
DebugNotificationWriteModel
}
func NewIAMDebugNotificationFileWriteModel() *IAMDebugNotificationFileWriteModel {
return &IAMDebugNotificationFileWriteModel{
DebugNotificationWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: domain.IAMID,
ResourceOwner: domain.IAMID,
},
},
}
}
func (wm *IAMDebugNotificationFileWriteModel) AppendEvents(events ...eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *iam.DebugNotificationProviderFileAddedEvent:
wm.DebugNotificationWriteModel.AppendEvents(&e.DebugNotificationProviderAddedEvent)
case *iam.DebugNotificationProviderFileChangedEvent:
wm.DebugNotificationWriteModel.AppendEvents(&e.DebugNotificationProviderChangedEvent)
case *iam.DebugNotificationProviderFileRemovedEvent:
wm.DebugNotificationWriteModel.AppendEvents(&e.DebugNotificationProviderRemovedEvent)
}
}
}
func (wm *IAMDebugNotificationFileWriteModel) IsValid() bool {
return wm.AggregateID != ""
}
func (wm *IAMDebugNotificationFileWriteModel) Reduce() error {
return wm.DebugNotificationWriteModel.Reduce()
}
func (wm *IAMDebugNotificationFileWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(iam.AggregateType).
AggregateIDs(wm.DebugNotificationWriteModel.AggregateID).
EventTypes(
iam.DebugNotificationProviderFileAddedEventType,
iam.DebugNotificationProviderFileChangedEventType,
iam.DebugNotificationProviderFileRemovedEventType).
Builder()
}
func (wm *IAMDebugNotificationFileWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
compact bool) (*iam.DebugNotificationProviderFileChangedEvent, bool) {
changes := make([]settings.DebugNotificationProviderChanges, 0)
if wm.Compact != compact {
changes = append(changes, settings.ChangeCompact(compact))
}
if len(changes) == 0 {
return nil, false
}
changedEvent, err := iam.NewDebugNotificationProviderFileChangedEvent(ctx, aggregate, changes)
if err != nil {
return nil, false
}
return changedEvent, true
}

View File

@ -0,0 +1,348 @@
package command
import (
"context"
"testing"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/stretchr/testify/assert"
)
func TestCommandSide_AddDefaultDebugNotificationProviderFile(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
provider *fs.FSConfig
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "provider already existing, already exists error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderFileAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: true,
},
},
res: res{
err: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "add provider,ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(
iam.NewDebugNotificationProviderFileAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: domain.IAMID,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.AddDebugNotificationProviderFile(tt.args.ctx, tt.args.provider)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_ChangeDebugNotificationProviderFile(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
provider *fs.FSConfig
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "provider not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: true,
},
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "no changes, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderFileAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: false,
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "no changes enabled, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderFileAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: true,
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "change, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderFileAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newDefaultDebugNotificationFileChangedEvent(context.Background(),
false),
),
},
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: false,
Enabled: false,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.ChangeDefaultNotificationFile(tt.args.ctx, tt.args.provider)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_RemoveDebugNotificationProviderFile(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "provider not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "remove, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderFileAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
iam.NewDebugNotificationProviderFileRemovedEvent(context.Background(),
&iam.NewAggregate().Aggregate),
),
},
),
),
},
args: args{
ctx: context.Background(),
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.RemoveDefaultNotificationFile(tt.args.ctx)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func newDefaultDebugNotificationFileChangedEvent(ctx context.Context, compact bool) *iam.DebugNotificationProviderFileChangedEvent {
event, _ := iam.NewDebugNotificationProviderFileChangedEvent(ctx,
&iam.NewAggregate().Aggregate,
[]settings.DebugNotificationProviderChanges{
settings.ChangeCompact(compact),
},
)
return event
}

View File

@ -0,0 +1,115 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/notification/channels/fs"
iam_repo "github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/telemetry/tracing"
)
func (c *Commands) AddDebugNotificationProviderLog(ctx context.Context, fileSystemProvider *fs.FSConfig) (*domain.ObjectDetails, error) {
writeModel := NewIAMDebugNotificationLogWriteModel()
iamAgg := IAMAggregateFromWriteModel(&writeModel.WriteModel)
events, err := c.addDefaultDebugNotificationLog(ctx, iamAgg, writeModel, fileSystemProvider)
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(writeModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.DebugNotificationWriteModel.WriteModel), nil
}
func (c *Commands) addDefaultDebugNotificationLog(ctx context.Context, iamAgg *eventstore.Aggregate, addedWriteModel *IAMDebugNotificationLogWriteModel, fileSystemProvider *fs.FSConfig) ([]eventstore.Command, error) {
err := c.eventstore.FilterToQueryReducer(ctx, addedWriteModel)
if err != nil {
return nil, err
}
if addedWriteModel.State.Exists() {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-3h0fs", "Errors.IAM.DebugNotificationProvider.AlreadyExists")
}
events := []eventstore.Command{
iam_repo.NewDebugNotificationProviderLogAddedEvent(ctx,
iamAgg,
fileSystemProvider.Compact),
}
return events, nil
}
func (c *Commands) ChangeDefaultNotificationLog(ctx context.Context, fileSystemProvider *fs.FSConfig) (*domain.ObjectDetails, error) {
writeModel := NewIAMDebugNotificationLogWriteModel()
iamAgg := IAMAggregateFromWriteModel(&writeModel.WriteModel)
event, err := c.changeDefaultDebugNotificationProviderLog(ctx, iamAgg, writeModel, fileSystemProvider)
if err != nil {
return nil, err
}
pushedEvents, err := c.eventstore.Push(ctx, event)
if err != nil {
return nil, err
}
err = AppendAndReduce(writeModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&writeModel.DebugNotificationWriteModel.WriteModel), nil
}
func (c *Commands) changeDefaultDebugNotificationProviderLog(ctx context.Context, iamAgg *eventstore.Aggregate, existingProvider *IAMDebugNotificationLogWriteModel, fileSystemProvider *fs.FSConfig) (eventstore.Command, error) {
err := c.defaultDebugNotificationProviderLogWriteModelByID(ctx, existingProvider)
if err != nil {
return nil, err
}
if !existingProvider.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "IAM-2h0s3", "Errors.IAM.DebugNotificationProvider.NotFound")
}
changedEvent, hasChanged := existingProvider.NewChangedEvent(ctx,
iamAgg,
fileSystemProvider.Compact)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-fn9p3", "Errors.IAM.LoginPolicy.NotChanged")
}
return changedEvent, nil
}
func (c *Commands) RemoveDefaultNotificationLog(ctx context.Context) (*domain.ObjectDetails, error) {
existingProvider := NewIAMDebugNotificationLogWriteModel()
iamAgg := IAMAggregateFromWriteModel(&existingProvider.WriteModel)
err := c.defaultDebugNotificationProviderLogWriteModelByID(ctx, existingProvider)
if err != nil {
return nil, err
}
if !existingProvider.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "IAM-39lse", "Errors.IAM.DebugNotificationProvider.NotFound")
}
events, err := c.eventstore.Push(ctx, iam_repo.NewDebugNotificationProviderLogRemovedEvent(ctx, iamAgg))
if err != nil {
return nil, err
}
err = AppendAndReduce(existingProvider, events...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&existingProvider.DebugNotificationWriteModel.WriteModel), nil
}
func (c *Commands) defaultDebugNotificationProviderLogWriteModelByID(ctx context.Context, writeModel *IAMDebugNotificationLogWriteModel) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,81 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/repository/iam"
)
type IAMDebugNotificationLogWriteModel struct {
DebugNotificationWriteModel
}
func NewIAMDebugNotificationLogWriteModel() *IAMDebugNotificationLogWriteModel {
return &IAMDebugNotificationLogWriteModel{
DebugNotificationWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: domain.IAMID,
ResourceOwner: domain.IAMID,
},
},
}
}
func (wm *IAMDebugNotificationLogWriteModel) AppendEvents(events ...eventstore.Event) {
for _, event := range events {
switch e := event.(type) {
case *iam.DebugNotificationProviderLogAddedEvent:
wm.DebugNotificationWriteModel.AppendEvents(&e.DebugNotificationProviderAddedEvent)
case *iam.DebugNotificationProviderLogChangedEvent:
wm.DebugNotificationWriteModel.AppendEvents(&e.DebugNotificationProviderChangedEvent)
case *iam.DebugNotificationProviderLogRemovedEvent:
wm.DebugNotificationWriteModel.AppendEvents(&e.DebugNotificationProviderRemovedEvent)
}
}
}
func (wm *IAMDebugNotificationLogWriteModel) IsValid() bool {
return wm.AggregateID != ""
}
func (wm *IAMDebugNotificationLogWriteModel) Reduce() error {
return wm.DebugNotificationWriteModel.Reduce()
}
func (wm *IAMDebugNotificationLogWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(iam.AggregateType).
AggregateIDs(wm.DebugNotificationWriteModel.AggregateID).
EventTypes(
iam.DebugNotificationProviderLogAddedEventType,
iam.DebugNotificationProviderLogChangedEventType,
iam.DebugNotificationProviderLogEnabledEventType,
iam.DebugNotificationProviderLogDisabledEventType,
iam.DebugNotificationProviderLogRemovedEventType).
Builder()
}
func (wm *IAMDebugNotificationLogWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
compact bool) (*iam.DebugNotificationProviderLogChangedEvent, bool) {
changes := make([]settings.DebugNotificationProviderChanges, 0)
if wm.Compact != compact {
changes = append(changes, settings.ChangeCompact(compact))
}
if len(changes) == 0 {
return nil, false
}
changedEvent, err := iam.NewDebugNotificationProviderLogChangedEvent(ctx, aggregate, changes)
if err != nil {
return nil, false
}
return changedEvent, true
}

View File

@ -0,0 +1,389 @@
package command
import (
"context"
"testing"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/stretchr/testify/assert"
)
func TestCommandSide_AddDefaultDebugNotificationProviderLog(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
provider *fs.FSConfig
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "provider already existing, already exists error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: true,
},
},
res: res{
err: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "add disabled provider,ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: domain.IAMID,
},
},
},
{
name: "add provider,ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: true,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: domain.IAMID,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.AddDebugNotificationProviderLog(tt.args.ctx, tt.args.provider)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_ChangeDebugNotificationProviderLog(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
provider *fs.FSConfig
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "provider not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: true,
},
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "no changes, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: true,
Enabled: false,
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "change, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newDefaultDebugNotificationLogChangedEvent(context.Background(),
false),
),
},
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: false,
Enabled: false,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
{
name: "change, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newDefaultDebugNotificationLogChangedEvent(context.Background(),
false),
),
},
),
),
},
args: args{
ctx: context.Background(),
provider: &fs.FSConfig{
Compact: false,
Enabled: true,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.ChangeDefaultNotificationLog(tt.args.ctx, tt.args.provider)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_RemoveDebugNotificationProviderLog(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "provider not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "remove, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewDebugNotificationProviderLogAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
true,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
iam.NewDebugNotificationProviderLogRemovedEvent(context.Background(),
&iam.NewAggregate().Aggregate),
),
},
),
),
},
args: args{
ctx: context.Background(),
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.RemoveDefaultNotificationLog(tt.args.ctx)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func newDefaultDebugNotificationLogChangedEvent(ctx context.Context, compact bool) *iam.DebugNotificationProviderLogChangedEvent {
event, _ := iam.NewDebugNotificationProviderLogChangedEvent(ctx,
&iam.NewAggregate().Aggregate,
[]settings.DebugNotificationProviderChanges{
settings.ChangeCompact(compact),
},
)
return event
}

View File

@ -6,7 +6,6 @@ import (
"golang.org/x/text/language"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/notification/channels/chat"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/twilio"
@ -56,7 +55,8 @@ type DomainVerification struct {
type Notifications struct {
DebugMode bool
Endpoints Endpoints
Providers Channels
FileSystemPath string
//Providers Channels
}
type Endpoints struct {
@ -68,7 +68,6 @@ type Endpoints struct {
}
type Channels struct {
Chat chat.ChatConfig
Twilio twilio.TwilioConfig
FileSystem fs.FSConfig
Log log.LogConfig

View File

@ -12,3 +12,26 @@ const (
func (f NotificationType) Valid() bool {
return f >= 0 && f < notificationCount
}
type NotificationProviderState int32
const (
NotificationProviderStateUnspecified NotificationProviderState = iota
NotificationProviderStateActive
NotificationProviderStateRemoved
notificationProviderCount
)
func (s NotificationProviderState) Exists() bool {
return s == NotificationProviderStateActive
}
type NotificationProviderType int32
const (
NotificationProviderTypeFile NotificationProviderType = iota
NotificationProviderTypeLog
notificationProviderTypeCount
)

View File

@ -1,80 +0,0 @@
package chat
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"net/url"
"unicode/utf8"
"github.com/k3a/html2text"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/channels"
)
func InitChatChannel(config ChatConfig) (channels.NotificationChannel, error) {
url, err := url.Parse(config.Url)
if err != nil {
return nil, err
}
logging.Log("NOTIF-kSvPp").Debug("successfully initialized chat email and sms channel")
return channels.HandleMessageFunc(func(message channels.Message) error {
contentText := message.GetContent()
if config.Compact {
contentText = html2text.HTML2Text(contentText)
}
for _, splittedMsg := range splitMessage(contentText, config.SplitCount) {
if err := sendMessage(splittedMsg, url); err != nil {
return err
}
}
return nil
}), nil
}
func sendMessage(message string, chatUrl *url.URL) error {
chatMsg := &struct {
Text string `json:"text"`
}{Text: message}
req, err := json.Marshal(chatMsg)
if err != nil {
return caos_errs.ThrowInternal(err, "PROVI-s8uie", "Could not unmarshal content")
}
response, err := http.Post(chatUrl.String(), "application/json; charset=UTF-8", bytes.NewReader(req))
if err != nil {
return caos_errs.ThrowInternal(err, "PROVI-si93s", "unable to send message")
}
if response.StatusCode != 200 {
defer response.Body.Close()
bodyBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return caos_errs.ThrowInternal(err, "PROVI-PSLd3", "unable to read response message")
}
logging.LogWithFields("PROVI-PS0kx", "Body", string(bodyBytes)).Warn("Chat Message post didnt get 200 OK")
return caos_errs.ThrowInternal(nil, "PROVI-LSopw", string(bodyBytes))
}
return nil
}
func splitMessage(message string, count int) []string {
if count == 0 {
return []string{message}
}
var splits []string
var l, r int
for l, r = 0, count; r < len(message); l, r = r, r+count {
for !utf8.RuneStart(message[r]) {
r--
}
splits = append(splits, message[l:r])
}
splits = append(splits, message[l:])
return splits
}

View File

@ -1,9 +0,0 @@
package chat
type ChatConfig struct {
// Defaults to true if DebugMode is set to true
Enabled *bool
Url string
SplitCount int
Compact bool
}

View File

@ -19,9 +19,11 @@ import (
"github.com/caos/zitadel/internal/notification/messages"
)
func InitFSChannel(config FSConfig) (channels.NotificationChannel, error) {
if err := os.MkdirAll(config.Path, os.ModePerm); err != nil {
func InitFSChannel(path string, config FSConfig) (channels.NotificationChannel, error) {
if path == "" {
return nil, nil
}
if err := os.MkdirAll(path, os.ModePerm); err != nil {
return nil, err
}
@ -46,6 +48,6 @@ func InitFSChannel(config FSConfig) (channels.NotificationChannel, error) {
return caos_errors.ThrowUnimplementedf(nil, "NOTIF-6f9a1", "filesystem provider doesn't support message type %T", message)
}
return ioutil.WriteFile(filepath.Join(config.Path, fileName), []byte(content), 0666)
return ioutil.WriteFile(filepath.Join(path, fileName), []byte(content), 0666)
}), nil
}

View File

@ -2,6 +2,5 @@ package fs
type FSConfig struct {
Enabled bool
Path string
Compact bool
}

View File

@ -24,12 +24,15 @@ type Email struct {
func InitSMTPChannel(ctx context.Context, getSMTPConfig func(ctx context.Context) (*EmailConfig, error)) (*Email, error) {
smtpConfig, err := getSMTPConfig(ctx)
if err != nil {
return nil, err
}
client, err := smtpConfig.SMTP.connectToSMTP(smtpConfig.Tls)
if err != nil {
return nil, err
}
logging.Log("NOTIF-4n4Ih").Debug("successfully initialized smtp email channel")
logging.New().Debug("successfully initialized smtp email channel")
return &Email{
smtpClient: client,

View File

@ -18,10 +18,10 @@ type Config struct {
Repository eventsourcing.Config
}
func Start(config Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) {
func Start(config Config, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) {
statikFS, err := fs.NewWithNamespace("notification")
logging.OnError(err).Panic("unable to start listener")
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix, smtpPasswordEncAlg)
_, err = eventsourcing.Start(config.Repository, statikFS, systemDefaults, command, queries, dbClient, assetsPrefix, smtpPasswordEncAlg, smsCrypto)
logging.OnError(err).Panic("unable to start app")
}

View File

@ -34,7 +34,7 @@ func (h *handler) Eventstore() v1.Eventstore {
return h.es
}
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) []queryv1.Handler {
func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es v1.Eventstore, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) []queryv1.Handler {
aesCrypto, err := crypto.NewAESCrypto(systemDefaults.UserVerificationKey)
logging.OnError(err).Fatal("error create new aes crypto")
@ -52,6 +52,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
dir,
assetsPrefix,
smtpPasswordEncAlg,
smsCrypto,
),
}
}

View File

@ -7,6 +7,9 @@ import (
"time"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/twilio"
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/command"
@ -43,18 +46,10 @@ type Notification struct {
assetsPrefix string
queries *query.Queries
smtpPasswordCrypto crypto.EncryptionAlgorithm
smsTokenCrypto crypto.EncryptionAlgorithm
}
func newNotification(
handler handler,
command *command.Commands,
query *query.Queries,
defaults sd.SystemDefaults,
aesCrypto crypto.EncryptionAlgorithm,
statikDir http.FileSystem,
assetsPrefix string,
smtpPasswordEncAlg crypto.EncryptionAlgorithm,
) *Notification {
func newNotification(handler handler, command *command.Commands, query *query.Queries, defaults sd.SystemDefaults, aesCrypto crypto.EncryptionAlgorithm, statikDir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) *Notification {
h := &Notification{
handler: handler,
command: command,
@ -64,6 +59,7 @@ func newNotification(
assetsPrefix: assetsPrefix,
queries: query,
smtpPasswordCrypto: smtpPasswordEncAlg,
smsTokenCrypto: smsCrypto,
}
h.subscribe()
@ -165,7 +161,7 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
return err
}
err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
err = types.SendUserInitCode(ctx, string(template.Template), translator, user, initCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@ -203,7 +199,7 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
if err != nil {
return err
}
err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
err = types.SendPasswordCode(ctx, string(template.Template), translator, user, pwCode, n.systemDefaults, n.getSMTPConfig, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@ -242,7 +238,7 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
return err
}
err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
err = types.SendEmailVerificationCode(ctx, string(template.Template), translator, user, emailCode, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@ -268,7 +264,7 @@ func (n *Notification) handlePhoneVerificationCode(event *models.Event) (err err
if err != nil {
return err
}
err = types.SendPhoneVerificationCode(translator, user, phoneCode, n.systemDefaults, n.AesCrypto)
err = types.SendPhoneVerificationCode(context.Background(), translator, user, phoneCode, n.systemDefaults, n.getTwilioConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto)
if err != nil {
return err
}
@ -308,7 +304,7 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
return err
}
err = types.SendDomainClaimed(ctx, string(template.Template), translator, user, data["userName"], n.systemDefaults, n.getSMTPConfig, colors, n.assetsPrefix)
err = types.SendDomainClaimed(ctx, string(template.Template), translator, user, data["userName"], n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, colors, n.assetsPrefix)
if err != nil {
return err
}
@ -355,7 +351,7 @@ func (n *Notification) handlePasswordlessRegistrationLink(event *models.Event) (
return err
}
err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.systemDefaults, n.getSMTPConfig, n.AesCrypto, colors, n.assetsPrefix)
err = types.SendPasswordlessRegistrationLink(ctx, string(template.Template), translator, user, addedEvent, n.systemDefaults, n.getSMTPConfig, n.getFileSystemProvider, n.getLogProvider, n.AesCrypto, colors, n.assetsPrefix)
if err != nil {
return err
}
@ -394,7 +390,7 @@ func (n *Notification) getUserEvents(userID string, sequence uint64) ([]*models.
}
func (n *Notification) OnError(event *models.Event, err error) error {
logging.LogWithFields("SPOOL-s9opc", "id", event.AggregateID, "sequence", event.Sequence).WithError(err).Warn("something went wrong in notification handler")
logging.WithFields("id", event.AggregateID, "sequence", event.Sequence).WithError(err).Warn("something went wrong in notification handler")
return spooler.HandleError(event, err, n.view.GetLatestNotificationFailedEvent, n.view.ProcessedNotificationFailedEvent, n.view.ProcessedNotificationSequence, n.errorCountUntilSkip)
}
@ -437,6 +433,48 @@ func (n *Notification) getSMTPConfig(ctx context.Context) (*smtp.EmailConfig, er
}, nil
}
// Read iam twilio config
func (n *Notification) getTwilioConfig(ctx context.Context) (*twilio.TwilioConfig, error) {
config, err := n.queries.SMSProviderConfigByID(ctx, domain.IAMID)
if err != nil {
return nil, err
}
if config.TwilioConfig == nil {
return nil, errors.ThrowNotFound(nil, "HANDLER-8nfow", "Errors.SMS.Twilio.NotFound")
}
token, err := crypto.Decrypt(config.TwilioConfig.Token, n.smtpPasswordCrypto)
if err != nil {
return nil, err
}
return &twilio.TwilioConfig{
SID: config.TwilioConfig.SID,
Token: string(token),
SenderNumber: config.TwilioConfig.SenderNumber,
}, nil
}
// Read iam filesystem provider config
func (n *Notification) getFileSystemProvider(ctx context.Context) (*fs.FSConfig, error) {
config, err := n.queries.NotificationProviderByIDAndType(ctx, domain.IAMID, domain.NotificationProviderTypeFile)
if err != nil {
return nil, err
}
return &fs.FSConfig{
Compact: config.Compact,
}, nil
}
// Read iam log provider config
func (n *Notification) getLogProvider(ctx context.Context) (*log.LogConfig, error) {
config, err := n.queries.NotificationProviderByIDAndType(ctx, domain.IAMID, domain.NotificationProviderTypeLog)
if err != nil {
return nil, err
}
return &log.LogConfig{
Compact: config.Compact,
}, nil
}
func (n *Notification) getTranslatorWithOrgTexts(orgID, textType string) (*i18n.Translator, error) {
ctx := context.Background()
translator, err := i18n.NewTranslator(n.statikDir, i18n.TranslatorConfig{DefaultLanguage: n.queries.GetDefaultLanguage(ctx)})

View File

@ -22,7 +22,7 @@ type EsRepository struct {
spooler *es_spol.Spooler
}
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) (*EsRepository, error) {
func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, command *command.Commands, queries *query.Queries, dbClient *sql.DB, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) (*EsRepository, error) {
es, err := v1.Start(dbClient)
if err != nil {
return nil, err
@ -33,7 +33,7 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults, c
return nil, err
}
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg)
spool := spooler.StartSpooler(conf.Spooler, es, view, dbClient, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg, smsCrypto)
return &EsRepository{
spool,

View File

@ -21,12 +21,12 @@ type SpoolerConfig struct {
Handlers handler.Configs
}
func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm) *spooler.Spooler {
func StartSpooler(c SpoolerConfig, es v1.Eventstore, view *view.View, sql *sql.DB, command *command.Commands, queries *query.Queries, systemDefaults sd.SystemDefaults, dir http.FileSystem, assetsPrefix string, smtpPasswordEncAlg crypto.EncryptionAlgorithm, smsCrypto *crypto.AESCrypto) *spooler.Spooler {
spoolerConfig := spooler.Config{
Eventstore: es,
Locker: &locker{dbClient: sql},
ConcurrentWorkers: c.ConcurrentWorkers,
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg),
ViewHandlers: handler.Register(c.Handlers, c.BulkLimit, c.FailureCountUntilSkip, view, es, command, queries, systemDefaults, dir, assetsPrefix, smtpPasswordEncAlg, smsCrypto),
}
spool := spoolerConfig.New()
spool.Start()

View File

@ -22,3 +22,7 @@ func (c *Chain) HandleMessage(message channels.Message) error {
}
return nil
}
func (c *Chain) Len() int {
return len(c.channels)
}

View File

@ -1,45 +1,28 @@
package senders
import (
"context"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels"
"github.com/caos/zitadel/internal/notification/channels/chat"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
)
func debugChannels(config systemdefaults.Notifications) (channels.NotificationChannel, error) {
func debugChannels(ctx context.Context, config systemdefaults.Notifications, getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (*Chain, error) {
var (
providers []channels.NotificationChannel
enableChat bool
)
if config.Providers.Chat.Enabled != nil {
enableChat = *config.Providers.Chat.Enabled
} else {
// ensures backward compatible configuration
enableChat = config.DebugMode
}
if enableChat {
p, err := chat.InitChatChannel(config.Providers.Chat)
if err != nil {
return nil, err
}
if fsProvider, err := getFileSystemProvider(ctx); err == nil {
p, err := fs.InitFSChannel(config.FileSystemPath, *fsProvider)
if err == nil {
providers = append(providers, p)
}
if config.Providers.FileSystem.Enabled {
p, err := fs.InitFSChannel(config.Providers.FileSystem)
if err != nil {
return nil, err
}
providers = append(providers, p)
}
if config.Providers.Log.Enabled {
providers = append(providers, log.InitStdoutChannel(config.Providers.Log))
if logProvider, err := getLogProvider(ctx); err == nil {
providers = append(providers, log.InitStdoutChannel(*logProvider))
}
return chainChannels(providers...), nil

View File

@ -3,25 +3,21 @@ package senders
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
)
func EmailChannels(ctx context.Context, config systemdefaults.Notifications, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error)) (channels.NotificationChannel, error) {
debug, err := debugChannels(config)
if err != nil {
return nil, err
}
if !config.DebugMode {
func EmailChannels(ctx context.Context, config systemdefaults.Notifications, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (chain *Chain, err error) {
p, err := smtp.InitSMTPChannel(ctx, emailConfig)
if err == nil {
chain.channels = append(chain.channels, p)
}
chain, err = debugChannels(ctx, config, getFileSystemProvider, getLogProvider)
if err != nil {
return nil, err
logging.New().Info("Error in creating debug channels")
}
return chainChannels(debug, p), nil
}
return debug, nil
return chain, nil
}

View File

@ -1,21 +1,21 @@
package senders
import (
"context"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/notification/channels"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/twilio"
)
func SMSChannels(config systemdefaults.Notifications) (channels.NotificationChannel, error) {
debug, err := debugChannels(config)
func SMSChannels(ctx context.Context, config systemdefaults.Notifications, twilioConfig *twilio.TwilioConfig, getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error)) (chain *Chain, err error) {
if twilioConfig != nil {
chain.channels = append(chain.channels, twilio.InitTwilioChannel(*twilioConfig))
}
chain, err = debugChannels(ctx, config, getFileSystemProvider, getLogProvider)
if err != nil {
return nil, err
}
if !config.DebugMode {
return chainChannels(debug, twilio.InitTwilioChannel(config.Providers.Twilio)), nil
}
return debug, nil
return chain, nil
}

View File

@ -7,6 +7,8 @@ import (
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
@ -18,7 +20,7 @@ type DomainClaimedData struct {
URL string
}
func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), colors *query.LabelPolicy, assetsPrefix string) error {
func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults, emailConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), colors *query.LabelPolicy, assetsPrefix string) error {
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.DomainClaimed, &UrlData{UserID: user.ID})
if err != nil {
return err
@ -35,5 +37,5 @@ func SendDomainClaimed(ctx context.Context, mailhtml string, translator *i18n.Tr
if err != nil {
return err
}
return generateEmail(ctx, user, domainClaimedData.Subject, template, systemDefaults.Notifications, emailConfig, true)
return generateEmail(ctx, user, domainClaimedData.Subject, template, systemDefaults.Notifications, emailConfig, getFileSystemProvider, getLogProvider, true)
}

View File

@ -7,6 +7,8 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
@ -19,7 +21,7 @@ type EmailVerificationCodeData struct {
URL string
}
func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@ -41,5 +43,5 @@ func SendEmailVerificationCode(ctx context.Context, mailhtml string, translator
if err != nil {
return err
}
return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true)
}

View File

@ -7,6 +7,8 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
@ -25,7 +27,7 @@ type UrlData struct {
PasswordSet bool
}
func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@ -45,5 +47,5 @@ func SendUserInitCode(ctx context.Context, mailhtml string, translator *i18n.Tra
if err != nil {
return err
}
return generateEmail(ctx, user, initCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
return generateEmail(ctx, user, initCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true)
}

View File

@ -7,7 +7,10 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/channels/twilio"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
@ -21,7 +24,7 @@ type PasswordCodeData struct {
URL string
}
func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getTwilioConfig func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@ -44,8 +47,8 @@ func SendPasswordCode(ctx context.Context, mailhtml string, translator *i18n.Tra
return err
}
if code.NotificationType == int32(domain.NotificationTypeSms) {
return generateSms(user, passwordResetData.Text, systemDefaults.Notifications, false)
return generateSms(ctx, user, passwordResetData.Text, systemDefaults.Notifications, getTwilioConfig, getFileSystemProvider, getLogProvider, false)
}
return generateEmail(ctx, user, passwordResetData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
return generateEmail(ctx, user, passwordResetData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true)
}

View File

@ -7,6 +7,8 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/templates"
"github.com/caos/zitadel/internal/query"
@ -19,7 +21,7 @@ type PasswordlessRegistrationLinkData struct {
URL string
}
func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, translator *i18n.Translator, user *view_model.NotifyUser, code *user.HumanPasswordlessInitCodeRequestedEvent, systemDefaults systemdefaults.SystemDefaults, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm, colors *query.LabelPolicy, assetsPrefix string) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@ -36,5 +38,5 @@ func SendPasswordlessRegistrationLink(ctx context.Context, mailhtml string, tran
if err != nil {
return err
}
return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, true)
return generateEmail(ctx, user, emailCodeData.Subject, template, systemDefaults.Notifications, smtpConfig, getFileSystemProvider, getLogProvider, true)
}

View File

@ -1,12 +1,16 @@
package types
import (
"context"
"fmt"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/i18n"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/twilio"
"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"
@ -16,7 +20,7 @@ type PhoneVerificationCodeData struct {
UserID string
}
func SendPhoneVerificationCode(translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PhoneCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm) error {
func SendPhoneVerificationCode(ctx context.Context, translator *i18n.Translator, user *view_model.NotifyUser, code *es_model.PhoneCode, systemDefaults systemdefaults.SystemDefaults, getTwilioConfig func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), alg crypto.EncryptionAlgorithm) error {
codeString, err := crypto.DecryptString(code.Code, alg)
if err != nil {
return err
@ -31,5 +35,5 @@ func SendPhoneVerificationCode(translator *i18n.Translator, user *view_model.Not
if err != nil {
return err
}
return generateSms(user, template, systemDefaults.Notifications, true)
return generateSms(ctx, user, template, systemDefaults.Notifications, getTwilioConfig, getFileSystemProvider, getLogProvider, true)
}

View File

@ -4,6 +4,9 @@ import (
"context"
"html"
caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/smtp"
"github.com/caos/zitadel/internal/notification/messages"
"github.com/caos/zitadel/internal/notification/senders"
@ -12,7 +15,7 @@ import (
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
)
func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, content string, config systemdefaults.Notifications, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), lastEmail bool) error {
func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, content string, config systemdefaults.Notifications, smtpConfig func(ctx context.Context) (*smtp.EmailConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), lastEmail bool) error {
content = html.UnescapeString(content)
message := &messages.Email{
Recipients: []string{user.VerifiedEmail},
@ -23,12 +26,15 @@ func generateEmail(ctx context.Context, user *view_model.NotifyUser, subject, co
message.Recipients = []string{user.LastEmail}
}
channels, err := senders.EmailChannels(ctx, config, smtpConfig)
channelChain, err := senders.EmailChannels(ctx, config, smtpConfig, getFileSystemProvider, getLogProvider)
if err != nil {
return err
}
return channels.HandleMessage(message)
if channelChain.Len() == 0 {
return caos_errors.ThrowPreconditionFailed(nil, "MAIL-83nof", "Errors.Notification.Channels.NotPresent")
}
return channelChain.HandleMessage(message)
}
func mapNotifyUserToArgs(user *view_model.NotifyUser) map[string]interface{} {

View File

@ -1,15 +1,26 @@
package types
import (
"context"
"github.com/caos/zitadel/internal/config/systemdefaults"
caos_errors "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/notification/channels/fs"
"github.com/caos/zitadel/internal/notification/channels/log"
"github.com/caos/zitadel/internal/notification/channels/twilio"
"github.com/caos/zitadel/internal/notification/messages"
"github.com/caos/zitadel/internal/notification/senders"
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
)
func generateSms(user *view_model.NotifyUser, content string, config systemdefaults.Notifications, lastPhone bool) error {
func generateSms(ctx context.Context, user *view_model.NotifyUser, content string, config systemdefaults.Notifications, getTwilioProvider func(ctx context.Context) (*twilio.TwilioConfig, error), getFileSystemProvider func(ctx context.Context) (*fs.FSConfig, error), getLogProvider func(ctx context.Context) (*log.LogConfig, error), lastPhone bool) error {
number := ""
twilio, err := getTwilioProvider(ctx)
if err == nil {
number = twilio.SenderNumber
}
message := &messages.SMS{
SenderPhoneNumber: config.Providers.Twilio.SenderNumber,
SenderPhoneNumber: number,
RecipientPhoneNumber: user.VerifiedPhone,
Content: content,
}
@ -17,9 +28,10 @@ func generateSms(user *view_model.NotifyUser, content string, config systemdefau
message.RecipientPhoneNumber = user.LastPhone
}
channels, err := senders.SMSChannels(config)
if err != nil {
return err
channelChain, err := senders.SMSChannels(ctx, config, twilio, getFileSystemProvider, getLogProvider)
if channelChain.Len() == 0 {
return caos_errors.ThrowPreconditionFailed(nil, "PHONE-w8nfow", "Errors.Notification.Channels.NotPresent")
}
return channels.HandleMessage(message)
return channelChain.HandleMessage(message)
}

View File

@ -0,0 +1,112 @@
package query
import (
"context"
"database/sql"
errs "errors"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
)
type DebugNotificationProvider struct {
AggregateID string
CreationDate time.Time
ChangeDate time.Time
Sequence uint64
ResourceOwner string
State domain.NotificationProviderState
Type domain.NotificationProviderType
Compact bool
}
var (
notificationProviderTable = table{
name: projection.DebugNotificationProviderTable,
}
NotificationProviderColumnAggID = Column{
name: projection.DebugNotificationProviderAggIDCol,
table: notificationProviderTable,
}
NotificationProviderColumnCreationDate = Column{
name: projection.DebugNotificationProviderCreationDateCol,
table: notificationProviderTable,
}
NotificationProviderColumnChangeDate = Column{
name: projection.DebugNotificationProviderChangeDateCol,
table: notificationProviderTable,
}
NotificationProviderColumnSequence = Column{
name: projection.DebugNotificationProviderSequenceCol,
table: notificationProviderTable,
}
NotificationProviderColumnResourceOwner = Column{
name: projection.DebugNotificationProviderResourceOwnerCol,
table: notificationProviderTable,
}
NotificationProviderColumnState = Column{
name: projection.DebugNotificationProviderStateCol,
table: notificationProviderTable,
}
NotificationProviderColumnType = Column{
name: projection.DebugNotificationProviderTypeCol,
table: notificationProviderTable,
}
NotificationProviderColumnCompact = Column{
name: projection.DebugNotificationProviderCompactCol,
table: notificationProviderTable,
}
)
func (q *Queries) NotificationProviderByIDAndType(ctx context.Context, aggID string, providerType domain.NotificationProviderType) (*DebugNotificationProvider, error) {
query, scan := prepareDebugNotificationProviderQuery()
stmt, args, err := query.Where(
sq.Or{
sq.Eq{
LoginPolicyColumnOrgID.identifier(): aggID,
},
}).
Limit(1).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-f9jSf", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
func prepareDebugNotificationProviderQuery() (sq.SelectBuilder, func(*sql.Row) (*DebugNotificationProvider, error)) {
return sq.Select(
NotificationProviderColumnAggID.identifier(),
NotificationProviderColumnCreationDate.identifier(),
NotificationProviderColumnChangeDate.identifier(),
NotificationProviderColumnSequence.identifier(),
NotificationProviderColumnResourceOwner.identifier(),
NotificationProviderColumnState.identifier(),
NotificationProviderColumnType.identifier(),
NotificationProviderColumnCompact.identifier(),
).From(notificationProviderTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*DebugNotificationProvider, error) {
p := new(DebugNotificationProvider)
err := row.Scan(
&p.AggregateID,
&p.CreationDate,
&p.ChangeDate,
&p.Sequence,
&p.ResourceOwner,
&p.State,
&p.Type,
&p.Compact,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-s9ujf", "Errors.NotificationProvider.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-2liu0", "Errors.Internal")
}
return p, nil
}
}

View File

@ -0,0 +1,130 @@
package query
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"regexp"
"testing"
"github.com/caos/zitadel/internal/domain"
errs "github.com/caos/zitadel/internal/errors"
)
func Test_NotificationProviderPrepares(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
}
tests := []struct {
name string
prepare interface{}
want want
object interface{}
}{
{
name: "prepareNotificationProviderQuery no result",
prepare: prepareDebugNotificationProviderQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(`SELECT zitadel.projections.notification_providers.aggregate_id,`+
` zitadel.projections.notification_providers.creation_date,`+
` zitadel.projections.notification_providers.change_date,`+
` zitadel.projections.notification_providers.sequence,`+
` zitadel.projections.notification_providers.resource_owner,`+
` zitadel.projections.notification_providers.state,`+
` zitadel.projections.notification_providers.provider_type,`+
` zitadel.projections.notification_providers.compact`+
` FROM zitadel.projections.notification_providers`),
nil,
nil,
),
err: func(err error) (error, bool) {
if !errs.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: (*DebugNotificationProvider)(nil),
},
{
name: "prepareNotificationProviderQuery found",
prepare: prepareDebugNotificationProviderQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(`SELECT zitadel.projections.notification_providers.aggregate_id,`+
` zitadel.projections.notification_providers.creation_date,`+
` zitadel.projections.notification_providers.change_date,`+
` zitadel.projections.notification_providers.sequence,`+
` zitadel.projections.notification_providers.resource_owner,`+
` zitadel.projections.notification_providers.state,`+
` zitadel.projections.notification_providers.provider_type,`+
` zitadel.projections.notification_providers.compact`+
` FROM zitadel.projections.notification_providers`),
[]string{
"aggregate_id",
"creation_date",
"change_date",
"sequence",
"resource_owner",
"state",
"provider_type",
"compact",
},
[]driver.Value{
"agg-id",
testNow,
testNow,
uint64(20211109),
"ro-id",
domain.NotificationProviderStateActive,
domain.NotificationProviderTypeFile,
true,
},
),
},
object: &DebugNotificationProvider{
AggregateID: "agg-id",
CreationDate: testNow,
ChangeDate: testNow,
Sequence: 20211109,
ResourceOwner: "ro-id",
State: domain.NotificationProviderStateActive,
Type: domain.NotificationProviderTypeFile,
Compact: true,
},
},
{
name: "prepareNotificationProviderQuery sql err",
prepare: prepareDebugNotificationProviderQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT zitadel.projections.notification_providers.aggregate_id,`+
` zitadel.projections.notification_providers.creation_date,`+
` zitadel.projections.notification_providers.change_date,`+
` zitadel.projections.notification_providers.sequence,`+
` zitadel.projections.notification_providers.resource_owner,`+
` zitadel.projections.notification_providers.state,`+
` zitadel.projections.notification_providers.provider_type,`+
` zitadel.projections.notification_providers.compact`+
` FROM zitadel.projections.notification_providers`),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
})
}
}

View File

@ -0,0 +1,160 @@
package projection
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/handler/crdb"
"github.com/caos/zitadel/internal/repository/iam"
)
type DebugNotificationProviderProjection struct {
crdb.StatementHandler
}
const (
DebugNotificationProviderTable = "zitadel.projections.notification_providers"
)
func NewDebugNotificationProviderProjection(ctx context.Context, config crdb.StatementHandlerConfig) *DebugNotificationProviderProjection {
p := &DebugNotificationProviderProjection{}
config.ProjectionName = DebugNotificationProviderTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p
}
func (p *DebugNotificationProviderProjection) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{
{
Aggregate: iam.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: iam.DebugNotificationProviderFileAddedEventType,
Reduce: p.reduceDebugNotificationProviderAdded,
},
{
Event: iam.DebugNotificationProviderFileChangedEventType,
Reduce: p.reduceDebugNotificationProviderChanged,
},
{
Event: iam.DebugNotificationProviderFileRemovedEventType,
Reduce: p.reduceDebugNotificationProviderRemoved,
},
{
Event: iam.DebugNotificationProviderLogAddedEventType,
Reduce: p.reduceDebugNotificationProviderAdded,
},
{
Event: iam.DebugNotificationProviderLogChangedEventType,
Reduce: p.reduceDebugNotificationProviderChanged,
},
{
Event: iam.DebugNotificationProviderLogRemovedEventType,
Reduce: p.reduceDebugNotificationProviderRemoved,
},
},
},
}
}
const (
DebugNotificationProviderAggIDCol = "aggregate_id"
DebugNotificationProviderCreationDateCol = "creation_date"
DebugNotificationProviderChangeDateCol = "change_date"
DebugNotificationProviderSequenceCol = "sequence"
DebugNotificationProviderResourceOwnerCol = "resource_owner"
DebugNotificationProviderStateCol = "state"
DebugNotificationProviderTypeCol = "provider_type"
DebugNotificationProviderCompactCol = "compact"
)
func (p *DebugNotificationProviderProjection) reduceDebugNotificationProviderAdded(event eventstore.Event) (*handler.Statement, error) {
var providerEvent settings.DebugNotificationProviderAddedEvent
var providerType domain.NotificationProviderType
switch e := event.(type) {
case *iam.DebugNotificationProviderFileAddedEvent:
providerEvent = e.DebugNotificationProviderAddedEvent
providerType = domain.NotificationProviderTypeFile
case *iam.DebugNotificationProviderLogAddedEvent:
providerEvent = e.DebugNotificationProviderAddedEvent
providerType = domain.NotificationProviderTypeLog
default:
logging.WithFields("seq", event.Sequence(), "expectedTypes", []eventstore.EventType{iam.DebugNotificationProviderFileAddedEventType, iam.DebugNotificationProviderLogAddedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-pYPxS", "reduce.wrong.event.type")
}
return crdb.NewCreateStatement(&providerEvent, []handler.Column{
handler.NewCol(DebugNotificationProviderAggIDCol, providerEvent.Aggregate().ID),
handler.NewCol(DebugNotificationProviderCreationDateCol, providerEvent.CreationDate()),
handler.NewCol(DebugNotificationProviderChangeDateCol, providerEvent.CreationDate()),
handler.NewCol(DebugNotificationProviderSequenceCol, providerEvent.Sequence()),
handler.NewCol(DebugNotificationProviderResourceOwnerCol, providerEvent.Aggregate().ResourceOwner),
handler.NewCol(DebugNotificationProviderStateCol, domain.NotificationProviderStateActive),
handler.NewCol(DebugNotificationProviderTypeCol, providerType),
handler.NewCol(DebugNotificationProviderCompactCol, providerEvent.Compact),
}), nil
}
func (p *DebugNotificationProviderProjection) reduceDebugNotificationProviderChanged(event eventstore.Event) (*handler.Statement, error) {
var providerEvent settings.DebugNotificationProviderChangedEvent
var providerType domain.NotificationProviderType
switch e := event.(type) {
case *iam.DebugNotificationProviderFileChangedEvent:
providerEvent = e.DebugNotificationProviderChangedEvent
providerType = domain.NotificationProviderTypeFile
case *iam.DebugNotificationProviderLogChangedEvent:
providerEvent = e.DebugNotificationProviderChangedEvent
providerType = domain.NotificationProviderTypeLog
default:
logging.WithFields("seq", event.Sequence(), "expectedTypes", []eventstore.EventType{iam.DebugNotificationProviderFileChangedEventType, iam.DebugNotificationProviderLogChangedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-pYPxS", "reduce.wrong.event.type")
}
cols := []handler.Column{
handler.NewCol(DebugNotificationProviderChangeDateCol, providerEvent.CreationDate()),
handler.NewCol(DebugNotificationProviderSequenceCol, providerEvent.Sequence()),
}
if providerEvent.Compact != nil {
cols = append(cols, handler.NewCol(DebugNotificationProviderCompactCol, *providerEvent.Compact))
}
return crdb.NewUpdateStatement(
&providerEvent,
cols,
[]handler.Condition{
handler.NewCond(DebugNotificationProviderAggIDCol, providerEvent.Aggregate().ID),
handler.NewCond(DebugNotificationProviderTypeCol, providerType),
},
), nil
}
func (p *DebugNotificationProviderProjection) reduceDebugNotificationProviderRemoved(event eventstore.Event) (*handler.Statement, error) {
var providerEvent settings.DebugNotificationProviderRemovedEvent
var providerType domain.NotificationProviderType
switch e := event.(type) {
case *iam.DebugNotificationProviderFileRemovedEvent:
providerEvent = e.DebugNotificationProviderRemovedEvent
providerType = domain.NotificationProviderTypeFile
case *iam.DebugNotificationProviderLogRemovedEvent:
providerEvent = e.DebugNotificationProviderRemovedEvent
providerType = domain.NotificationProviderTypeLog
default:
logging.WithFields("seq", event.Sequence(), "expectedTypes", []eventstore.EventType{iam.DebugNotificationProviderFileRemovedEventType, iam.DebugNotificationProviderLogRemovedEventType}).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-dow9f", "reduce.wrong.event.type")
}
return crdb.NewDeleteStatement(
&providerEvent,
[]handler.Condition{
handler.NewCond(DebugNotificationProviderAggIDCol, providerEvent.Aggregate().ID),
handler.NewCond(DebugNotificationProviderTypeCol, providerType),
},
), nil
}

View File

@ -0,0 +1,232 @@
package projection
import (
"testing"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/repository/iam"
)
func TestDebugNotificationProviderProjection_reduces(t *testing.T) {
type args struct {
event func(t *testing.T) eventstore.Event
}
tests := []struct {
name string
args args
reduce func(event eventstore.Event) (*handler.Statement, error)
want wantReduce
}{
{
name: "iam.reduceNotificationProviderFileAdded",
reduce: (&DebugNotificationProviderProjection{}).reduceDebugNotificationProviderAdded,
args: args{
event: getEvent(testEvent(
repository.EventType(iam.DebugNotificationProviderFileAddedEventType),
iam.AggregateType,
[]byte(`{
"compact": true
}`),
), iam.DebugNotificationProviderFileAddedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: DebugNotificationProviderTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO zitadel.projections.notification_providers (aggregate_id, creation_date, change_date, sequence, resource_owner, state, provider_type, compact) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
expectedArgs: []interface{}{
"agg-id",
anyArg{},
anyArg{},
uint64(15),
"ro-id",
domain.NotificationProviderStateActive,
domain.NotificationProviderTypeFile,
true,
},
},
},
},
},
},
{
name: "iam.reduceNotificationProviderFileChanged",
reduce: (&DebugNotificationProviderProjection{}).reduceDebugNotificationProviderChanged,
args: args{
event: getEvent(testEvent(
repository.EventType(iam.DebugNotificationProviderFileChangedEventType),
iam.AggregateType,
[]byte(`{
"compact": true
}`),
), iam.DebugNotificationProviderFileChangedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: DebugNotificationProviderTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.notification_providers SET (change_date, sequence, compact) = ($1, $2, $3) WHERE (aggregate_id = $4) AND (provider_type = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
true,
"agg-id",
domain.NotificationProviderTypeFile,
},
},
},
},
},
},
{
name: "iam.reduceNotificationProviderFileRemoved",
reduce: (&DebugNotificationProviderProjection{}).reduceDebugNotificationProviderRemoved,
args: args{
event: getEvent(testEvent(
repository.EventType(iam.DebugNotificationProviderFileRemovedEventType),
iam.AggregateType,
nil,
), iam.DebugNotificationProviderFileRemovedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: DebugNotificationProviderTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM zitadel.projections.notification_providers WHERE (aggregate_id = $1) AND (provider_type = $2)",
expectedArgs: []interface{}{
"agg-id",
domain.NotificationProviderTypeFile,
},
},
},
},
},
},
{
name: "iam.reduceNotificationProviderLogAdded",
reduce: (&DebugNotificationProviderProjection{}).reduceDebugNotificationProviderAdded,
args: args{
event: getEvent(testEvent(
repository.EventType(iam.DebugNotificationProviderLogAddedEventType),
iam.AggregateType,
[]byte(`{
"compact": true
}`),
), iam.DebugNotificationProviderLogAddedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: DebugNotificationProviderTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO zitadel.projections.notification_providers (aggregate_id, creation_date, change_date, sequence, resource_owner, state, provider_type, compact) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
expectedArgs: []interface{}{
"agg-id",
anyArg{},
anyArg{},
uint64(15),
"ro-id",
domain.NotificationProviderStateActive,
domain.NotificationProviderTypeLog,
true,
},
},
},
},
},
},
{
name: "iam.reduceNotificationProviderLogChanged",
reduce: (&DebugNotificationProviderProjection{}).reduceDebugNotificationProviderChanged,
args: args{
event: getEvent(testEvent(
repository.EventType(iam.DebugNotificationProviderLogChangedEventType),
iam.AggregateType,
[]byte(`{
"compact": true
}`),
), iam.DebugNotificationProviderLogChangedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: DebugNotificationProviderTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.notification_providers SET (change_date, sequence, compact) = ($1, $2, $3) WHERE (aggregate_id = $4) AND (provider_type = $5)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
true,
"agg-id",
domain.NotificationProviderTypeLog,
},
},
},
},
},
},
{
name: "iam.reduceNotificationProviderLogRemoved",
reduce: (&DebugNotificationProviderProjection{}).reduceDebugNotificationProviderRemoved,
args: args{
event: getEvent(testEvent(
repository.EventType(iam.DebugNotificationProviderLogRemovedEventType),
iam.AggregateType,
nil,
), iam.DebugNotificationProviderLogRemovedEventMapper),
},
want: wantReduce{
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
projection: DebugNotificationProviderTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "DELETE FROM zitadel.projections.notification_providers WHERE (aggregate_id = $1) AND (provider_type = $2)",
expectedArgs: []interface{}{
"agg-id",
domain.NotificationProviderTypeLog,
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := baseEvent(t)
got, err := tt.reduce(event)
if _, ok := err.(errors.InvalidArgument); !ok {
t.Errorf("no wrong event mapping: %v, got: %v", err, got)
}
event = tt.args.event(t)
got, err = tt.reduce(event)
assertReduce(t, got, err, tt.want)
})
}
}

View File

@ -74,6 +74,7 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co
NewSMSConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["sms_config"]))
NewOIDCSettingsProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["oidc_settings"]))
_, err := NewKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyConfig, keyChan)
NewDebugNotificationProviderProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["debug_notification_provider"]))
return err
}

View File

@ -0,0 +1,106 @@
package iam
import (
"context"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
fileType = ".file"
)
var (
DebugNotificationProviderFileAddedEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + fileType + settings.DebugNotificationProviderAdded
DebugNotificationProviderFileChangedEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + fileType + settings.DebugNotificationProviderChanged
DebugNotificationProviderFileRemovedEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + fileType + settings.DebugNotificationProviderRemoved
)
type DebugNotificationProviderFileAddedEvent struct {
settings.DebugNotificationProviderAddedEvent
}
func NewDebugNotificationProviderFileAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
compact bool,
) *DebugNotificationProviderFileAddedEvent {
return &DebugNotificationProviderFileAddedEvent{
DebugNotificationProviderAddedEvent: *settings.NewDebugNotificationProviderAddedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
DebugNotificationProviderFileAddedEventType),
compact),
}
}
func DebugNotificationProviderFileAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := settings.DebugNotificationProviderAddedEventMapper(event)
if err != nil {
return nil, err
}
return &DebugNotificationProviderFileAddedEvent{DebugNotificationProviderAddedEvent: *e.(*settings.DebugNotificationProviderAddedEvent)}, nil
}
type DebugNotificationProviderFileChangedEvent struct {
settings.DebugNotificationProviderChangedEvent
}
func NewDebugNotificationProviderFileChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []settings.DebugNotificationProviderChanges,
) (*DebugNotificationProviderFileChangedEvent, error) {
changedEvent, err := settings.NewDebugNotificationProviderChangedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
DebugNotificationProviderFileChangedEventType),
changes,
)
if err != nil {
return nil, err
}
return &DebugNotificationProviderFileChangedEvent{DebugNotificationProviderChangedEvent: *changedEvent}, nil
}
func DebugNotificationProviderFileChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := settings.DebugNotificationProviderChangedEventMapper(event)
if err != nil {
return nil, err
}
return &DebugNotificationProviderFileChangedEvent{DebugNotificationProviderChangedEvent: *e.(*settings.DebugNotificationProviderChangedEvent)}, nil
}
type DebugNotificationProviderFileRemovedEvent struct {
settings.DebugNotificationProviderRemovedEvent
}
func NewDebugNotificationProviderFileRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *DebugNotificationProviderFileRemovedEvent {
return &DebugNotificationProviderFileRemovedEvent{
DebugNotificationProviderRemovedEvent: *settings.NewDebugNotificationProviderRemovedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
DebugNotificationProviderFileRemovedEventType),
),
}
}
func DebugNotificationProviderFileRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := settings.DebugNotificationProviderRemovedEventMapper(event)
if err != nil {
return nil, err
}
return &DebugNotificationProviderFileRemovedEvent{DebugNotificationProviderRemovedEvent: *e.(*settings.DebugNotificationProviderRemovedEvent)}, nil
}

View File

@ -0,0 +1,108 @@
package iam
import (
"context"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/settings"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
logType = ".log"
)
var (
DebugNotificationProviderLogAddedEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + logType + settings.DebugNotificationProviderAdded
DebugNotificationProviderLogChangedEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + logType + settings.DebugNotificationProviderChanged
DebugNotificationProviderLogEnabledEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + logType + settings.DebugNotificationProviderEnabled
DebugNotificationProviderLogDisabledEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + logType + settings.DebugNotificationProviderDisabled
DebugNotificationProviderLogRemovedEventType = iamEventTypePrefix + settings.DebugNotificationPrefix + logType + settings.DebugNotificationProviderRemoved
)
type DebugNotificationProviderLogAddedEvent struct {
settings.DebugNotificationProviderAddedEvent
}
func NewDebugNotificationProviderLogAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
compact bool,
) *DebugNotificationProviderLogAddedEvent {
return &DebugNotificationProviderLogAddedEvent{
DebugNotificationProviderAddedEvent: *settings.NewDebugNotificationProviderAddedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
DebugNotificationProviderLogAddedEventType),
compact),
}
}
func DebugNotificationProviderLogAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := settings.DebugNotificationProviderAddedEventMapper(event)
if err != nil {
return nil, err
}
return &DebugNotificationProviderLogAddedEvent{DebugNotificationProviderAddedEvent: *e.(*settings.DebugNotificationProviderAddedEvent)}, nil
}
type DebugNotificationProviderLogChangedEvent struct {
settings.DebugNotificationProviderChangedEvent
}
func NewDebugNotificationProviderLogChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []settings.DebugNotificationProviderChanges,
) (*DebugNotificationProviderLogChangedEvent, error) {
changedEvent, err := settings.NewDebugNotificationProviderChangedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
DebugNotificationProviderLogChangedEventType),
changes,
)
if err != nil {
return nil, err
}
return &DebugNotificationProviderLogChangedEvent{DebugNotificationProviderChangedEvent: *changedEvent}, nil
}
func DebugNotificationProviderLogChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := settings.DebugNotificationProviderChangedEventMapper(event)
if err != nil {
return nil, err
}
return &DebugNotificationProviderLogChangedEvent{DebugNotificationProviderChangedEvent: *e.(*settings.DebugNotificationProviderChangedEvent)}, nil
}
type DebugNotificationProviderLogRemovedEvent struct {
settings.DebugNotificationProviderRemovedEvent
}
func NewDebugNotificationProviderLogRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
) *DebugNotificationProviderLogRemovedEvent {
return &DebugNotificationProviderLogRemovedEvent{
DebugNotificationProviderRemovedEvent: *settings.NewDebugNotificationProviderRemovedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
DebugNotificationProviderLogRemovedEventType),
),
}
}
func DebugNotificationProviderLogRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
e, err := settings.DebugNotificationProviderRemovedEventMapper(event)
if err != nil {
return nil, err
}
return &DebugNotificationProviderLogRemovedEvent{DebugNotificationProviderRemovedEvent: *e.(*settings.DebugNotificationProviderRemovedEvent)}, nil
}

View File

@ -23,6 +23,12 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(SMSConfigActivatedEventType, SMSConfigActivatedEventMapper).
RegisterFilterEventMapper(SMSConfigDeactivatedEventType, SMSConfigDeactivatedEventMapper).
RegisterFilterEventMapper(SMSConfigRemovedEventType, SMSConfigRemovedEventMapper).
RegisterFilterEventMapper(DebugNotificationProviderFileAddedEventType, DebugNotificationProviderFileAddedEventMapper).
RegisterFilterEventMapper(DebugNotificationProviderFileChangedEventType, DebugNotificationProviderFileChangedEventMapper).
RegisterFilterEventMapper(DebugNotificationProviderFileRemovedEventType, DebugNotificationProviderFileRemovedEventMapper).
RegisterFilterEventMapper(DebugNotificationProviderLogAddedEventType, DebugNotificationProviderLogAddedEventMapper).
RegisterFilterEventMapper(DebugNotificationProviderLogChangedEventType, DebugNotificationProviderLogChangedEventMapper).
RegisterFilterEventMapper(DebugNotificationProviderLogRemovedEventType, DebugNotificationProviderLogRemovedEventMapper).
RegisterFilterEventMapper(OIDCSettingsAddedEventType, OIDCSettingsAddedEventMapper).
RegisterFilterEventMapper(OIDCSettingsChangedEventType, OIDCSettingsChangedEventMapper).
RegisterFilterEventMapper(LabelPolicyAddedEventType, LabelPolicyAddedEventMapper).

View File

@ -0,0 +1,130 @@
package settings
import (
"encoding/json"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
DebugNotificationPrefix = "notification.provider.debug"
DebugNotificationProviderAdded = "added"
DebugNotificationProviderChanged = "changed"
DebugNotificationProviderEnabled = "enabled"
DebugNotificationProviderDisabled = "disabled"
DebugNotificationProviderRemoved = "removed"
)
type DebugNotificationProviderAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Compact bool `json:"compact,omitempty"`
}
func (e *DebugNotificationProviderAddedEvent) Data() interface{} {
return e
}
func (e *DebugNotificationProviderAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewDebugNotificationProviderAddedEvent(
base *eventstore.BaseEvent,
compact bool,
) *DebugNotificationProviderAddedEvent {
return &DebugNotificationProviderAddedEvent{
BaseEvent: *base,
Compact: compact,
}
}
func DebugNotificationProviderAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &DebugNotificationProviderAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "SET-f93ns", "unable to unmarshal debug notification added")
}
return e, nil
}
type DebugNotificationProviderChangedEvent struct {
eventstore.BaseEvent `json:"-"`
Compact *bool `json:"compact,omitempty"`
}
func (e *DebugNotificationProviderChangedEvent) Data() interface{} {
return e
}
func (e *DebugNotificationProviderChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewDebugNotificationProviderChangedEvent(
base *eventstore.BaseEvent,
changes []DebugNotificationProviderChanges,
) (*DebugNotificationProviderChangedEvent, error) {
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "SET-hj90s", "Errors.NoChangesFound")
}
changeEvent := &DebugNotificationProviderChangedEvent{
BaseEvent: *base,
}
for _, change := range changes {
change(changeEvent)
}
return changeEvent, nil
}
type DebugNotificationProviderChanges func(*DebugNotificationProviderChangedEvent)
func ChangeCompact(compact bool) func(*DebugNotificationProviderChangedEvent) {
return func(e *DebugNotificationProviderChangedEvent) {
e.Compact = &compact
}
}
func DebugNotificationProviderChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &DebugNotificationProviderChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "POLIC-ehssl", "unable to unmarshal policy")
}
return e, nil
}
type DebugNotificationProviderRemovedEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *DebugNotificationProviderRemovedEvent) Data() interface{} {
return nil
}
func (e *DebugNotificationProviderRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewDebugNotificationProviderRemovedEvent(base *eventstore.BaseEvent) *DebugNotificationProviderRemovedEvent {
return &DebugNotificationProviderRemovedEvent{
BaseEvent: *base,
}
}
func DebugNotificationProviderRemovedEventMapper(event *repository.Event) (eventstore.Event, error) {
return &DebugNotificationProviderRemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}

View File

@ -2,11 +2,11 @@ syntax = "proto3";
import "zitadel/idp.proto";
import "zitadel/user.proto";
import "zitadel/settings.proto";
import "zitadel/object.proto";
import "zitadel/options.proto";
import "zitadel/org.proto";
import "zitadel/policy.proto";
import "zitadel/settings.proto";
import "zitadel/text.proto";
import "zitadel/member.proto";
import "zitadel/features.proto";
@ -336,6 +336,28 @@ service AdminService {
};
}
// Get file system notification provider
rpc GetFileSystemNotificationProvider(GetFileSystemNotificationProviderRequest) returns (GetFileSystemNotificationProviderResponse) {
option (google.api.http) = {
get: "/notification/provider/file";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
}
// Get log notification provider
rpc GetLogNotificationProvider(GetLogNotificationProviderRequest) returns (GetLogNotificationProviderResponse) {
option (google.api.http) = {
get: "/notification/provider/log";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
}
// Returns an organisation by id
rpc GetOrgByID(GetOrgByIDRequest) returns (GetOrgByIDResponse) {
option (google.api.http) = {
@ -2561,6 +2583,20 @@ message UpdateSMSProviderTwilioTokenResponse {
zitadel.v1.ObjectDetails details = 1;
}
//This is an empty request
message GetFileSystemNotificationProviderRequest {}
message GetFileSystemNotificationProviderResponse {
zitadel.settings.v1.DebugNotificationProvider provider = 1;
}
//This is an empty request
message GetLogNotificationProviderRequest {}
message GetLogNotificationProviderResponse {
zitadel.settings.v1.DebugNotificationProvider provider = 1;
}
// This is an empty request
message GetOIDCSettingsRequest {}

View File

@ -72,6 +72,11 @@ enum SMSProviderConfigState {
SMS_PROVIDER_CONFIG_INACTIVE = 2;
}
message DebugNotificationProvider {
zitadel.v1.ObjectDetails details = 1;
bool compact = 2;
}
message OIDCSettings {
zitadel.v1.ObjectDetails details = 1;
google.protobuf.Duration access_token_lifetime = 2;