Skip to content

Commit

Permalink
Add custom SMS gateway configuration screen
Browse files Browse the repository at this point in the history
ref DEV-2301
  • Loading branch information
louischan-oursky authored and tung2744 committed Jan 20, 2025
2 parents 6ceb5ef + d8a2b82 commit e0e0118
Show file tree
Hide file tree
Showing 38 changed files with 1,947 additions and 364 deletions.
2 changes: 1 addition & 1 deletion cmd/authgear/background/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion e2e/cmd/e2e/pkg/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/admin/wire_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

342 changes: 171 additions & 171 deletions pkg/auth/wire_gen.go

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions pkg/lib/config/messaging.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ var _ = Schema.Add("MessagingConfig", `
`)

type MessagingConfig struct {
SMSProvider SMSProvider `json:"sms_provider,omitempty"`
SMSGateway *SMSGatewayConfig `json:"sms_gateway,omitempty" nullable:"true"`
Deprecated_SMS *SMSConfig `json:"sms,omitempty"`
Deprecated_Email *EmailConfig `json:"email,omitempty"`
Whatsapp *WhatsappConfig `json:"whatsapp,omitempty"`
RateLimits *MessagingRateLimitsConfig `json:"rate_limits,omitempty"`
Deprecated_SMSProvider SMSProvider `json:"sms_provider,omitempty"`
SMSGateway *SMSGatewayConfig `json:"sms_gateway,omitempty" nullable:"true"`
Deprecated_SMS *SMSConfig `json:"sms,omitempty"`
Deprecated_Email *EmailConfig `json:"email,omitempty"`
Whatsapp *WhatsappConfig `json:"whatsapp,omitempty"`
RateLimits *MessagingRateLimitsConfig `json:"rate_limits,omitempty"`
}

