diff --git a/internal/start/start.go b/internal/start/start.go index 2483b6aed..9096f381d 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -583,44 +583,44 @@ EOF ) } - if utils.Config.Auth.Hook.MFAVerificationAttempt.Enabled { + if hook := utils.Config.Auth.Hook.MFAVerificationAttempt; hook != nil && hook.Enabled { env = append( env, "GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_ENABLED=true", - "GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI="+utils.Config.Auth.Hook.MFAVerificationAttempt.URI, - "GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_SECRETS="+utils.Config.Auth.Hook.MFAVerificationAttempt.Secrets, + "GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_URI="+hook.URI, + "GOTRUE_HOOK_MFA_VERIFICATION_ATTEMPT_SECRETS="+hook.Secrets, ) } - if utils.Config.Auth.Hook.PasswordVerificationAttempt.Enabled { + if hook := utils.Config.Auth.Hook.PasswordVerificationAttempt; hook != nil && hook.Enabled { env = append( env, "GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_ENABLED=true", - "GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI="+utils.Config.Auth.Hook.PasswordVerificationAttempt.URI, - "GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_SECRETS="+utils.Config.Auth.Hook.PasswordVerificationAttempt.Secrets, + "GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_URI="+hook.URI, + "GOTRUE_HOOK_PASSWORD_VERIFICATION_ATTEMPT_SECRETS="+hook.Secrets, ) } - if utils.Config.Auth.Hook.CustomAccessToken.Enabled { + if hook := utils.Config.Auth.Hook.CustomAccessToken; hook != nil && hook.Enabled { env = append( env, "GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_ENABLED=true", - "GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI="+utils.Config.Auth.Hook.CustomAccessToken.URI, - "GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS="+utils.Config.Auth.Hook.CustomAccessToken.Secrets, + "GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_URI="+hook.URI, + "GOTRUE_HOOK_CUSTOM_ACCESS_TOKEN_SECRETS="+hook.Secrets, ) } - if utils.Config.Auth.Hook.SendSMS.Enabled { + if hook := utils.Config.Auth.Hook.SendSMS; hook != nil && hook.Enabled { env = append( env, "GOTRUE_HOOK_SEND_SMS_ENABLED=true", - "GOTRUE_HOOK_SEND_SMS_URI="+utils.Config.Auth.Hook.SendSMS.URI, - "GOTRUE_HOOK_SEND_SMS_SECRETS="+utils.Config.Auth.Hook.SendSMS.Secrets, + "GOTRUE_HOOK_SEND_SMS_URI="+hook.URI, + "GOTRUE_HOOK_SEND_SMS_SECRETS="+hook.Secrets, ) } - if utils.Config.Auth.Hook.SendEmail.Enabled { + if hook := utils.Config.Auth.Hook.SendEmail; hook != nil && hook.Enabled { env = append( env, "GOTRUE_HOOK_SEND_EMAIL_ENABLED=true", - "GOTRUE_HOOK_SEND_EMAIL_URI="+utils.Config.Auth.Hook.SendEmail.URI, - "GOTRUE_HOOK_SEND_EMAIL_SECRETS="+utils.Config.Auth.Hook.SendEmail.Secrets, + "GOTRUE_HOOK_SEND_EMAIL_URI="+hook.URI, + "GOTRUE_HOOK_SEND_EMAIL_SECRETS="+hook.Secrets, ) } diff --git a/pkg/config/auth.go b/pkg/config/auth.go index 6c14cf649..b2e6b0fa6 100644 --- a/pkg/config/auth.go +++ b/pkg/config/auth.go @@ -145,11 +145,11 @@ type ( } hook struct { - MFAVerificationAttempt hookConfig `toml:"mfa_verification_attempt"` - PasswordVerificationAttempt hookConfig `toml:"password_verification_attempt"` - CustomAccessToken hookConfig `toml:"custom_access_token"` - SendSMS hookConfig `toml:"send_sms"` - SendEmail hookConfig `toml:"send_email"` + MFAVerificationAttempt *hookConfig `toml:"mfa_verification_attempt"` + PasswordVerificationAttempt *hookConfig `toml:"password_verification_attempt"` + CustomAccessToken *hookConfig `toml:"custom_access_token"` + SendSMS *hookConfig `toml:"send_sms"` + SendEmail *hookConfig `toml:"send_email"` } factorTypeConfiguration struct { @@ -261,80 +261,88 @@ func (a *auth) FromRemoteAuthConfig(remoteConfig v1API.AuthConfigResponse) { } func (h hook) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { - if body.HookCustomAccessTokenEnabled = &h.CustomAccessToken.Enabled; *body.HookCustomAccessTokenEnabled { - body.HookCustomAccessTokenUri = &h.CustomAccessToken.URI - if len(h.CustomAccessToken.Secrets) > 0 { - body.HookCustomAccessTokenSecrets = &h.CustomAccessToken.Secrets + // When local config is not set, we assume platform defaults should not change + if hook := h.CustomAccessToken; hook != nil { + if body.HookCustomAccessTokenEnabled = &hook.Enabled; hook.Enabled { + body.HookCustomAccessTokenUri = &hook.URI + if len(hook.Secrets) > 0 { + body.HookCustomAccessTokenSecrets = &hook.Secrets + } } } - if body.HookSendEmailEnabled = &h.SendEmail.Enabled; *body.HookSendEmailEnabled { - body.HookSendEmailUri = &h.SendEmail.URI - if len(h.SendEmail.Secrets) > 0 { - body.HookSendEmailSecrets = &h.SendEmail.Secrets + if hook := h.SendEmail; hook != nil { + if body.HookSendEmailEnabled = &hook.Enabled; hook.Enabled { + body.HookSendEmailUri = &hook.URI + if len(hook.Secrets) > 0 { + body.HookSendEmailSecrets = &hook.Secrets + } } } - if body.HookSendSmsEnabled = &h.SendSMS.Enabled; *body.HookSendSmsEnabled { - body.HookSendSmsUri = &h.SendSMS.URI - if len(h.SendSMS.Secrets) > 0 { - body.HookSendSmsSecrets = &h.SendSMS.Secrets + if hook := h.SendSMS; hook != nil { + if body.HookSendSmsEnabled = &hook.Enabled; hook.Enabled { + body.HookSendSmsUri = &hook.URI + if len(hook.Secrets) > 0 { + body.HookSendSmsSecrets = &hook.Secrets + } } } // Enterprise and team only features - if body.HookMfaVerificationAttemptEnabled = &h.MFAVerificationAttempt.Enabled; *body.HookMfaVerificationAttemptEnabled { - body.HookMfaVerificationAttemptUri = &h.MFAVerificationAttempt.URI - if len(h.MFAVerificationAttempt.Secrets) > 0 { - body.HookMfaVerificationAttemptSecrets = &h.MFAVerificationAttempt.Secrets + if hook := h.MFAVerificationAttempt; hook != nil { + if body.HookMfaVerificationAttemptEnabled = &hook.Enabled; hook.Enabled { + body.HookMfaVerificationAttemptUri = &hook.URI + if len(hook.Secrets) > 0 { + body.HookMfaVerificationAttemptSecrets = &hook.Secrets + } } } - if body.HookPasswordVerificationAttemptEnabled = &h.PasswordVerificationAttempt.Enabled; *body.HookPasswordVerificationAttemptEnabled { - body.HookPasswordVerificationAttemptUri = &h.PasswordVerificationAttempt.URI - if len(h.PasswordVerificationAttempt.Secrets) > 0 { - body.HookPasswordVerificationAttemptSecrets = &h.PasswordVerificationAttempt.Secrets + if hook := h.PasswordVerificationAttempt; hook != nil { + if body.HookPasswordVerificationAttemptEnabled = &hook.Enabled; hook.Enabled { + body.HookPasswordVerificationAttemptUri = &hook.URI + if len(hook.Secrets) > 0 { + body.HookPasswordVerificationAttemptSecrets = &hook.Secrets + } } } } func (h *hook) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) { - // Ignore disabled hooks because their envs are not loaded - if h.CustomAccessToken.Enabled { - h.CustomAccessToken.URI = cast.Val(remoteConfig.HookCustomAccessTokenUri, "") - if remoteConfig.HookCustomAccessTokenSecrets != nil { - h.CustomAccessToken.Secrets = hashPrefix + cast.Val(remoteConfig.HookCustomAccessTokenSecrets, "") + // When local config is not set, we assume platform defaults should not change + if hook := h.CustomAccessToken; hook != nil { + // Ignore disabled hooks because their envs are not loaded + if hook.Enabled { + hook.URI = cast.Val(remoteConfig.HookCustomAccessTokenUri, "") + hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookCustomAccessTokenSecrets, "") } + hook.Enabled = cast.Val(remoteConfig.HookCustomAccessTokenEnabled, false) } - h.CustomAccessToken.Enabled = cast.Val(remoteConfig.HookCustomAccessTokenEnabled, false) - - if h.SendEmail.Enabled { - h.SendEmail.URI = cast.Val(remoteConfig.HookSendEmailUri, "") - if remoteConfig.HookSendEmailSecrets != nil { - h.SendEmail.Secrets = hashPrefix + cast.Val(remoteConfig.HookSendEmailSecrets, "") + if hook := h.SendEmail; hook != nil { + if hook.Enabled { + hook.URI = cast.Val(remoteConfig.HookSendEmailUri, "") + hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookSendEmailSecrets, "") } + hook.Enabled = cast.Val(remoteConfig.HookSendEmailEnabled, false) } - h.SendEmail.Enabled = cast.Val(remoteConfig.HookSendEmailEnabled, false) - - if h.SendSMS.Enabled { - h.SendSMS.URI = cast.Val(remoteConfig.HookSendSmsUri, "") - if remoteConfig.HookSendSmsSecrets != nil { - h.SendSMS.Secrets = hashPrefix + cast.Val(remoteConfig.HookSendSmsSecrets, "") + if hook := h.SendSMS; hook != nil { + if hook.Enabled { + hook.URI = cast.Val(remoteConfig.HookSendSmsUri, "") + hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookSendSmsSecrets, "") } + hook.Enabled = cast.Val(remoteConfig.HookSendSmsEnabled, false) } - h.SendSMS.Enabled = cast.Val(remoteConfig.HookSendSmsEnabled, false) - // Enterprise and team only features - if h.MFAVerificationAttempt.Enabled { - h.MFAVerificationAttempt.URI = cast.Val(remoteConfig.HookMfaVerificationAttemptUri, "") - if remoteConfig.HookMfaVerificationAttemptSecrets != nil { - h.MFAVerificationAttempt.Secrets = hashPrefix + cast.Val(remoteConfig.HookMfaVerificationAttemptSecrets, "") + if hook := h.MFAVerificationAttempt; hook != nil { + if hook.Enabled { + hook.URI = cast.Val(remoteConfig.HookMfaVerificationAttemptUri, "") + hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookMfaVerificationAttemptSecrets, "") } + hook.Enabled = cast.Val(remoteConfig.HookMfaVerificationAttemptEnabled, false) } - h.MFAVerificationAttempt.Enabled = cast.Val(remoteConfig.HookMfaVerificationAttemptEnabled, false) - - if h.PasswordVerificationAttempt.Enabled { - h.PasswordVerificationAttempt.URI = cast.Val(remoteConfig.HookPasswordVerificationAttemptUri, "") - if remoteConfig.HookPasswordVerificationAttemptSecrets != nil { - h.PasswordVerificationAttempt.Secrets = hashPrefix + cast.Val(remoteConfig.HookPasswordVerificationAttemptSecrets, "") + if hook := h.PasswordVerificationAttempt; hook != nil { + if hook.Enabled { + hook.URI = cast.Val(remoteConfig.HookPasswordVerificationAttemptUri, "") + hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookPasswordVerificationAttemptSecrets, "") } + hook.Enabled = cast.Val(remoteConfig.HookPasswordVerificationAttemptEnabled, false) } - h.PasswordVerificationAttempt.Enabled = cast.Val(remoteConfig.HookPasswordVerificationAttemptEnabled, false) } func (m mfa) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) { @@ -931,9 +939,12 @@ const hashPrefix = "hash:" func (a *auth) HashSecrets(key string) { hash := func(v string) string { + if len(v) == 0 { + return hashPrefix + } return hashPrefix + sha256Hmac(key, v) } - if a.Email.Smtp != nil && len(a.Email.Smtp.Pass) > 0 { + if a.Email.Smtp != nil && a.Email.Smtp.IsEnabled() { a.Email.Smtp.Pass = hash(a.Email.Smtp.Pass) } // Only hash secrets for locally enabled providers because other envs won't be loaded @@ -949,19 +960,19 @@ func (a *auth) HashSecrets(key string) { case a.Sms.Vonage.Enabled: a.Sms.Vonage.ApiSecret = hash(a.Sms.Vonage.ApiSecret) } - if a.Hook.MFAVerificationAttempt.Enabled && len(a.Hook.MFAVerificationAttempt.Secrets) > 0 { + if a.Hook.MFAVerificationAttempt != nil && a.Hook.MFAVerificationAttempt.Enabled { a.Hook.MFAVerificationAttempt.Secrets = hash(a.Hook.MFAVerificationAttempt.Secrets) } - if a.Hook.PasswordVerificationAttempt.Enabled && len(a.Hook.PasswordVerificationAttempt.Secrets) > 0 { + if a.Hook.PasswordVerificationAttempt != nil && a.Hook.PasswordVerificationAttempt.Enabled { a.Hook.PasswordVerificationAttempt.Secrets = hash(a.Hook.PasswordVerificationAttempt.Secrets) } - if a.Hook.CustomAccessToken.Enabled && len(a.Hook.CustomAccessToken.Secrets) > 0 { + if a.Hook.CustomAccessToken != nil && a.Hook.CustomAccessToken.Enabled { a.Hook.CustomAccessToken.Secrets = hash(a.Hook.CustomAccessToken.Secrets) } - if a.Hook.SendSMS.Enabled && len(a.Hook.SendSMS.Secrets) > 0 { + if a.Hook.SendSMS != nil && a.Hook.SendSMS.Enabled { a.Hook.SendSMS.Secrets = hash(a.Hook.SendSMS.Secrets) } - if a.Hook.SendEmail.Enabled && len(a.Hook.SendEmail.Secrets) > 0 { + if a.Hook.SendEmail != nil && a.Hook.SendEmail.Enabled { a.Hook.SendEmail.Secrets = hash(a.Hook.SendEmail.Secrets) } for name, provider := range a.External { diff --git a/pkg/config/auth_test.go b/pkg/config/auth_test.go index 404f1cbb6..e659da166 100644 --- a/pkg/config/auth_test.go +++ b/pkg/config/auth_test.go @@ -121,27 +121,27 @@ func TestHookDiff(t *testing.T) { t.Run("local and remote enabled", func(t *testing.T) { c := newWithDefaults() c.Hook = hook{ - CustomAccessToken: hookConfig{ + CustomAccessToken: &hookConfig{ Enabled: true, URI: "http://example.com", Secrets: "test-secret", }, - SendSMS: hookConfig{ + SendSMS: &hookConfig{ Enabled: true, URI: "http://example.com", Secrets: "test-secret", }, - SendEmail: hookConfig{ + SendEmail: &hookConfig{ Enabled: true, URI: "https://example.com", Secrets: "test-secret", }, - MFAVerificationAttempt: hookConfig{ + MFAVerificationAttempt: &hookConfig{ Enabled: true, URI: "https://example.com", Secrets: "test-secret", }, - PasswordVerificationAttempt: hookConfig{ + PasswordVerificationAttempt: &hookConfig{ Enabled: true, URI: "pg-functions://verifyPassword", }, @@ -151,12 +151,12 @@ func TestHookDiff(t *testing.T) { HookCustomAccessTokenEnabled: cast.Ptr(true), HookCustomAccessTokenUri: cast.Ptr("http://example.com"), HookCustomAccessTokenSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), - HookSendEmailEnabled: cast.Ptr(true), - HookSendEmailUri: cast.Ptr("https://example.com"), - HookSendEmailSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), HookSendSmsEnabled: cast.Ptr(true), HookSendSmsUri: cast.Ptr("http://example.com"), HookSendSmsSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), + HookSendEmailEnabled: cast.Ptr(true), + HookSendEmailUri: cast.Ptr("https://example.com"), + HookSendEmailSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), HookMfaVerificationAttemptEnabled: cast.Ptr(true), HookMfaVerificationAttemptUri: cast.Ptr("https://example.com"), HookMfaVerificationAttemptSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), @@ -168,41 +168,38 @@ func TestHookDiff(t *testing.T) { assert.Empty(t, string(diff)) }) - t.Run("local enabled and disabled", func(t *testing.T) { + t.Run("local disabled remote enabled", func(t *testing.T) { c := newWithDefaults() c.Hook = hook{ - CustomAccessToken: hookConfig{ - Enabled: true, - URI: "http://example.com", - Secrets: "test-secret", + CustomAccessToken: &hookConfig{ + Enabled: false, }, - SendSMS: hookConfig{ + SendSMS: &hookConfig{ Enabled: false, URI: "https://example.com", Secrets: "test-secret", }, - SendEmail: hookConfig{ - Enabled: true, - URI: "pg-functions://sendEmail", + SendEmail: &hookConfig{ + Enabled: false, }, - MFAVerificationAttempt: hookConfig{ + MFAVerificationAttempt: &hookConfig{ Enabled: false, - URI: "pg-functions://verifyMFA", + URI: "pg-functions://postgres/public/verifyMFA", }, - PasswordVerificationAttempt: hookConfig{Enabled: false}, + PasswordVerificationAttempt: nil, } // Run test diff, err := c.DiffWithRemote("", v1API.AuthConfigResponse{ - HookCustomAccessTokenEnabled: cast.Ptr(false), - HookCustomAccessTokenUri: cast.Ptr(""), - HookCustomAccessTokenSecrets: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), - HookSendEmailEnabled: cast.Ptr(false), - HookSendEmailUri: cast.Ptr(""), + HookCustomAccessTokenEnabled: cast.Ptr(true), + HookCustomAccessTokenUri: cast.Ptr("http://example.com"), + HookCustomAccessTokenSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), HookSendSmsEnabled: cast.Ptr(true), - HookSendSmsUri: cast.Ptr("http://example.com"), + HookSendSmsUri: cast.Ptr("https://example.com"), HookSendSmsSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), + HookSendEmailEnabled: cast.Ptr(true), + HookSendEmailUri: cast.Ptr("pg-functions://postgres/public/sendEmail"), HookMfaVerificationAttemptEnabled: cast.Ptr(true), - HookMfaVerificationAttemptUri: cast.Ptr("pg-functions://verifyMFA"), + HookMfaVerificationAttemptUri: cast.Ptr("pg-functions://postgres/public/verifyMFA"), HookPasswordVerificationAttemptEnabled: cast.Ptr(true), HookPasswordVerificationAttemptUri: cast.Ptr("https://example.com"), HookPasswordVerificationAttemptSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), @@ -212,13 +209,62 @@ func TestHookDiff(t *testing.T) { assertSnapshotEqual(t, diff) }) - t.Run("local and remote disabled", func(t *testing.T) { + t.Run("local enabled remote disabled", func(t *testing.T) { c := newWithDefaults() + c.Hook = hook{ + CustomAccessToken: &hookConfig{ + Enabled: true, + URI: "http://example.com", + Secrets: "test-secret", + }, + SendSMS: &hookConfig{ + Enabled: true, + URI: "https://example.com", + Secrets: "test-secret", + }, + SendEmail: &hookConfig{ + Enabled: true, + URI: "pg-functions://postgres/public/sendEmail", + }, + MFAVerificationAttempt: &hookConfig{ + Enabled: true, + URI: "pg-functions://postgres/public/verifyMFA", + }, + PasswordVerificationAttempt: nil, + } // Run test diff, err := c.DiffWithRemote("", v1API.AuthConfigResponse{ HookCustomAccessTokenEnabled: cast.Ptr(false), + HookCustomAccessTokenUri: cast.Ptr("pg-functions://postgres/public/customToken"), + HookSendSmsEnabled: cast.Ptr(false), + HookSendSmsUri: cast.Ptr("https://example.com"), + HookSendSmsSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), HookSendEmailEnabled: cast.Ptr(false), + HookSendEmailUri: cast.Ptr("https://example.com"), + HookSendEmailSecrets: cast.Ptr("ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252"), + HookMfaVerificationAttemptEnabled: cast.Ptr(false), + HookMfaVerificationAttemptUri: cast.Ptr("pg-functions://postgres/public/verifyMFA"), + HookPasswordVerificationAttemptEnabled: cast.Ptr(false), + }) + // Check error + assert.NoError(t, err) + assertSnapshotEqual(t, diff) + }) + + t.Run("local and remote disabled", func(t *testing.T) { + c := newWithDefaults() + c.Hook = hook{ + CustomAccessToken: &hookConfig{Enabled: false}, + SendSMS: &hookConfig{Enabled: false}, + SendEmail: &hookConfig{Enabled: false}, + MFAVerificationAttempt: &hookConfig{Enabled: false}, + PasswordVerificationAttempt: &hookConfig{Enabled: false}, + } + // Run test + diff, err := c.DiffWithRemote("", v1API.AuthConfigResponse{ + HookCustomAccessTokenEnabled: cast.Ptr(false), HookSendSmsEnabled: cast.Ptr(false), + HookSendEmailEnabled: cast.Ptr(false), HookMfaVerificationAttemptEnabled: cast.Ptr(false), HookPasswordVerificationAttemptEnabled: cast.Ptr(false), }) @@ -719,7 +765,7 @@ func TestSmsDiff(t *testing.T) { diff, err := c.DiffWithRemote("", v1API.AuthConfigResponse{ ExternalPhoneEnabled: cast.Ptr(false), SmsProvider: cast.Ptr("messagebird"), - SmsMessagebirdAccessKey: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + SmsMessagebirdAccessKey: cast.Ptr(""), }) // Check error assert.NoError(t, err) @@ -756,67 +802,67 @@ func TestExternalDiff(t *testing.T) { ExternalAppleAdditionalClientIds: cast.Ptr(""), ExternalAppleClientId: cast.Ptr(""), ExternalAppleEnabled: cast.Ptr(true), - ExternalAppleSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalAppleSecret: cast.Ptr(""), ExternalAzureClientId: cast.Ptr(""), ExternalAzureEnabled: cast.Ptr(true), - ExternalAzureSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalAzureSecret: cast.Ptr(""), ExternalAzureUrl: cast.Ptr(""), ExternalBitbucketClientId: cast.Ptr(""), ExternalBitbucketEnabled: cast.Ptr(true), - ExternalBitbucketSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalBitbucketSecret: cast.Ptr(""), ExternalDiscordClientId: cast.Ptr(""), ExternalDiscordEnabled: cast.Ptr(true), - ExternalDiscordSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalDiscordSecret: cast.Ptr(""), ExternalFacebookClientId: cast.Ptr(""), ExternalFacebookEnabled: cast.Ptr(true), - ExternalFacebookSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalFacebookSecret: cast.Ptr(""), ExternalFigmaClientId: cast.Ptr(""), ExternalFigmaEnabled: cast.Ptr(true), - ExternalFigmaSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalFigmaSecret: cast.Ptr(""), ExternalGithubClientId: cast.Ptr(""), ExternalGithubEnabled: cast.Ptr(true), - ExternalGithubSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalGithubSecret: cast.Ptr(""), ExternalGitlabClientId: cast.Ptr(""), ExternalGitlabEnabled: cast.Ptr(true), - ExternalGitlabSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalGitlabSecret: cast.Ptr(""), ExternalGitlabUrl: cast.Ptr(""), ExternalGoogleAdditionalClientIds: cast.Ptr(""), ExternalGoogleClientId: cast.Ptr(""), ExternalGoogleEnabled: cast.Ptr(true), - ExternalGoogleSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalGoogleSecret: cast.Ptr(""), ExternalGoogleSkipNonceCheck: cast.Ptr(false), ExternalKakaoClientId: cast.Ptr(""), ExternalKakaoEnabled: cast.Ptr(true), - ExternalKakaoSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalKakaoSecret: cast.Ptr(""), ExternalKeycloakClientId: cast.Ptr(""), ExternalKeycloakEnabled: cast.Ptr(true), - ExternalKeycloakSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalKeycloakSecret: cast.Ptr(""), ExternalKeycloakUrl: cast.Ptr(""), ExternalLinkedinOidcClientId: cast.Ptr(""), ExternalLinkedinOidcEnabled: cast.Ptr(true), - ExternalLinkedinOidcSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalLinkedinOidcSecret: cast.Ptr(""), ExternalNotionClientId: cast.Ptr(""), ExternalNotionEnabled: cast.Ptr(true), - ExternalNotionSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalNotionSecret: cast.Ptr(""), ExternalSlackOidcClientId: cast.Ptr(""), ExternalSlackOidcEnabled: cast.Ptr(true), - ExternalSlackOidcSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalSlackOidcSecret: cast.Ptr(""), ExternalSpotifyClientId: cast.Ptr(""), ExternalSpotifyEnabled: cast.Ptr(true), - ExternalSpotifySecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalSpotifySecret: cast.Ptr(""), ExternalTwitchClientId: cast.Ptr(""), ExternalTwitchEnabled: cast.Ptr(true), - ExternalTwitchSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalTwitchSecret: cast.Ptr(""), ExternalTwitterClientId: cast.Ptr(""), ExternalTwitterEnabled: cast.Ptr(true), - ExternalTwitterSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalTwitterSecret: cast.Ptr(""), ExternalWorkosClientId: cast.Ptr(""), ExternalWorkosEnabled: cast.Ptr(true), - ExternalWorkosSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalWorkosSecret: cast.Ptr(""), ExternalWorkosUrl: cast.Ptr(""), ExternalZoomClientId: cast.Ptr(""), ExternalZoomEnabled: cast.Ptr(true), - ExternalZoomSecret: cast.Ptr("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"), + ExternalZoomSecret: cast.Ptr(""), // Deprecated fields should be ignored ExternalSlackClientId: cast.Ptr(""), ExternalSlackEnabled: cast.Ptr(true), diff --git a/pkg/config/config.go b/pkg/config/config.go index 6b8306ba5..0e3f00a84 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -950,21 +950,36 @@ func (e external) validate() (err error) { } func (h *hook) validate() error { - if err := h.MFAVerificationAttempt.validate("mfa_verification_attempt"); err != nil { - return err + if hook := h.MFAVerificationAttempt; hook != nil { + if err := hook.validate("mfa_verification_attempt"); err != nil { + return err + } } - if err := h.PasswordVerificationAttempt.validate("password_verification_attempt"); err != nil { - return err + if hook := h.PasswordVerificationAttempt; hook != nil { + if err := hook.validate("password_verification_attempt"); err != nil { + return err + } } - if err := h.CustomAccessToken.validate("custom_access_token"); err != nil { - return err + if hook := h.CustomAccessToken; hook != nil { + if err := hook.validate("custom_access_token"); err != nil { + return err + } } - if err := h.SendSMS.validate("send_sms"); err != nil { - return err + if hook := h.SendSMS; hook != nil { + if err := hook.validate("send_sms"); err != nil { + return err + } + } + if hook := h.SendEmail; hook != nil { + if err := h.SendEmail.validate("send_email"); err != nil { + return err + } } - return h.SendEmail.validate("send_email") + return nil } +var hookSecretPattern = regexp.MustCompile(`^v1,whsec_[A-Za-z0-9+/=]{32,88}$`) + func (h *hookConfig) validate(hookType string) (err error) { // If not enabled do nothing if !h.Enabled { @@ -984,12 +999,17 @@ func (h *hookConfig) validate(hookType string) (err error) { } else if h.Secrets, err = maybeLoadEnv(h.Secrets); err != nil { return err } + for _, secret := range strings.Split(h.Secrets, "|") { + if !hookSecretPattern.MatchString(secret) { + return errors.Errorf(`Invalid hook config: auth.hook.%s.secrets must be formatted as "v1,whsec_"`, hookType) + } + } case "pg-functions": if len(h.Secrets) > 0 { return errors.Errorf("Invalid hook config: auth.hook.%s.secrets is unsupported for pg-functions URI", hookType) } default: - return errors.Errorf("Invalid hook config: auth.hook.%v should be a HTTP, HTTPS, or pg-functions URI", hookType) + return errors.Errorf("Invalid hook config: auth.hook.%s.uri should be a HTTP, HTTPS, or pg-functions URI", hookType) } return nil } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 4aa7ce243..e1678f2ed 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -214,7 +214,7 @@ func TestValidateHookURI(t *testing.T) { hookConfig: hookConfig{ Enabled: true, URI: "http://example.com", - Secrets: "test-secret", + Secrets: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==", }, }, { @@ -222,7 +222,7 @@ func TestValidateHookURI(t *testing.T) { hookConfig: hookConfig{ Enabled: true, URI: "https://example.com", - Secrets: "test-secret", + Secrets: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==", }, }, { @@ -237,16 +237,16 @@ func TestValidateHookURI(t *testing.T) { hookConfig: hookConfig{ Enabled: true, URI: "ftp://example.com", - Secrets: "test-secret", + Secrets: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==", }, - errorMsg: "Invalid hook config: auth.hook.invalid URI with unsupported scheme should be a HTTP, HTTPS, or pg-functions URI", + errorMsg: "Invalid hook config: auth.hook.invalid URI with unsupported scheme.uri should be a HTTP, HTTPS, or pg-functions URI", }, { name: "invalid URI with parsing error", hookConfig: hookConfig{ Enabled: true, URI: "http://a b.com", - Secrets: "test-secret", + Secrets: "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==", }, errorMsg: "failed to parse template url: parse \"http://a b.com\": invalid character \" \" in host name", }, diff --git a/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff index 3db4d5462..b4022569c 100644 --- a/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff +++ b/pkg/config/testdata/TestAuthDiff/local_enabled_and_disabled.diff @@ -25,4 +25,4 @@ diff remote[auth] local[auth] +password_requirements = "lower_upper_letters_digits_symbols" [hook] - [hook.mfa_verification_attempt] + diff --git a/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff index 3308fd0a4..99a50071f 100644 --- a/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff +++ b/pkg/config/testdata/TestEmailDiff/local_disabled_remote_enabled.diff @@ -1,7 +1,7 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -51,13 +51,13 @@ +@@ -31,13 +31,13 @@ inactivity_timeout = "0s" [email] diff --git a/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff index d42967e05..85d30362a 100644 --- a/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff +++ b/pkg/config/testdata/TestEmailDiff/local_enabled_remote_disabled.diff @@ -1,7 +1,7 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -51,35 +51,43 @@ +@@ -31,35 +31,43 @@ inactivity_timeout = "0s" [email] diff --git a/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff index 37ab653dd..089d74eee 100644 --- a/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff +++ b/pkg/config/testdata/TestExternalDiff/local_enabled_and_disabled.diff @@ -1,7 +1,7 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -91,7 +91,7 @@ +@@ -71,7 +71,7 @@ [external] [external.apple] @@ -10,7 +10,7 @@ diff remote[auth] local[auth] client_id = "test-client-1,test-client-2" secret = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" url = "" -@@ -98,9 +98,9 @@ +@@ -78,9 +78,9 @@ redirect_uri = "" skip_nonce_check = false [external.azure] @@ -23,7 +23,7 @@ diff remote[auth] local[auth] url = "" redirect_uri = "" skip_nonce_check = false -@@ -147,7 +147,7 @@ +@@ -127,7 +127,7 @@ redirect_uri = "" skip_nonce_check = false [external.google] diff --git a/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff new file mode 100644 index 000000000..c8cf4c5f0 --- /dev/null +++ b/pkg/config/testdata/TestHookDiff/local_disabled_remote_enabled.diff @@ -0,0 +1,27 @@ +diff remote[auth] local[auth] +--- remote[auth] ++++ local[auth] +@@ -11,19 +11,19 @@ + + [hook] + [hook.mfa_verification_attempt] +-enabled = true ++enabled = false + uri = "pg-functions://postgres/public/verifyMFA" + secrets = "" + [hook.custom_access_token] +-enabled = true ++enabled = false + uri = "" + secrets = "" + [hook.send_sms] +-enabled = true ++enabled = false + uri = "https://example.com" + secrets = "test-secret" + [hook.send_email] +-enabled = true ++enabled = false + uri = "" + secrets = "" + diff --git a/pkg/config/testdata/TestHookDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestHookDiff/local_enabled_and_disabled.diff deleted file mode 100644 index e3afeb491..000000000 --- a/pkg/config/testdata/TestHookDiff/local_enabled_and_disabled.diff +++ /dev/null @@ -1,36 +0,0 @@ -diff remote[auth] local[auth] ---- remote[auth] -+++ local[auth] -@@ -11,24 +11,24 @@ - - [hook] - [hook.mfa_verification_attempt] --enabled = true -+enabled = false - uri = "pg-functions://verifyMFA" - secrets = "" - [hook.password_verification_attempt] --enabled = true -+enabled = false - uri = "" - secrets = "" - [hook.custom_access_token] --enabled = false --uri = "" --secrets = "hash:b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad" -+enabled = true -+uri = "http://example.com" -+secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" - [hook.send_sms] --enabled = true -+enabled = false - uri = "https://example.com" - secrets = "test-secret" - [hook.send_email] --enabled = false --uri = "" -+enabled = true -+uri = "pg-functions://sendEmail" - secrets = "" - - [mfa] diff --git a/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff new file mode 100644 index 000000000..e5f26740e --- /dev/null +++ b/pkg/config/testdata/TestHookDiff/local_enabled_remote_disabled.diff @@ -0,0 +1,33 @@ +diff remote[auth] local[auth] +--- remote[auth] ++++ local[auth] +@@ -11,21 +11,21 @@ + + [hook] + [hook.mfa_verification_attempt] +-enabled = false ++enabled = true + uri = "pg-functions://postgres/public/verifyMFA" + secrets = "hash:" + [hook.custom_access_token] +-enabled = false +-uri = "pg-functions://postgres/public/customToken" +-secrets = "hash:" ++enabled = true ++uri = "http://example.com" ++secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" + [hook.send_sms] +-enabled = false ++enabled = true + uri = "https://example.com" + secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" + [hook.send_email] +-enabled = false +-uri = "https://example.com" +-secrets = "hash:ce62bb9bcced294fd4afe668f8ab3b50a89cf433093c526fffa3d0e46bf55252" ++enabled = true ++uri = "pg-functions://postgres/public/sendEmail" ++secrets = "hash:" + + [mfa] + max_enrolled_factors = 0 diff --git a/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff b/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff index 866ce8fae..e5fa7c476 100644 --- a/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff +++ b/pkg/config/testdata/TestMfaDiff/local_enabled_and_disabled.diff @@ -1,8 +1,8 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -32,16 +32,16 @@ - secrets = "" +@@ -12,16 +12,16 @@ + [hook] [mfa] -max_enrolled_factors = 10 diff --git a/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff b/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff index 637a0206e..a7c312c91 100644 --- a/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff +++ b/pkg/config/testdata/TestSmsDiff/enable_sign_up_without_provider.diff @@ -1,7 +1,7 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -60,7 +60,7 @@ +@@ -40,7 +40,7 @@ otp_expiry = 0 [sms] diff --git a/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff b/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff index 4348c80ba..1dd938cd7 100644 --- a/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff +++ b/pkg/config/testdata/TestSmsDiff/local_disabled_remote_enabled.diff @@ -1,7 +1,7 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -60,12 +60,12 @@ +@@ -40,12 +40,12 @@ otp_expiry = 0 [sms] @@ -19,7 +19,7 @@ diff remote[auth] local[auth] account_sid = "" message_service_sid = "" auth_token = "" -@@ -88,8 +88,6 @@ +@@ -68,8 +68,6 @@ api_key = "" api_secret = "" [sms.test_otp] diff --git a/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff b/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff index e29c287ed..4418013bf 100644 --- a/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff +++ b/pkg/config/testdata/TestSmsDiff/local_enabled_remote_disabled.diff @@ -1,7 +1,7 @@ diff remote[auth] local[auth] --- remote[auth] +++ local[auth] -@@ -60,12 +60,12 @@ +@@ -40,12 +40,12 @@ otp_expiry = 0 [sms] @@ -19,7 +19,7 @@ diff remote[auth] local[auth] account_sid = "" message_service_sid = "" auth_token = "" -@@ -75,9 +75,9 @@ +@@ -55,9 +55,9 @@ message_service_sid = "" auth_token = "" [sms.messagebird] @@ -32,7 +32,7 @@ diff remote[auth] local[auth] [sms.textlocal] enabled = false sender = "" -@@ -88,6 +88,7 @@ +@@ -68,6 +68,7 @@ api_key = "" api_secret = "" [sms.test_otp]