fix: sms providers (#3801)

This commit is contained in:
Livio Spring 2022-06-13 08:34:11 +02:00 committed by GitHub
parent 504d91d424
commit f57e3df39d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 295 additions and 45 deletions

View File

@ -224,6 +224,30 @@ Update twilio sms provider token
PUT: /sms/twilio/{id}/token
### ActivateSMSProvider
> **rpc** ActivateSMSProvider([ActivateSMSProviderRequest](#activatesmsproviderrequest))
[ActivateSMSProviderResponse](#activatesmsproviderresponse)
Activate sms provider
POST: /sms/{id}/_activate
### DeactivateSMSProvider
> **rpc** DeactivateSMSProvider([DeactivateSMSProviderRequest](#deactivatesmsproviderrequest))
[DeactivateSMSProviderResponse](#deactivatesmsproviderresponse)
Deactivate sms provider
POST: /sms/{id}/_deactivate
### RemoveSMSProvider
> **rpc** RemoveSMSProvider([RemoveSMSProviderRequest](#removesmsproviderrequest))
@ -1482,6 +1506,28 @@ This is an empty request
### ActivateSMSProviderRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
### ActivateSMSProviderResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### AddCustomDomainPolicyRequest
@ -1753,6 +1799,28 @@ This is an empty request
### DeactivateSMSProviderRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| id | string | - | string.min_len: 1<br /> string.max_len: 200<br /> |
### DeactivateSMSProviderResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### FailedEvent

View File

@ -6,7 +6,6 @@ import (
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/grpc/object"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
settings_pb "github.com/zitadel/zitadel/pkg/grpc/settings"
)
func (s *Server) ListSMSProviders(ctx context.Context, req *admin_pb.ListSMSProvidersRequest) (*admin_pb.ListSMSProvidersResponse, error) {
@ -17,10 +16,10 @@ func (s *Server) ListSMSProviders(ctx context.Context, req *admin_pb.ListSMSProv
result, err := s.query.SearchSMSConfigs(ctx, queries)
if err != nil {
return nil, err
}
return &admin_pb.ListSMSProvidersResponse{
Details: object.ToListDetails(result.Count, result.Sequence, result.Timestamp),
Result: SMSConfigsToPb(result.Configs),
}, nil
}
@ -28,15 +27,9 @@ func (s *Server) GetSMSProvider(ctx context.Context, req *admin_pb.GetSMSProvide
result, err := s.query.SMSProviderConfigByID(ctx, req.Id)
if err != nil {
return nil, err
}
return &admin_pb.GetSMSProviderResponse{
Config: &settings_pb.SMSProvider{
Details: object.ToViewDetailsPb(result.Sequence, result.CreationDate, result.ChangeDate, result.ResourceOwner),
Id: result.ID,
State: smsStateToPb(result.State),
Config: SMSConfigToPb(result),
},
Config: SMSConfigToProviderPb(result),
}, nil
}
@ -44,7 +37,6 @@ func (s *Server) AddSMSProviderTwilio(ctx context.Context, req *admin_pb.AddSMSP
id, result, err := s.command.AddSMSConfigTwilio(ctx, authz.GetInstance(ctx).InstanceID(), AddSMSConfigTwilioToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.AddSMSProviderTwilioResponse{
Details: object.DomainToAddDetailsPb(result),
@ -56,7 +48,6 @@ func (s *Server) UpdateSMSProviderTwilio(ctx context.Context, req *admin_pb.Upda
result, err := s.command.ChangeSMSConfigTwilio(ctx, authz.GetInstance(ctx).InstanceID(), req.Id, UpdateSMSConfigTwilioToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.UpdateSMSProviderTwilioResponse{
Details: object.DomainToChangeDetailsPb(result),
@ -74,11 +65,31 @@ func (s *Server) UpdateSMSProviderTwilioToken(ctx context.Context, req *admin_pb
}, nil
}
func (s *Server) ActivateSMSProvider(ctx context.Context, req *admin_pb.ActivateSMSProviderRequest) (*admin_pb.ActivateSMSProviderResponse, error) {
result, err := s.command.ActivateSMSConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.ActivateSMSProviderResponse{
Details: object.DomainToAddDetailsPb(result),
}, nil
}
func (s *Server) DeactivateSMSProvider(ctx context.Context, req *admin_pb.DeactivateSMSProviderRequest) (*admin_pb.DeactivateSMSProviderResponse, error) {
result, err := s.command.DeactivateSMSConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.DeactivateSMSProviderResponse{
Details: object.DomainToAddDetailsPb(result),
}, nil
}
func (s *Server) RemoveSMSProvider(ctx context.Context, req *admin_pb.RemoveSMSProviderRequest) (*admin_pb.RemoveSMSProviderResponse, error) {
result, err := s.command.RemoveSMSConfig(ctx, authz.GetInstance(ctx).InstanceID(), req.Id)
if err != nil {
return nil, err
}
return &admin_pb.RemoveSMSProviderResponse{
Details: object.DomainToAddDetailsPb(result),

View File

@ -20,9 +20,26 @@ func listSMSConfigsToModel(req *admin_pb.ListSMSProvidersRequest) (*query.SMSCon
}, nil
}
func SMSConfigToPb(app *query.SMSConfig) settings_pb.SMSConfig {
if app.TwilioConfig != nil {
return TwilioConfigToPb(app.TwilioConfig)
func SMSConfigsToPb(configs []*query.SMSConfig) []*settings_pb.SMSProvider {
c := make([]*settings_pb.SMSProvider, len(configs))
for i, config := range configs {
c[i] = SMSConfigToProviderPb(config)
}
return c
}
func SMSConfigToProviderPb(config *query.SMSConfig) *settings_pb.SMSProvider {
return &settings_pb.SMSProvider{
Details: object.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.ResourceOwner),
Id: config.ID,
State: smsStateToPb(config.State),
Config: SMSConfigToPb(config),
}
}
func SMSConfigToPb(config *query.SMSConfig) settings_pb.SMSConfig {
if config.TwilioConfig != nil {
return TwilioConfigToPb(config.TwilioConfig)
}
return nil
}

View File

@ -110,7 +110,7 @@ func (c *Commands) ChangeSMSConfigTwilioToken(ctx context.Context, instanceID, i
return writeModelToObjectDetails(&smsConfigWriteModel.WriteModel), nil
}
func (c *Commands) ActivateSMSConfigTwilio(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) {
func (c *Commands) ActivateSMSConfig(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "SMS-dn93n", "Errors.IDMissing")
}
@ -119,7 +119,7 @@ func (c *Commands) ActivateSMSConfigTwilio(ctx context.Context, instanceID, id s
return nil, err
}
if !smsConfigWriteModel.State.Exists() || smsConfigWriteModel.Twilio == nil {
if !smsConfigWriteModel.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-sn9we", "Errors.SMSConfig.NotFound")
}
if smsConfigWriteModel.State == domain.SMSConfigStateActive {
@ -140,7 +140,7 @@ func (c *Commands) ActivateSMSConfigTwilio(ctx context.Context, instanceID, id s
return writeModelToObjectDetails(&smsConfigWriteModel.WriteModel), nil
}
func (c *Commands) DeactivateSMSConfigTwilio(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) {
func (c *Commands) DeactivateSMSConfig(ctx context.Context, instanceID, id string) (*domain.ObjectDetails, error) {
if id == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "SMS-frkwf", "Errors.IDMissing")
}
@ -148,7 +148,7 @@ func (c *Commands) DeactivateSMSConfigTwilio(ctx context.Context, instanceID, id
if err != nil {
return nil, err
}
if !smsConfigWriteModel.State.Exists() || smsConfigWriteModel.Twilio == nil {
if !smsConfigWriteModel.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-s39Kg", "Errors.SMSConfig.NotFound")
}
if smsConfigWriteModel.State == domain.SMSConfigStateInactive {

View File

@ -364,7 +364,7 @@ func TestCommandSide_ActivateSMSConfigTwilio(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.ActivateSMSConfigTwilio(tt.args.ctx, tt.args.instanceID, tt.args.id)
got, err := r.ActivateSMSConfig(tt.args.ctx, tt.args.instanceID, tt.args.id)
if tt.res.err == nil {
assert.NoError(t, err)
}
@ -482,7 +482,7 @@ func TestCommandSide_DeactivateSMSConfigTwilio(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.DeactivateSMSConfigTwilio(tt.args.ctx, tt.args.instanceID, tt.args.id)
got, err := r.DeactivateSMSConfig(tt.args.ctx, tt.args.instanceID, tt.args.id)
if tt.res.err == nil {
assert.NoError(t, err)
}

View File

@ -1,8 +1,9 @@
package twilio
import (
twilio "github.com/kevinburke/twilio-go"
"github.com/kevinburke/twilio-go"
"github.com/zitadel/logging"
caos_errs "github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/notification/channels"
"github.com/zitadel/zitadel/internal/notification/messages"
@ -11,7 +12,7 @@ import (
func InitTwilioChannel(config TwilioConfig) channels.NotificationChannel {
client := twilio.NewClient(config.SID, config.Token, nil)
logging.Log("NOTIF-KaxDZ").Debug("successfully initialized twilio sms channel")
logging.Debug("successfully initialized twilio sms channel")
return channels.HandleMessageFunc(func(message channels.Message) error {
twilioMsg, ok := message.(*messages.SMS)
@ -22,7 +23,7 @@ func InitTwilioChannel(config TwilioConfig) channels.NotificationChannel {
if err != nil {
return caos_errs.ThrowInternal(err, "TWILI-osk3S", "could not send message")
}
logging.LogWithFields("SMS_-f335c523", "message_sid", m.Sid, "status", m.Status).Debug("sms sent")
logging.WithFields("message_sid", m.Sid, "status", m.Status).Debug("sms sent")
return nil
})
}

View File

@ -494,14 +494,18 @@ func (n *Notification) getSMTPConfig(ctx context.Context) (*smtp.EmailConfig, er
// Read iam twilio config
func (n *Notification) getTwilioConfig(ctx context.Context) (*twilio.TwilioConfig, error) {
config, err := n.queries.SMSProviderConfigByID(ctx, authz.GetInstance(ctx).InstanceID())
active, err := query.NewSMSProviderStateQuery(domain.SMSConfigStateActive)
if err != nil {
return nil, err
}
config, err := n.queries.SMSProviderConfig(ctx, active)
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)
token, err := crypto.Decrypt(config.TwilioConfig.Token, n.smsTokenCrypto)
if err != nil {
return nil, err
}

View File

@ -82,6 +82,10 @@ func (p *SMSConfigProjection) reducers() []handler.AggregateReducer {
Event: instance.SMSConfigTwilioChangedEventType,
Reduce: p.reduceSMSConfigTwilioChanged,
},
{
Event: instance.SMSConfigTwilioTokenChangedEventType,
Reduce: p.reduceSMSConfigTwilioTokenChanged,
},
{
Event: instance.SMSConfigActivatedEventType,
Reduce: p.reduceSMSConfigActivated,
@ -111,7 +115,6 @@ func (p *SMSConfigProjection) reduceSMSConfigTwilioAdded(event eventstore.Event)
[]handler.Column{
handler.NewCol(SMSColumnID, e.ID),
handler.NewCol(SMSColumnAggregateID, e.Aggregate().ID),
handler.NewCol(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SMSColumnCreationDate, e.CreationDate()),
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnResourceOwner, e.Aggregate().ResourceOwner),
@ -140,10 +143,43 @@ func (p *SMSConfigProjection) reduceSMSConfigTwilioChanged(event eventstore.Even
}
columns := make([]handler.Column, 0)
if e.SID != nil {
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSID, e.SID))
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSID, *e.SID))
}
if e.SenderNumber != nil {
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSenderNumber, e.SenderNumber))
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnSenderNumber, *e.SenderNumber))
}
return crdb.NewMultiStatement(
e,
crdb.AddUpdateStatement(
columns,
[]handler.Condition{
handler.NewCond(SMSTwilioConfigColumnSMSID, e.ID),
handler.NewCond(SMSTwilioColumnInstanceID, e.Aggregate().InstanceID),
},
crdb.WithTableSuffix(smsTwilioTableSuffix),
),
crdb.AddUpdateStatement(
[]handler.Column{
handler.NewCol(SMSColumnChangeDate, e.CreationDate()),
handler.NewCol(SMSColumnSequence, e.Sequence()),
},
[]handler.Condition{
handler.NewCond(SMSColumnID, e.ID),
handler.NewCond(SMSColumnInstanceID, e.Aggregate().InstanceID),
},
),
), nil
}
func (p *SMSConfigProjection) reduceSMSConfigTwilioTokenChanged(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SMSConfigTwilioTokenChangedEvent)
if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-fi99F", "reduce.wrong.event.type %s", instance.SMSConfigTwilioTokenChangedEventType)
}
columns := make([]handler.Column, 0)
if e.Token != nil {
columns = append(columns, handler.NewCol(SMSTwilioConfigColumnToken, e.Token))
}
return crdb.NewMultiStatement(

View File

@ -3,6 +3,7 @@ package projection
import (
"testing"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
@ -11,12 +12,6 @@ import (
"github.com/zitadel/zitadel/internal/repository/instance"
)
var (
sid = "sid"
token = "token"
senderNumber = "sender-number"
)
func TestSMSProjection_reduces(t *testing.T) {
type args struct {
event func(t *testing.T) eventstore.Event
@ -39,7 +34,8 @@ func TestSMSProjection_reduces(t *testing.T) {
"token": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id"
"keyId": "key-id",
"crypted": "Y3J5cHRlZA=="
},
"senderNumber": "sender-number"
}`),
@ -54,11 +50,10 @@ func TestSMSProjection_reduces(t *testing.T) {
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO projections.sms_configs (id, aggregate_id, instance_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedStmt: "INSERT INTO projections.sms_configs (id, aggregate_id, creation_date, change_date, resource_owner, instance_id, state, sequence) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
expectedArgs: []interface{}{
"id",
"agg-id",
"instance-id",
anyArg{},
anyArg{},
"ro-id",
@ -73,7 +68,12 @@ func TestSMSProjection_reduces(t *testing.T) {
"id",
"instance-id",
"sid",
anyArg{},
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "RSA-265",
KeyID: "key-id",
Crypted: []byte("crypted"),
},
"sender-number",
},
},
@ -105,8 +105,59 @@ func TestSMSProjection_reduces(t *testing.T) {
{
expectedStmt: "UPDATE projections.sms_configs_twilio SET (sid, sender_number) = ($1, $2) WHERE (sms_id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
&sid,
&senderNumber,
"sid",
"sender-number",
"id",
"instance-id",
},
},
{
expectedStmt: "UPDATE projections.sms_configs SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
"id",
"instance-id",
},
},
},
},
},
},
{
name: "instance.reduceSMSConfigTwilioTokenChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(instance.SMSConfigTwilioTokenChangedEventType),
instance.AggregateType,
[]byte(`{
"id": "id",
"token": {
"cryptoType": 0,
"algorithm": "RSA-265",
"keyId": "key-id",
"crypted": "Y3J5cHRlZA=="
}
}`),
), instance.SMSConfigTwilioTokenChangedEventMapper),
},
reduce: (&SMSConfigProjection{}).reduceSMSConfigTwilioTokenChanged,
want: wantReduce{
aggregateType: eventstore.AggregateType("instance"),
sequence: 15,
previousSequence: 10,
projection: SMSConfigProjectionTable,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE projections.sms_configs_twilio SET (token) = ($1) WHERE (sms_id = $2) AND (instance_id = $3)",
expectedArgs: []interface{}{
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "RSA-265",
KeyID: "key-id",
Crypted: []byte("crypted"),
},
"id",
"instance-id",
},

View File

@ -112,8 +112,8 @@ var (
)
func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (*SMSConfig, error) {
stmt, scan := prepareSMSConfigQuery()
query, args, err := stmt.Where(
query, scan := prepareSMSConfigQuery()
stmt, args, err := query.Where(
sq.Eq{
SMSConfigColumnID.identifier(): id,
SMSConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
@ -123,7 +123,25 @@ func (q *Queries) SMSProviderConfigByID(ctx context.Context, id string) (*SMSCon
return nil, errors.ThrowInternal(err, "QUERY-dn9JW", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
func (q *Queries) SMSProviderConfig(ctx context.Context, queries ...SearchQuery) (*SMSConfig, error) {
query, scan := prepareSMSConfigQuery()
for _, searchQuery := range queries {
query = searchQuery.toQuery(query)
}
stmt, args, err := query.Where(
sq.Eq{
SMSConfigColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
},
).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-dn9JW", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, stmt, args...)
return scan(row)
}
@ -149,6 +167,10 @@ func (q *Queries) SearchSMSConfigs(ctx context.Context, queries *SMSConfigsSearc
return apps, err
}
func NewSMSProviderStateQuery(state domain.SMSConfigState) (SearchQuery, error) {
return NewNumberQuery(SMSConfigColumnState, state, NumberEquals)
}
func prepareSMSConfigQuery() (sq.SelectBuilder, func(*sql.Row) (*SMSConfig, error)) {
return sq.Select(
SMSConfigColumnID.identifier(),

View File

@ -140,7 +140,7 @@ type SMSConfigTwilioTokenChangedEvent struct {
eventstore.BaseEvent `json:"-"`
ID string `json:"id,omitempty"`
Token *crypto.CryptoValue `json:"password,omitempty"`
Token *crypto.CryptoValue `json:"token,omitempty"`
}
func NewSMSConfigTokenChangedEvent(

View File

@ -343,6 +343,30 @@ service AdminService {
};
}
// Activate sms provider
rpc ActivateSMSProvider(ActivateSMSProviderRequest) returns (ActivateSMSProviderResponse) {
option (google.api.http) = {
post: "/sms/{id}/_activate";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
}
// Deactivate sms provider
rpc DeactivateSMSProvider(DeactivateSMSProviderRequest) returns (DeactivateSMSProviderResponse) {
option (google.api.http) = {
post: "/sms/{id}/_deactivate";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
}
// Remove sms provider token
rpc RemoveSMSProvider(RemoveSMSProviderRequest) returns (RemoveSMSProviderResponse) {
option (google.api.http) = {
@ -2783,6 +2807,22 @@ message UpdateSMSProviderTwilioTokenResponse {
zitadel.v1.ObjectDetails details = 1;
}
message ActivateSMSProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message ActivateSMSProviderResponse {
zitadel.v1.ObjectDetails details = 1;
}
message DeactivateSMSProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}
message DeactivateSMSProviderResponse {
zitadel.v1.ObjectDetails details = 1;
}
message RemoveSMSProviderRequest {
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
}