func (c *MessagingConfig) SetDefaults() {
Expand Down
4 changes: 2 additions & 2 deletions pkg/lib/config/secret_custom_sms_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ var _ = SecretConfigSchema.Add("CustomSMSProviderConfig", `
"type": "object",
"additionalProperties": false,
"properties": {
"url": { "type": "string" },
"timeout": { "type": "integer" }
"url": { "type": "string", "format": "x_hook_uri" },
"timeout": { "type": "integer", "minimum": 1, "maximum": 60 }
},
"required": ["url"]
}
Expand Down
117 changes: 117 additions & 0 deletions pkg/lib/config/secret_update_instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ type SecretConfigUpdateInstruction struct {
BotProtectionProviderCredentialsUpdateInstruction *BotProtectionProviderCredentialsUpdateInstruction `json:"botProtectionProviderSecret,omitempty"`
SAMLIdpSigningSecretsUpdateInstruction *SAMLIdpSigningSecretsUpdateInstruction `json:"samlIdpSigningSecrets,omitempty"`
SAMLSpSigningSecretsUpdateInstruction *SAMLSpSigningSecretsUpdateInstruction `json:"samlSpSigningSecrets,omitempty"`
SMSProviderSecretsUpdateInstruction *SMSProviderSecretsUpdateInstruction `json:"smsProviderSecrets,omitempty"`
}

func (i *SecretConfigUpdateInstruction) ApplyTo(ctx *SecretConfigUpdateInstructionContext, currentConfig *SecretConfig) (*SecretConfig, error) {
Expand Down Expand Up @@ -84,6 +85,13 @@ func (i *SecretConfigUpdateInstruction) ApplyTo(ctx *SecretConfigUpdateInstructi
}
}

if i.SMSProviderSecretsUpdateInstruction != nil {
newConfig, err = i.SMSProviderSecretsUpdateInstruction.ApplyTo(ctx, newConfig)
if err != nil {
return nil, err
}
}

return newConfig, nil
}

Expand Down Expand Up @@ -766,6 +774,114 @@ func (i *SAMLSpSigningSecretsUpdateInstruction) set(currentConfig *SecretConfig)
return out, nil
}

type SMSProviderSecretsUpdateInstructionSetData struct {
TwilioCredentials *SMSProviderSecretsUpdateInstructionTwilioCredentials `json:"twilioCredentials,omitempty"`
CustomSMSProviderCredentials *SMSProviderSecretsUpdateInstructionCustomSMSProvider `json:"customSMSProviderCredentials,omitempty"`
}

type SMSProviderSecretsUpdateInstructionTwilioCredentials struct {
AccountSID string `json:"accountSID,omitempty"`
AuthToken string `json:"authToken,omitempty"`
MessagingServiceSID string `json:"messagingServiceSID,omitempty"`
}

type SMSProviderSecretsUpdateInstructionCustomSMSProvider struct {
URL string `json:"url,omitempty"`
Timeout *DurationSeconds `json:"timeout,omitempty"`
}

type SMSProviderSecretsUpdateInstruction struct {
Action SecretUpdateInstructionAction `json:"action,omitempty"`
SetData *SMSProviderSecretsUpdateInstructionSetData `json:"setData,omitempty"`
}

func (i *SMSProviderSecretsUpdateInstruction) ApplyTo(ctx *SecretConfigUpdateInstructionContext, currentConfig *SecretConfig) (*SecretConfig, error) {
switch i.Action {
case SecretUpdateInstructionActionSet:
return i.set(currentConfig)
default:
return nil, fmt.Errorf("config: unexpected action for SMSProviderSecretsUpdateInstruction: %s", i.Action)
}
}

func (i *SMSProviderSecretsUpdateInstruction) set(currentConfig *SecretConfig) (*SecretConfig, error) {
out := &SecretConfig{}
for _, item := range currentConfig.Secrets {
out.Secrets = append(out.Secrets, item)
}

if i.SetData == nil {
return nil, fmt.Errorf("config: missing SetData for SMSProviderSecretsUpdateInstruction")
}

upsert := func(credentialKey SecretKey, secrets any) error {
var data []byte
data, err := json.Marshal(secrets)
if err != nil {
return err
}
newSecretItem := SecretItem{
Key: credentialKey,
RawData: json.RawMessage(data),
}

idx, _, found := out.LookupDataWithIndex(credentialKey)
if found {
out.Secrets[idx] = newSecretItem
} else {
out.Secrets = append(out.Secrets, newSecretItem)
}
return nil
}

remove := func(credentialKey SecretKey) error {
newSecretItems := []SecretItem{}

for _, item := range out.Secrets {
if item.Key != credentialKey {
newSecretItems = append(newSecretItems, item)
}
}

out.Secrets = newSecretItems
return nil
}

if i.SetData.TwilioCredentials != nil {
twilioCredentials := TwilioCredentials{
AccountSID: i.SetData.TwilioCredentials.AccountSID,
AuthToken: i.SetData.TwilioCredentials.AuthToken,
MessagingServiceSID: i.SetData.TwilioCredentials.MessagingServiceSID,
}
err := upsert(TwilioCredentialsKey, twilioCredentials)
if err != nil {
return nil, err
}
} else {
err := remove(TwilioCredentialsKey)
if err != nil {
return nil, err
}
}

if i.SetData.CustomSMSProviderCredentials != nil {
customSMSProviderConfig := CustomSMSProviderConfig{
URL: i.SetData.CustomSMSProviderCredentials.URL,
Timeout: i.SetData.CustomSMSProviderCredentials.Timeout,
}
err := upsert(CustomSMSProviderConfigKey, customSMSProviderConfig)
if err != nil {
return nil, err
}
} else {
err := remove(CustomSMSProviderConfigKey)
if err != nil {
return nil, err
}
}
return out, nil
}

var _ SecretConfigUpdateInstructionInterface = &SecretConfigUpdateInstruction{}
var _ SecretConfigUpdateInstructionInterface = &OAuthSSOProviderCredentialsUpdateInstruction{}
var _ SecretConfigUpdateInstructionInterface = &SMTPServerCredentialsUpdateInstruction{}
Expand All @@ -774,3 +890,4 @@ var _ SecretConfigUpdateInstructionInterface = &AdminAPIAuthKeyUpdateInstruction
var _ SecretConfigUpdateInstructionInterface = &BotProtectionProviderCredentialsUpdateInstruction{}
var _ SecretConfigUpdateInstructionInterface = &SAMLIdpSigningSecretsUpdateInstruction{}
var _ SecretConfigUpdateInstructionInterface = &SAMLSpSigningSecretsUpdateInstruction{}
var _ SecretConfigUpdateInstructionInterface = &SMSProviderSecretsUpdateInstruction{}
2 changes: 1 addition & 1 deletion pkg/lib/deps/deps_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ var ConfigDeps = wire.NewSet(
"LDAP",
),
wire.FieldsOf(new(*config.MessagingConfig),
"SMSProvider",
"Deprecated_SMSProvider",
"SMSGateway",
"Whatsapp",
"RateLimits",
Expand Down
2 changes: 1 addition & 1 deletion pkg/lib/infra/sms/client_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func TestClientResolver(t *testing.T) {
var authgearYAMLSMSProvider config.SMSProvider
var authgearYAMLSMSGateway *config.SMSGatewayConfig
if messagingConfig != nil {
authgearYAMLSMSProvider = messagingConfig.SMSProvider
authgearYAMLSMSProvider = messagingConfig.Deprecated_SMSProvider
authgearYAMLSMSGateway = messagingConfig.SMSGateway
}

Expand Down
64 changes: 57 additions & 7 deletions pkg/portal/graphql/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,48 @@ var samlSpSigningSecret = graphql.NewObject(graphql.ObjectConfig{
},
})

var smsProviderTwilioCredentials = graphql.NewObject(graphql.ObjectConfig{
Name: "SMSProviderTwilioCredentials",
Description: "Twilio credentials",
Fields: graphql.Fields{
"accountSID": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
"authToken": &graphql.Field{
Type: graphql.String,
},
"messagingServiceSID": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
},
})

var smsProviderCustomSmsProviderSecrets = graphql.NewObject(graphql.ObjectConfig{
Name: "SMSProviderCustomSMSProviderSecrets",
Description: "Custom SMS Provider configs",
Fields: graphql.Fields{
"url": &graphql.Field{
Type: graphql.NewNonNull(graphql.String),
},
"timeout": &graphql.Field{
Type: graphql.Int,
},
},
})

var smsProviderSecret = graphql.NewObject(graphql.ObjectConfig{
Name: "SMSProviderSecrets",
Description: "SMS Provider secrets",
Fields: graphql.Fields{
"twilioCredentials": &graphql.Field{
Type: smsProviderTwilioCredentials,
},
"customSMSProviderCredentials": &graphql.Field{
Type: smsProviderCustomSmsProviderSecrets,
},
},
})

type AppSecretKey string

const (
Expand All @@ -182,6 +224,7 @@ const (
AppSecretKeyBotProtectionProviderSecret AppSecretKey = "botProtectionProviderSecret"
AppSecretKeySAMLIdpSigningSecrets AppSecretKey = "samlIdpSigningSecrets" // nolint:gosec
AppSecretKeySAMLSpSigningSecrets AppSecretKey = "samlSpSigningSecrets" // nolint:gosec
AppSecretKeySMSProviderSecrets AppSecretKey = "smsProviderSecrets" // nolint:gosec
)

var secretConfig = graphql.NewObject(graphql.ObjectConfig{
Expand Down Expand Up @@ -212,6 +255,9 @@ var secretConfig = graphql.NewObject(graphql.ObjectConfig{
string(AppSecretKeySAMLSpSigningSecrets): &graphql.Field{
Type: graphql.NewList(graphql.NewNonNull(samlSpSigningSecret)),
},
string(AppSecretKeySMSProviderSecrets): &graphql.Field{
Type: smsProviderSecret,
},
},
})

Expand Down Expand Up @@ -242,16 +288,20 @@ var appSecretKey = graphql.NewEnum(graphql.EnumConfig{
"SAML_SP_SIGNING_SECRETS": &graphql.EnumValueConfig{
Value: AppSecretKeySAMLSpSigningSecrets,
},
"SMS_PROVIDER_SECRETS": &graphql.EnumValueConfig{
Value: AppSecretKeySMSProviderSecrets,
},
},
})

var secretKeyToConfigKeyMap map[AppSecretKey]config.SecretKey = map[AppSecretKey]config.SecretKey{
AppSecretKeyOauthSSOProviderClientSecrets: config.OAuthSSOProviderCredentialsKey,
AppSecretKeyWebhookSecret: config.WebhookKeyMaterialsKey,
AppSecretKeyAdminAPISecrets: config.AdminAPIAuthKeyKey,
AppSecretKeySmtpSecret: config.SMTPServerCredentialsKey,
AppSecretKeyOauthClientSecrets: config.OAuthClientCredentialsKey,
AppSecretKeyBotProtectionProviderSecret: config.BotProtectionProviderCredentialsKey,
var secretKeyToConfigKeyMap map[AppSecretKey][]config.SecretKey = map[AppSecretKey][]config.SecretKey{
AppSecretKeyOauthSSOProviderClientSecrets: {config.OAuthSSOProviderCredentialsKey},
AppSecretKeyWebhookSecret: {config.WebhookKeyMaterialsKey},
AppSecretKeyAdminAPISecrets: {config.AdminAPIAuthKeyKey},
AppSecretKeySmtpSecret: {config.SMTPServerCredentialsKey},
AppSecretKeyOauthClientSecrets: {config.OAuthClientCredentialsKey},
AppSecretKeyBotProtectionProviderSecret: {config.BotProtectionProviderCredentialsKey},
AppSecretKeySMSProviderSecrets: {config.TwilioCredentialsKey, config.CustomSMSProviderConfigKey},
}

const typeApp = "App"
Expand Down
56 changes: 55 additions & 1 deletion pkg/portal/graphql/app_mutation.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,45 @@ var samlSpSigningSecretsSetDataInput = graphql.NewInputObject(graphql.InputObjec
},
})

var smsProviderSecretsSetDataInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "SMSProviderSecretsSetDataInput",
Fields: graphql.InputObjectConfigFieldMap{
"twilioCredentials": &graphql.InputObjectFieldConfig{
Type: smsProviderTwilioCredentialsInput,
},
"customSMSProviderCredentials": &graphql.InputObjectFieldConfig{
Type: customSmsProviderSecretsInput,
},
},
})

var smsProviderTwilioCredentialsInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "SMSProviderTwilioCredentialsInput",
Fields: graphql.InputObjectConfigFieldMap{
"accountSID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"authToken": &graphql.InputObjectFieldConfig{
Type: graphql.String,
},
"messagingServiceSID": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
},
})

var customSmsProviderSecretsInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "CustomSmsProviderSecretsInput",
Fields: graphql.InputObjectConfigFieldMap{
"url": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"timeout": &graphql.InputObjectFieldConfig{
Type: graphql.Int,
},
},
})

var smtpSecretUpdateInstructionsInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "SmtpSecretUpdateInstructionsInput",
Fields: graphql.InputObjectConfigFieldMap{
Expand Down Expand Up @@ -196,6 +235,18 @@ var samlSpSigningSecretsUpdateInstructionsInput = graphql.NewInputObject(graphql
},
})

var smsProviderSecretsUpdateInstructionsInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "SMSProviderSecretsUpdateInstructionsInput",
Fields: graphql.InputObjectConfigFieldMap{
"action": &graphql.InputObjectFieldConfig{
Type: graphql.NewNonNull(graphql.String),
},
"setData": &graphql.InputObjectFieldConfig{
Type: smsProviderSecretsSetDataInput,
},
},
})

var adminAPIAuthKeyUpdateInstructionInput = graphql.NewInputObject(graphql.InputObjectConfig{
Name: "AdminAPIAuthKeyUpdateInstructionInput",
Fields: graphql.InputObjectConfigFieldMap{
Expand Down Expand Up @@ -256,6 +307,9 @@ var secretConfigUpdateInstructionsInput = graphql.NewInputObject(graphql.InputOb
"samlSpSigningSecrets": &graphql.InputObjectFieldConfig{
Type: samlSpSigningSecretsUpdateInstructionsInput,
},
"smsProviderSecrets": &graphql.InputObjectFieldConfig{
Type: smsProviderSecretsUpdateInstructionsInput,
},
},
})

Expand Down Expand Up @@ -708,7 +762,7 @@ var _ = registerMutationField(
appSecretKey := s.(AppSecretKey)
appSecretKeys = append(appSecretKeys, string(appSecretKey))
configSecretKey := secretKeyToConfigKeyMap[appSecretKey]
secrets = append(secrets, configSecretKey)
secrets = append(secrets, configSecretKey...)
}

resolvedNodeID := relay.FromGlobalID(appNodeID)
Expand Down
Loading

0 comments on commit e0e0118

Please sign in to comment.