• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / cli / 12595422178

03 Jan 2025 09:13AM UTC coverage: 59.673% (-0.06%) from 59.735%
12595422178

Pull #3012

github

web-flow
Merge 31ef259b7 into 8b8a876fd
Pull Request #3012: fix: ignore hash if local secret is unset

19 of 30 new or added lines in 1 file covered. (63.33%)

8 existing lines in 2 files now uncovered.

7665 of 12845 relevant lines covered (59.67%)

203.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

65.75
/pkg/config/auth.go
1
package config
2

3
import (
4
        "strconv"
5
        "strings"
6
        "time"
7

8
        v1API "github.com/supabase/cli/pkg/api"
9
        "github.com/supabase/cli/pkg/cast"
10
        "github.com/supabase/cli/pkg/diff"
11
)
12

13
type PasswordRequirements string
14

15
const (
16
        NoRequirements                 PasswordRequirements = ""
17
        LettersDigits                  PasswordRequirements = "letters_digits"
18
        LowerUpperLettersDigits        PasswordRequirements = "lower_upper_letters_digits"
19
        LowerUpperLettersDigitsSymbols PasswordRequirements = "lower_upper_letters_digits_symbols"
20
)
21

22
func (r PasswordRequirements) ToChar() v1API.UpdateAuthConfigBodyPasswordRequiredCharacters {
2✔
23
        switch r {
2✔
24
        case LettersDigits:
×
25
                return v1API.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
×
26
        case LowerUpperLettersDigits:
×
27
                return v1API.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891
×
28
        case LowerUpperLettersDigitsSymbols:
×
29
                return v1API.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892
×
30
        }
31
        return v1API.Empty
2✔
32
}
33

34
func NewPasswordRequirement(c v1API.UpdateAuthConfigBodyPasswordRequiredCharacters) PasswordRequirements {
26✔
35
        switch c {
26✔
36
        case v1API.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:
2✔
37
                return LettersDigits
2✔
38
        case v1API.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891:
×
39
                return LowerUpperLettersDigits
×
40
        case v1API.AbcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567892:
×
41
                return LowerUpperLettersDigitsSymbols
×
42
        }
43
        return NoRequirements
24✔
44
}
45

46
type (
47
        auth struct {
48
                Enabled bool   `toml:"enabled"`
49
                Image   string `toml:"-"`
50

51
                SiteUrl                    string               `toml:"site_url" mapstructure:"site_url"`
52
                AdditionalRedirectUrls     []string             `toml:"additional_redirect_urls"`
53
                JwtExpiry                  uint                 `toml:"jwt_expiry"`
54
                EnableRefreshTokenRotation bool                 `toml:"enable_refresh_token_rotation"`
55
                RefreshTokenReuseInterval  uint                 `toml:"refresh_token_reuse_interval"`
56
                EnableManualLinking        bool                 `toml:"enable_manual_linking"`
57
                EnableSignup               bool                 `toml:"enable_signup"`
58
                EnableAnonymousSignIns     bool                 `toml:"enable_anonymous_sign_ins"`
59
                MinimumPasswordLength      uint                 `toml:"minimum_password_length"`
60
                PasswordRequirements       PasswordRequirements `toml:"password_requirements"`
61

62
                Hook     hook     `toml:"hook"`
63
                MFA      mfa      `toml:"mfa"`
64
                Sessions sessions `toml:"sessions"`
65
                Email    email    `toml:"email"`
66
                Sms      sms      `toml:"sms"`
67
                External external `toml:"external"`
68

69
                // Custom secrets can be injected from .env file
70
                JwtSecret      string `toml:"-" mapstructure:"jwt_secret"`
71
                AnonKey        string `toml:"-" mapstructure:"anon_key"`
72
                ServiceRoleKey string `toml:"-" mapstructure:"service_role_key"`
73

74
                ThirdParty thirdParty `toml:"third_party"`
75
        }
76

77
        external map[string]provider
78

79
        thirdParty struct {
80
                Firebase tpaFirebase `toml:"firebase"`
81
                Auth0    tpaAuth0    `toml:"auth0"`
82
                Cognito  tpaCognito  `toml:"aws_cognito"`
83
        }
84

85
        tpaFirebase struct {
86
                Enabled bool `toml:"enabled"`
87

88
                ProjectID string `toml:"project_id"`
89
        }
90

91
        tpaAuth0 struct {
92
                Enabled bool `toml:"enabled"`
93

94
                Tenant       string `toml:"tenant"`
95
                TenantRegion string `toml:"tenant_region"`
96
        }
97

98
        tpaCognito struct {
99
                Enabled bool `toml:"enabled"`
100

101
                UserPoolID     string `toml:"user_pool_id"`
102
                UserPoolRegion string `toml:"user_pool_region"`
103
        }
104

105
        email struct {
106
                EnableSignup         bool                     `toml:"enable_signup"`
107
                DoubleConfirmChanges bool                     `toml:"double_confirm_changes"`
108
                EnableConfirmations  bool                     `toml:"enable_confirmations"`
109
                SecurePasswordChange bool                     `toml:"secure_password_change"`
110
                Template             map[string]emailTemplate `toml:"template"`
111
                Smtp                 *smtp                    `toml:"smtp"`
112
                MaxFrequency         time.Duration            `toml:"max_frequency"`
113
                OtpLength            uint                     `toml:"otp_length"`
114
                OtpExpiry            uint                     `toml:"otp_expiry"`
115
        }
116

117
        smtp struct {
118
                Enabled    *bool  `toml:"enabled"`
119
                Host       string `toml:"host"`
120
                Port       uint16 `toml:"port"`
121
                User       string `toml:"user"`
122
                Pass       string `toml:"pass"`
123
                AdminEmail string `toml:"admin_email"`
124
                SenderName string `toml:"sender_name"`
125
        }
126

127
        emailTemplate struct {
128
                Subject *string `toml:"subject"`
129
                Content *string `toml:"content"`
130
                // Only content path is accepted in config.toml
131
                ContentPath string `toml:"content_path"`
132
        }
133

134
        sms struct {
135
                EnableSignup        bool              `toml:"enable_signup"`
136
                EnableConfirmations bool              `toml:"enable_confirmations"`
137
                Template            string            `toml:"template"`
138
                Twilio              twilioConfig      `toml:"twilio" mapstructure:"twilio"`
139
                TwilioVerify        twilioConfig      `toml:"twilio_verify" mapstructure:"twilio_verify"`
140
                Messagebird         messagebirdConfig `toml:"messagebird" mapstructure:"messagebird"`
141
                Textlocal           textlocalConfig   `toml:"textlocal" mapstructure:"textlocal"`
142
                Vonage              vonageConfig      `toml:"vonage" mapstructure:"vonage"`
143
                TestOTP             map[string]string `toml:"test_otp"`
144
                MaxFrequency        time.Duration     `toml:"max_frequency"`
145
        }
146

147
        hook struct {
148
                MFAVerificationAttempt      *hookConfig `toml:"mfa_verification_attempt"`
149
                PasswordVerificationAttempt *hookConfig `toml:"password_verification_attempt"`
150
                CustomAccessToken           *hookConfig `toml:"custom_access_token"`
151
                SendSMS                     *hookConfig `toml:"send_sms"`
152
                SendEmail                   *hookConfig `toml:"send_email"`
153
        }
154

155
        factorTypeConfiguration struct {
156
                EnrollEnabled bool `toml:"enroll_enabled"`
157
                VerifyEnabled bool `toml:"verify_enabled"`
158
        }
159

160
        phoneFactorTypeConfiguration struct {
161
                factorTypeConfiguration
162
                OtpLength    uint          `toml:"otp_length"`
163
                Template     string        `toml:"template"`
164
                MaxFrequency time.Duration `toml:"max_frequency"`
165
        }
166

167
        mfa struct {
168
                TOTP               factorTypeConfiguration      `toml:"totp"`
169
                Phone              phoneFactorTypeConfiguration `toml:"phone"`
170
                WebAuthn           factorTypeConfiguration      `toml:"web_authn"`
171
                MaxEnrolledFactors uint                         `toml:"max_enrolled_factors"`
172
        }
173

174
        hookConfig struct {
175
                Enabled bool   `toml:"enabled"`
176
                URI     string `toml:"uri"`
177
                Secrets string `toml:"secrets"`
178
        }
179

180
        sessions struct {
181
                Timebox           time.Duration `toml:"timebox"`
182
                InactivityTimeout time.Duration `toml:"inactivity_timeout"`
183
        }
184

185
        twilioConfig struct {
186
                Enabled           bool   `toml:"enabled"`
187
                AccountSid        string `toml:"account_sid"`
188
                MessageServiceSid string `toml:"message_service_sid"`
189
                AuthToken         string `toml:"auth_token" mapstructure:"auth_token"`
190
        }
191

192
        messagebirdConfig struct {
193
                Enabled    bool   `toml:"enabled"`
194
                Originator string `toml:"originator"`
195
                AccessKey  string `toml:"access_key" mapstructure:"access_key"`
196
        }
197

198
        textlocalConfig struct {
199
                Enabled bool   `toml:"enabled"`
200
                Sender  string `toml:"sender"`
201
                ApiKey  string `toml:"api_key" mapstructure:"api_key"`
202
        }
203

204
        vonageConfig struct {
205
                Enabled   bool   `toml:"enabled"`
206
                From      string `toml:"from"`
207
                ApiKey    string `toml:"api_key" mapstructure:"api_key"`
208
                ApiSecret string `toml:"api_secret" mapstructure:"api_secret"`
209
        }
210

211
        provider struct {
212
                Enabled        bool   `toml:"enabled"`
213
                ClientId       string `toml:"client_id"`
214
                Secret         string `toml:"secret"`
215
                Url            string `toml:"url"`
216
                RedirectUri    string `toml:"redirect_uri"`
217
                SkipNonceCheck bool   `toml:"skip_nonce_check"`
218
        }
219
)
220

221
func (a *auth) ToUpdateAuthConfigBody() v1API.UpdateAuthConfigBody {
2✔
222
        body := v1API.UpdateAuthConfigBody{
2✔
223
                SiteUrl:                           &a.SiteUrl,
2✔
224
                UriAllowList:                      cast.Ptr(strings.Join(a.AdditionalRedirectUrls, ",")),
2✔
225
                JwtExp:                            cast.UintToIntPtr(&a.JwtExpiry),
2✔
226
                RefreshTokenRotationEnabled:       &a.EnableRefreshTokenRotation,
2✔
227
                SecurityRefreshTokenReuseInterval: cast.UintToIntPtr(&a.RefreshTokenReuseInterval),
2✔
228
                SecurityManualLinkingEnabled:      &a.EnableManualLinking,
2✔
229
                DisableSignup:                     cast.Ptr(!a.EnableSignup),
2✔
230
                ExternalAnonymousUsersEnabled:     &a.EnableAnonymousSignIns,
2✔
231
                PasswordMinLength:                 cast.UintToIntPtr(&a.MinimumPasswordLength),
2✔
232
                PasswordRequiredCharacters:        cast.Ptr(a.PasswordRequirements.ToChar()),
2✔
233
        }
2✔
234
        a.Hook.toAuthConfigBody(&body)
2✔
235
        a.MFA.toAuthConfigBody(&body)
2✔
236
        a.Sessions.toAuthConfigBody(&body)
2✔
237
        a.Email.toAuthConfigBody(&body)
2✔
238
        a.Sms.toAuthConfigBody(&body)
2✔
239
        a.External.toAuthConfigBody(&body)
2✔
240
        return body
2✔
241
}
2✔
242

243
func (a *auth) FromRemoteAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
244
        a.SiteUrl = cast.Val(remoteConfig.SiteUrl, "")
26✔
245
        a.AdditionalRedirectUrls = strToArr(cast.Val(remoteConfig.UriAllowList, ""))
26✔
246
        a.JwtExpiry = cast.IntToUint(cast.Val(remoteConfig.JwtExp, 0))
26✔
247
        a.EnableRefreshTokenRotation = cast.Val(remoteConfig.RefreshTokenRotationEnabled, false)
26✔
248
        a.RefreshTokenReuseInterval = cast.IntToUint(cast.Val(remoteConfig.SecurityRefreshTokenReuseInterval, 0))
26✔
249
        a.EnableManualLinking = cast.Val(remoteConfig.SecurityManualLinkingEnabled, false)
26✔
250
        a.EnableSignup = !cast.Val(remoteConfig.DisableSignup, false)
26✔
251
        a.EnableAnonymousSignIns = cast.Val(remoteConfig.ExternalAnonymousUsersEnabled, false)
26✔
252
        a.MinimumPasswordLength = cast.IntToUint(cast.Val(remoteConfig.PasswordMinLength, 0))
26✔
253
        prc := cast.Val(remoteConfig.PasswordRequiredCharacters, "")
26✔
254
        a.PasswordRequirements = NewPasswordRequirement(v1API.UpdateAuthConfigBodyPasswordRequiredCharacters(prc))
26✔
255
        a.Hook.fromAuthConfig(remoteConfig)
26✔
256
        a.MFA.fromAuthConfig(remoteConfig)
26✔
257
        a.Sessions.fromAuthConfig(remoteConfig)
26✔
258
        a.Email.fromAuthConfig(remoteConfig)
26✔
259
        a.Sms.fromAuthConfig(remoteConfig)
26✔
260
        a.External.fromAuthConfig(remoteConfig)
26✔
261
}
26✔
262

263
func (h hook) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
2✔
264
        // When local config is not set, we assume platform defaults should not change
2✔
265
        if hook := h.CustomAccessToken; hook != nil {
2✔
266
                if body.HookCustomAccessTokenEnabled = &hook.Enabled; hook.Enabled {
×
267
                        body.HookCustomAccessTokenUri = &hook.URI
×
268
                        if len(hook.Secrets) > 0 {
×
269
                                body.HookCustomAccessTokenSecrets = &hook.Secrets
×
270
                        }
×
271
                }
272
        }
273
        if hook := h.SendEmail; hook != nil {
2✔
274
                if body.HookSendEmailEnabled = &hook.Enabled; hook.Enabled {
×
275
                        body.HookSendEmailUri = &hook.URI
×
276
                        if len(hook.Secrets) > 0 {
×
277
                                body.HookSendEmailSecrets = &hook.Secrets
×
278
                        }
×
279
                }
280
        }
281
        if hook := h.SendSMS; hook != nil {
2✔
282
                if body.HookSendSmsEnabled = &hook.Enabled; hook.Enabled {
×
283
                        body.HookSendSmsUri = &hook.URI
×
284
                        if len(hook.Secrets) > 0 {
×
285
                                body.HookSendSmsSecrets = &hook.Secrets
×
286
                        }
×
287
                }
288
        }
289
        // Enterprise and team only features
290
        if hook := h.MFAVerificationAttempt; hook != nil {
2✔
291
                if body.HookMfaVerificationAttemptEnabled = &hook.Enabled; hook.Enabled {
×
292
                        body.HookMfaVerificationAttemptUri = &hook.URI
×
293
                        if len(hook.Secrets) > 0 {
×
294
                                body.HookMfaVerificationAttemptSecrets = &hook.Secrets
×
295
                        }
×
296
                }
297
        }
298
        if hook := h.PasswordVerificationAttempt; hook != nil {
2✔
299
                if body.HookPasswordVerificationAttemptEnabled = &hook.Enabled; hook.Enabled {
×
300
                        body.HookPasswordVerificationAttemptUri = &hook.URI
×
301
                        if len(hook.Secrets) > 0 {
×
302
                                body.HookPasswordVerificationAttemptSecrets = &hook.Secrets
×
303
                        }
×
304
                }
305
        }
306
}
307
func (h *hook) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
308
        // When local config is not set, we assume platform defaults should not change
26✔
309
        if hook := h.CustomAccessToken; hook != nil {
30✔
310
                // Ignore disabled hooks because their envs are not loaded
4✔
311
                if hook.Enabled {
6✔
312
                        hook.URI = cast.Val(remoteConfig.HookCustomAccessTokenUri, "")
2✔
313
                        if hook.Secrets != hashPrefix {
4✔
314
                                hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookCustomAccessTokenSecrets, "")
2✔
315
                        }
2✔
316
                }
317
                hook.Enabled = cast.Val(remoteConfig.HookCustomAccessTokenEnabled, false)
4✔
318
        }
319
        if hook := h.SendEmail; hook != nil {
30✔
320
                if hook.Enabled {
6✔
321
                        hook.URI = cast.Val(remoteConfig.HookSendEmailUri, "")
2✔
322
                        if hook.Secrets != hashPrefix {
3✔
323
                                hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookSendEmailSecrets, "")
1✔
324
                        }
1✔
325
                }
326
                hook.Enabled = cast.Val(remoteConfig.HookSendEmailEnabled, false)
4✔
327
        }
328
        if hook := h.SendSMS; hook != nil {
30✔
329
                if hook.Enabled {
6✔
330
                        hook.URI = cast.Val(remoteConfig.HookSendSmsUri, "")
2✔
331
                        if hook.Secrets != hashPrefix {
4✔
332
                                hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookSendSmsSecrets, "")
2✔
333
                        }
2✔
334
                }
335
                hook.Enabled = cast.Val(remoteConfig.HookSendSmsEnabled, false)
4✔
336
        }
337
        // Enterprise and team only features
338
        if hook := h.MFAVerificationAttempt; hook != nil {
30✔
339
                if hook.Enabled {
6✔
340
                        hook.URI = cast.Val(remoteConfig.HookMfaVerificationAttemptUri, "")
2✔
341
                        if hook.Secrets != hashPrefix {
3✔
342
                                hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookMfaVerificationAttemptSecrets, "")
1✔
343
                        }
1✔
344
                }
345
                hook.Enabled = cast.Val(remoteConfig.HookMfaVerificationAttemptEnabled, false)
4✔
346
        }
347
        if hook := h.PasswordVerificationAttempt; hook != nil {
28✔
348
                if hook.Enabled {
3✔
349
                        hook.URI = cast.Val(remoteConfig.HookPasswordVerificationAttemptUri, "")
1✔
350
                        if hook.Secrets != hashPrefix {
1✔
NEW
351
                                hook.Secrets = hashPrefix + cast.Val(remoteConfig.HookPasswordVerificationAttemptSecrets, "")
×
NEW
352
                        }
×
353
                }
354
                hook.Enabled = cast.Val(remoteConfig.HookPasswordVerificationAttemptEnabled, false)
2✔
355
        }
356
}
357

358
func (m mfa) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
2✔
359
        body.MfaMaxEnrolledFactors = cast.UintToIntPtr(&m.MaxEnrolledFactors)
2✔
360
        body.MfaTotpEnrollEnabled = &m.TOTP.EnrollEnabled
2✔
361
        body.MfaTotpVerifyEnabled = &m.TOTP.VerifyEnabled
2✔
362
        body.MfaPhoneEnrollEnabled = &m.Phone.EnrollEnabled
2✔
363
        body.MfaPhoneVerifyEnabled = &m.Phone.VerifyEnabled
2✔
364
        body.MfaPhoneOtpLength = cast.UintToIntPtr(&m.Phone.OtpLength)
2✔
365
        body.MfaPhoneTemplate = &m.Phone.Template
2✔
366
        body.MfaPhoneMaxFrequency = cast.Ptr(int(m.Phone.MaxFrequency.Seconds()))
2✔
367
        body.MfaWebAuthnEnrollEnabled = &m.WebAuthn.EnrollEnabled
2✔
368
        body.MfaWebAuthnVerifyEnabled = &m.WebAuthn.VerifyEnabled
2✔
369
}
2✔
370

371
func (m *mfa) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
372
        m.MaxEnrolledFactors = cast.IntToUint(cast.Val(remoteConfig.MfaMaxEnrolledFactors, 0))
26✔
373
        m.TOTP.EnrollEnabled = cast.Val(remoteConfig.MfaTotpEnrollEnabled, false)
26✔
374
        m.TOTP.VerifyEnabled = cast.Val(remoteConfig.MfaTotpVerifyEnabled, false)
26✔
375
        m.Phone.EnrollEnabled = cast.Val(remoteConfig.MfaPhoneEnrollEnabled, false)
26✔
376
        m.Phone.VerifyEnabled = cast.Val(remoteConfig.MfaPhoneVerifyEnabled, false)
26✔
377
        m.Phone.OtpLength = cast.IntToUint(remoteConfig.MfaPhoneOtpLength)
26✔
378
        m.Phone.Template = cast.Val(remoteConfig.MfaPhoneTemplate, "")
26✔
379
        m.Phone.MaxFrequency = time.Duration(cast.Val(remoteConfig.MfaPhoneMaxFrequency, 0)) * time.Second
26✔
380
        m.WebAuthn.EnrollEnabled = cast.Val(remoteConfig.MfaWebAuthnEnrollEnabled, false)
26✔
381
        m.WebAuthn.VerifyEnabled = cast.Val(remoteConfig.MfaWebAuthnVerifyEnabled, false)
26✔
382
}
26✔
383

384
func (s sessions) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
2✔
385
        body.SessionsTimebox = cast.Ptr(int(s.Timebox.Seconds()))
2✔
386
        body.SessionsInactivityTimeout = cast.Ptr(int(s.InactivityTimeout.Seconds()))
2✔
387
}
2✔
388

389
func (s *sessions) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
390
        s.Timebox = time.Duration(cast.Val(remoteConfig.SessionsTimebox, 0)) * time.Second
26✔
391
        s.InactivityTimeout = time.Duration(cast.Val(remoteConfig.SessionsInactivityTimeout, 0)) * time.Second
26✔
392
}
26✔
393

394
func (e email) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
2✔
395
        body.ExternalEmailEnabled = &e.EnableSignup
2✔
396
        body.MailerSecureEmailChangeEnabled = &e.DoubleConfirmChanges
2✔
397
        body.MailerAutoconfirm = cast.Ptr(!e.EnableConfirmations)
2✔
398
        body.MailerOtpLength = cast.UintToIntPtr(&e.OtpLength)
2✔
399
        body.MailerOtpExp = cast.UintToIntPtr(&e.OtpExpiry)
2✔
400
        body.SecurityUpdatePasswordRequireReauthentication = &e.SecurePasswordChange
2✔
401
        body.SmtpMaxFrequency = cast.Ptr(int(e.MaxFrequency.Seconds()))
2✔
402
        // When local config is not set, we assume platform defaults should not change
2✔
403
        if e.Smtp != nil {
2✔
404
                e.Smtp.toAuthConfigBody(body)
×
405
        }
×
406
        if len(e.Template) == 0 {
4✔
407
                return
2✔
408
        }
2✔
409
        var tmpl *emailTemplate
×
410
        tmpl = cast.Ptr(e.Template["invite"])
×
411
        body.MailerSubjectsInvite = tmpl.Subject
×
412
        body.MailerTemplatesInviteContent = tmpl.Content
×
413
        tmpl = cast.Ptr(e.Template["confirmation"])
×
414
        body.MailerSubjectsConfirmation = tmpl.Subject
×
415
        body.MailerTemplatesConfirmationContent = tmpl.Content
×
416
        tmpl = cast.Ptr(e.Template["recovery"])
×
417
        body.MailerSubjectsRecovery = tmpl.Subject
×
418
        body.MailerTemplatesRecoveryContent = tmpl.Content
×
419
        tmpl = cast.Ptr(e.Template["magic_link"])
×
420
        body.MailerSubjectsMagicLink = tmpl.Subject
×
421
        body.MailerTemplatesMagicLinkContent = tmpl.Content
×
422
        tmpl = cast.Ptr(e.Template["email_change"])
×
423
        body.MailerSubjectsEmailChange = tmpl.Subject
×
424
        body.MailerTemplatesEmailChangeContent = tmpl.Content
×
425
        tmpl = cast.Ptr(e.Template["reauthentication"])
×
426
        body.MailerSubjectsReauthentication = tmpl.Subject
×
427
        body.MailerTemplatesReauthenticationContent = tmpl.Content
×
428
}
429

430
func (e *email) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
431
        e.EnableSignup = cast.Val(remoteConfig.ExternalEmailEnabled, false)
26✔
432
        e.DoubleConfirmChanges = cast.Val(remoteConfig.MailerSecureEmailChangeEnabled, false)
26✔
433
        e.EnableConfirmations = !cast.Val(remoteConfig.MailerAutoconfirm, false)
26✔
434
        e.OtpLength = cast.IntToUint(cast.Val(remoteConfig.MailerOtpLength, 0))
26✔
435
        e.OtpExpiry = cast.IntToUint(remoteConfig.MailerOtpExp)
26✔
436
        e.SecurePasswordChange = cast.Val(remoteConfig.SecurityUpdatePasswordRequireReauthentication, false)
26✔
437
        e.MaxFrequency = time.Duration(cast.Val(remoteConfig.SmtpMaxFrequency, 0)) * time.Second
26✔
438
        // When local config is not set, we assume platform defaults should not change
26✔
439
        if e.Smtp != nil {
29✔
440
                e.Smtp.fromAuthConfig(remoteConfig)
3✔
441
        }
3✔
442
        if len(e.Template) == 0 {
48✔
443
                return
22✔
444
        }
22✔
445
        var tmpl emailTemplate
4✔
446
        tmpl = e.Template["invite"]
4✔
447
        if tmpl.Subject != nil {
6✔
448
                tmpl.Subject = remoteConfig.MailerSubjectsInvite
2✔
449
        }
2✔
450
        if tmpl.Content != nil {
6✔
451
                tmpl.Content = remoteConfig.MailerTemplatesInviteContent
2✔
452
        }
2✔
453
        e.Template["invite"] = tmpl
4✔
454

4✔
455
        tmpl = e.Template["confirmation"]
4✔
456
        if tmpl.Subject != nil {
6✔
457
                tmpl.Subject = remoteConfig.MailerSubjectsConfirmation
2✔
458
        }
2✔
459
        if tmpl.Content != nil {
5✔
460
                tmpl.Content = remoteConfig.MailerTemplatesConfirmationContent
1✔
461
        }
1✔
462
        e.Template["confirmation"] = tmpl
4✔
463

4✔
464
        tmpl = e.Template["recovery"]
4✔
465
        if tmpl.Subject != nil {
5✔
466
                tmpl.Subject = remoteConfig.MailerSubjectsRecovery
1✔
467
        }
1✔
468
        if tmpl.Content != nil {
6✔
469
                tmpl.Content = remoteConfig.MailerTemplatesRecoveryContent
2✔
470
        }
2✔
471
        e.Template["recovery"] = tmpl
4✔
472

4✔
473
        tmpl = e.Template["magic_link"]
4✔
474
        if tmpl.Subject != nil {
6✔
475
                tmpl.Subject = remoteConfig.MailerSubjectsMagicLink
2✔
476
        }
2✔
477
        if tmpl.Content != nil {
6✔
478
                tmpl.Content = remoteConfig.MailerTemplatesMagicLinkContent
2✔
479
        }
2✔
480
        e.Template["magic_link"] = tmpl
4✔
481

4✔
482
        tmpl = e.Template["email_change"]
4✔
483
        if tmpl.Subject != nil {
6✔
484
                tmpl.Subject = remoteConfig.MailerSubjectsEmailChange
2✔
485
        }
2✔
486
        if tmpl.Content != nil {
6✔
487
                tmpl.Content = remoteConfig.MailerTemplatesEmailChangeContent
2✔
488
        }
2✔
489
        e.Template["email_change"] = tmpl
4✔
490

4✔
491
        tmpl = e.Template["reauthentication"]
4✔
492
        if tmpl.Subject != nil {
6✔
493
                tmpl.Subject = remoteConfig.MailerSubjectsReauthentication
2✔
494
        }
2✔
495
        if tmpl.Content != nil {
6✔
496
                tmpl.Content = remoteConfig.MailerTemplatesReauthenticationContent
2✔
497
        }
2✔
498
        e.Template["reauthentication"] = tmpl
4✔
499
}
500

501
func (s smtp) IsEnabled() bool {
12✔
502
        // If Enabled is not defined, or defined and set to true
12✔
503
        return cast.Val(s.Enabled, true)
12✔
504
}
12✔
505

506
func (s smtp) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
×
507
        if !s.IsEnabled() {
×
508
                // Setting a single empty string disables SMTP
×
509
                body.SmtpHost = cast.Ptr("")
×
510
                return
×
511
        }
×
512
        body.SmtpHost = &s.Host
×
513
        body.SmtpPort = cast.Ptr(strconv.Itoa(int(s.Port)))
×
514
        body.SmtpUser = &s.User
×
515
        body.SmtpPass = &s.Pass
×
516
        body.SmtpAdminEmail = &s.AdminEmail
×
517
        body.SmtpSenderName = &s.SenderName
×
518
}
519

520
func (s *smtp) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
3✔
521
        showDiff := s.IsEnabled()
3✔
522
        // Api resets all values when SMTP is disabled
3✔
523
        if enabled := remoteConfig.SmtpHost != nil; s.Enabled != nil {
5✔
524
                *s.Enabled = enabled
2✔
525
        }
2✔
526
        if !showDiff {
4✔
527
                return
1✔
528
        }
1✔
529
        s.Host = cast.Val(remoteConfig.SmtpHost, "")
2✔
530
        s.User = cast.Val(remoteConfig.SmtpUser, "")
2✔
531
        s.Pass = hashPrefix + cast.Val(remoteConfig.SmtpPass, "")
2✔
532
        s.AdminEmail = cast.Val(remoteConfig.SmtpAdminEmail, "")
2✔
533
        s.SenderName = cast.Val(remoteConfig.SmtpSenderName, "")
2✔
534
        portStr := cast.Val(remoteConfig.SmtpPort, "0")
2✔
535
        if port, err := strconv.ParseUint(portStr, 10, 16); err == nil {
4✔
536
                s.Port = uint16(port)
2✔
537
        }
2✔
538
}
539

540
func (s sms) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
2✔
541
        body.ExternalPhoneEnabled = &s.EnableSignup
2✔
542
        body.SmsMaxFrequency = cast.Ptr(int(s.MaxFrequency.Seconds()))
2✔
543
        body.SmsAutoconfirm = &s.EnableConfirmations
2✔
544
        body.SmsTemplate = &s.Template
2✔
545
        if otpString := mapToEnv(s.TestOTP); len(otpString) > 0 {
2✔
546
                body.SmsTestOtp = &otpString
×
547
                // Set a 10 year validity for test OTP
×
548
                timestamp := time.Now().UTC().AddDate(10, 0, 0).Format(time.RFC3339)
×
549
                body.SmsTestOtpValidUntil = &timestamp
×
550
        }
×
551
        // Api only overrides configs of enabled providers
552
        switch {
2✔
553
        case s.Twilio.Enabled:
×
554
                body.SmsProvider = cast.Ptr("twilio")
×
555
                if len(s.Twilio.AuthToken) > 0 {
×
556
                        body.SmsTwilioAuthToken = &s.Twilio.AuthToken
×
557
                }
×
558
                body.SmsTwilioAccountSid = &s.Twilio.AccountSid
×
559
                body.SmsTwilioMessageServiceSid = &s.Twilio.MessageServiceSid
×
560
        case s.TwilioVerify.Enabled:
×
561
                body.SmsProvider = cast.Ptr("twilio_verify")
×
562
                if len(s.TwilioVerify.AuthToken) > 0 {
×
563
                        body.SmsTwilioVerifyAuthToken = &s.TwilioVerify.AuthToken
×
564
                }
×
565
                body.SmsTwilioVerifyAccountSid = &s.TwilioVerify.AccountSid
×
566
                body.SmsTwilioVerifyMessageServiceSid = &s.TwilioVerify.MessageServiceSid
×
567
        case s.Messagebird.Enabled:
×
568
                body.SmsProvider = cast.Ptr("messagebird")
×
569
                if len(s.Messagebird.AccessKey) > 0 {
×
570
                        body.SmsMessagebirdAccessKey = &s.Messagebird.AccessKey
×
571
                }
×
572
                body.SmsMessagebirdOriginator = &s.Messagebird.Originator
×
573
        case s.Textlocal.Enabled:
×
574
                body.SmsProvider = cast.Ptr("textlocal")
×
575
                if len(s.Textlocal.ApiKey) > 0 {
×
576
                        body.SmsTextlocalApiKey = &s.Textlocal.ApiKey
×
577
                }
×
578
                body.SmsTextlocalSender = &s.Textlocal.Sender
×
579
        case s.Vonage.Enabled:
×
580
                body.SmsProvider = cast.Ptr("vonage")
×
581
                if len(s.Vonage.ApiSecret) > 0 {
×
582
                        body.SmsVonageApiSecret = &s.Vonage.ApiSecret
×
583
                }
×
584
                body.SmsVonageApiKey = &s.Vonage.ApiKey
×
585
                body.SmsVonageFrom = &s.Vonage.From
×
586
        }
587
}
588

589
func (s *sms) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
590
        s.EnableSignup = cast.Val(remoteConfig.ExternalPhoneEnabled, false)
26✔
591
        s.MaxFrequency = time.Duration(cast.Val(remoteConfig.SmsMaxFrequency, 0)) * time.Second
26✔
592
        s.EnableConfirmations = cast.Val(remoteConfig.SmsAutoconfirm, false)
26✔
593
        s.Template = cast.Val(remoteConfig.SmsTemplate, "")
26✔
594
        s.TestOTP = envToMap(cast.Val(remoteConfig.SmsTestOtp, ""))
26✔
595
        // We are only interested in the provider that's enabled locally
26✔
596
        switch {
26✔
597
        case s.Twilio.Enabled:
1✔
598
                if s.Twilio.AuthToken != hashPrefix {
2✔
599
                        s.Twilio.AuthToken = hashPrefix + cast.Val(remoteConfig.SmsTwilioAuthToken, "")
1✔
600
                }
1✔
601
                s.Twilio.AccountSid = cast.Val(remoteConfig.SmsTwilioAccountSid, "")
1✔
602
                s.Twilio.MessageServiceSid = cast.Val(remoteConfig.SmsTwilioMessageServiceSid, "")
1✔
603
        case s.TwilioVerify.Enabled:
×
NEW
604
                if s.TwilioVerify.AuthToken != hashPrefix {
×
NEW
605
                        s.TwilioVerify.AuthToken = hashPrefix + cast.Val(remoteConfig.SmsTwilioVerifyAuthToken, "")
×
NEW
606
                }
×
607
                s.TwilioVerify.AccountSid = cast.Val(remoteConfig.SmsTwilioVerifyAccountSid, "")
×
608
                s.TwilioVerify.MessageServiceSid = cast.Val(remoteConfig.SmsTwilioVerifyMessageServiceSid, "")
×
609
        case s.Messagebird.Enabled:
2✔
610
                if s.Messagebird.AccessKey != hashPrefix {
3✔
611
                        s.Messagebird.AccessKey = hashPrefix + cast.Val(remoteConfig.SmsMessagebirdAccessKey, "")
1✔
612
                }
1✔
613
                s.Messagebird.Originator = cast.Val(remoteConfig.SmsMessagebirdOriginator, "")
2✔
614
        case s.Textlocal.Enabled:
×
NEW
615
                if s.Textlocal.ApiKey != hashPrefix {
×
NEW
616
                        s.Textlocal.ApiKey = hashPrefix + cast.Val(remoteConfig.SmsTextlocalApiKey, "")
×
NEW
617
                }
×
618
                s.Textlocal.Sender = cast.Val(remoteConfig.SmsTextlocalSender, "")
×
619
        case s.Vonage.Enabled:
×
NEW
620
                if s.Vonage.ApiSecret != hashPrefix {
×
NEW
621
                        s.Vonage.ApiSecret = hashPrefix + cast.Val(remoteConfig.SmsVonageApiSecret, "")
×
NEW
622
                }
×
623
                s.Vonage.ApiKey = cast.Val(remoteConfig.SmsVonageApiKey, "")
×
624
                s.Vonage.From = cast.Val(remoteConfig.SmsVonageFrom, "")
×
625
        case !s.EnableSignup:
22✔
626
                // Nothing to do if both local and remote providers are disabled.
22✔
627
                return
22✔
628
        }
629
        if provider := cast.Val(remoteConfig.SmsProvider, ""); len(provider) > 0 {
8✔
630
                s.Twilio.Enabled = provider == "twilio"
4✔
631
                s.TwilioVerify.Enabled = provider == "twilio_verify"
4✔
632
                s.Messagebird.Enabled = provider == "messagebird"
4✔
633
                s.Textlocal.Enabled = provider == "textlocal"
4✔
634
                s.Vonage.Enabled = provider == "vonage"
4✔
635
        }
4✔
636
}
637

638
func (e external) toAuthConfigBody(body *v1API.UpdateAuthConfigBody) {
2✔
639
        if len(e) == 0 {
4✔
640
                return
2✔
641
        }
2✔
642
        // Ignore configs of disabled providers because their envs are not loaded
643
        if p, ok := e["apple"]; ok {
×
644
                if body.ExternalAppleEnabled = &p.Enabled; *body.ExternalAppleEnabled {
×
645
                        body.ExternalAppleClientId = &p.ClientId
×
646
                        body.ExternalAppleSecret = &p.Secret
×
647
                }
×
648
        }
649
        if p, ok := e["azure"]; ok {
×
650
                if body.ExternalAzureEnabled = &p.Enabled; *body.ExternalAzureEnabled {
×
651
                        body.ExternalAzureClientId = &p.ClientId
×
652
                        body.ExternalAzureSecret = &p.Secret
×
653
                        body.ExternalAzureUrl = &p.Url
×
654
                }
×
655
        }
656
        if p, ok := e["bitbucket"]; ok {
×
657
                if body.ExternalBitbucketEnabled = &p.Enabled; *body.ExternalBitbucketEnabled {
×
658
                        body.ExternalBitbucketClientId = &p.ClientId
×
659
                        body.ExternalBitbucketSecret = &p.Secret
×
660
                }
×
661
        }
662
        if p, ok := e["discord"]; ok {
×
663
                if body.ExternalDiscordEnabled = &p.Enabled; *body.ExternalDiscordEnabled {
×
664
                        body.ExternalDiscordClientId = &p.ClientId
×
665
                        body.ExternalDiscordSecret = &p.Secret
×
666
                }
×
667
        }
668
        if p, ok := e["facebook"]; ok {
×
669
                if body.ExternalFacebookEnabled = &p.Enabled; *body.ExternalFacebookEnabled {
×
670
                        body.ExternalFacebookClientId = &p.ClientId
×
671
                        body.ExternalFacebookSecret = &p.Secret
×
672
                }
×
673
        }
674
        if p, ok := e["figma"]; ok {
×
675
                if body.ExternalFigmaEnabled = &p.Enabled; *body.ExternalFigmaEnabled {
×
676
                        body.ExternalFigmaClientId = &p.ClientId
×
677
                        body.ExternalFigmaSecret = &p.Secret
×
678
                }
×
679
        }
680
        if p, ok := e["github"]; ok {
×
681
                if body.ExternalGithubEnabled = &p.Enabled; *body.ExternalGithubEnabled {
×
682
                        body.ExternalGithubClientId = &p.ClientId
×
683
                        body.ExternalGithubSecret = &p.Secret
×
684
                }
×
685
        }
686
        if p, ok := e["gitlab"]; ok {
×
687
                if body.ExternalGitlabEnabled = &p.Enabled; *body.ExternalGitlabEnabled {
×
688
                        body.ExternalGitlabClientId = &p.ClientId
×
689
                        body.ExternalGitlabSecret = &p.Secret
×
690
                        body.ExternalGitlabUrl = &p.Url
×
691
                }
×
692
        }
693
        if p, ok := e["google"]; ok {
×
694
                if body.ExternalGoogleEnabled = &p.Enabled; *body.ExternalGoogleEnabled {
×
695
                        body.ExternalGoogleClientId = &p.ClientId
×
696
                        body.ExternalGoogleSecret = &p.Secret
×
697
                        body.ExternalGoogleSkipNonceCheck = &p.SkipNonceCheck
×
698
                }
×
699
        }
700
        if p, ok := e["kakao"]; ok {
×
701
                if body.ExternalKakaoEnabled = &p.Enabled; *body.ExternalKakaoEnabled {
×
702
                        body.ExternalKakaoClientId = &p.ClientId
×
703
                        body.ExternalKakaoSecret = &p.Secret
×
704
                }
×
705
        }
706
        if p, ok := e["keycloak"]; ok {
×
707
                if body.ExternalKeycloakEnabled = &p.Enabled; *body.ExternalKeycloakEnabled {
×
708
                        body.ExternalKeycloakClientId = &p.ClientId
×
709
                        body.ExternalKeycloakSecret = &p.Secret
×
710
                        body.ExternalKeycloakUrl = &p.Url
×
711
                }
×
712
        }
713
        if p, ok := e["linkedin_oidc"]; ok {
×
714
                if body.ExternalLinkedinOidcEnabled = &p.Enabled; *body.ExternalLinkedinOidcEnabled {
×
715
                        body.ExternalLinkedinOidcClientId = &p.ClientId
×
716
                        body.ExternalLinkedinOidcSecret = &p.Secret
×
717
                }
×
718
        }
719
        if p, ok := e["notion"]; ok {
×
720
                if body.ExternalNotionEnabled = &p.Enabled; *body.ExternalNotionEnabled {
×
721
                        body.ExternalNotionClientId = &p.ClientId
×
722
                        body.ExternalNotionSecret = &p.Secret
×
723
                }
×
724
        }
725
        if p, ok := e["slack_oidc"]; ok {
×
726
                if body.ExternalSlackOidcEnabled = &p.Enabled; *body.ExternalSlackOidcEnabled {
×
727
                        body.ExternalSlackOidcClientId = &p.ClientId
×
728
                        body.ExternalSlackOidcSecret = &p.Secret
×
729
                }
×
730
        }
731
        if p, ok := e["spotify"]; ok {
×
732
                if body.ExternalSpotifyEnabled = &p.Enabled; *body.ExternalSpotifyEnabled {
×
733
                        body.ExternalSpotifyClientId = &p.ClientId
×
734
                        body.ExternalSpotifySecret = &p.Secret
×
735
                }
×
736
        }
737
        if p, ok := e["twitch"]; ok {
×
738
                if body.ExternalTwitchEnabled = &p.Enabled; *body.ExternalTwitchEnabled {
×
739
                        body.ExternalTwitchClientId = &p.ClientId
×
740
                        body.ExternalTwitchSecret = &p.Secret
×
741
                }
×
742
        }
743
        if p, ok := e["twitter"]; ok {
×
744
                if body.ExternalTwitterEnabled = &p.Enabled; *body.ExternalTwitterEnabled {
×
745
                        body.ExternalTwitterClientId = &p.ClientId
×
746
                        body.ExternalTwitterSecret = &p.Secret
×
747
                }
×
748
        }
749
        if p, ok := e["workos"]; ok {
×
750
                if body.ExternalWorkosEnabled = &p.Enabled; *body.ExternalWorkosEnabled {
×
751
                        body.ExternalWorkosClientId = &p.ClientId
×
752
                        body.ExternalWorkosSecret = &p.Secret
×
753
                        body.ExternalWorkosUrl = &p.Url
×
754
                }
×
755
        }
756
        if p, ok := e["zoom"]; ok {
×
757
                if body.ExternalZoomEnabled = &p.Enabled; *body.ExternalZoomEnabled {
×
758
                        body.ExternalZoomClientId = &p.ClientId
×
759
                        body.ExternalZoomSecret = &p.Secret
×
760
                }
×
761
        }
762
}
763

764
func (e external) fromAuthConfig(remoteConfig v1API.AuthConfigResponse) {
26✔
765
        if len(e) == 0 {
49✔
766
                return
23✔
767
        }
23✔
768
        // Ignore configs of disabled providers because their envs are not loaded
769
        if p, ok := e["apple"]; ok {
6✔
770
                if p.Enabled {
5✔
771
                        p.ClientId = cast.Val(remoteConfig.ExternalAppleClientId, "")
2✔
772
                        if ids := cast.Val(remoteConfig.ExternalAppleAdditionalClientIds, ""); len(ids) > 0 {
3✔
773
                                p.ClientId += "," + ids
1✔
774
                        }
1✔
775
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalAppleSecret, "")
2✔
776
                }
777
                p.Enabled = cast.Val(remoteConfig.ExternalAppleEnabled, false)
3✔
778
                e["apple"] = p
3✔
779
        }
780

781
        if p, ok := e["azure"]; ok {
6✔
782
                if p.Enabled {
5✔
783
                        p.ClientId = cast.Val(remoteConfig.ExternalAzureClientId, "")
2✔
784
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalAzureSecret, "")
2✔
785
                        p.Url = cast.Val(remoteConfig.ExternalAzureUrl, "")
2✔
786
                }
2✔
787
                p.Enabled = cast.Val(remoteConfig.ExternalAzureEnabled, false)
3✔
788
                e["azure"] = p
3✔
789
        }
790

791
        if p, ok := e["bitbucket"]; ok {
6✔
792
                if p.Enabled {
4✔
793
                        p.ClientId = cast.Val(remoteConfig.ExternalBitbucketClientId, "")
1✔
794
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalBitbucketSecret, "")
1✔
795
                }
1✔
796
                p.Enabled = cast.Val(remoteConfig.ExternalBitbucketEnabled, false)
3✔
797
                e["bitbucket"] = p
3✔
798
        }
799

800
        if p, ok := e["discord"]; ok {
6✔
801
                if p.Enabled {
4✔
802
                        p.ClientId = cast.Val(remoteConfig.ExternalDiscordClientId, "")
1✔
803
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalDiscordSecret, "")
1✔
804
                }
1✔
805
                p.Enabled = cast.Val(remoteConfig.ExternalDiscordEnabled, false)
3✔
806
                e["discord"] = p
3✔
807
        }
808

809
        if p, ok := e["facebook"]; ok {
6✔
810
                if p.Enabled {
4✔
811
                        p.ClientId = cast.Val(remoteConfig.ExternalFacebookClientId, "")
1✔
812
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalFacebookSecret, "")
1✔
813
                }
1✔
814
                p.Enabled = cast.Val(remoteConfig.ExternalFacebookEnabled, false)
3✔
815
                e["facebook"] = p
3✔
816
        }
817

818
        if p, ok := e["figma"]; ok {
6✔
819
                if p.Enabled {
4✔
820
                        p.ClientId = cast.Val(remoteConfig.ExternalFigmaClientId, "")
1✔
821
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalFigmaSecret, "")
1✔
822
                }
1✔
823
                p.Enabled = cast.Val(remoteConfig.ExternalFigmaEnabled, false)
3✔
824
                e["figma"] = p
3✔
825
        }
826

827
        if p, ok := e["github"]; ok {
6✔
828
                if p.Enabled {
4✔
829
                        p.ClientId = cast.Val(remoteConfig.ExternalGithubClientId, "")
1✔
830
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalGithubSecret, "")
1✔
831
                }
1✔
832
                p.Enabled = cast.Val(remoteConfig.ExternalGithubEnabled, false)
3✔
833
                e["github"] = p
3✔
834
        }
835

836
        if p, ok := e["gitlab"]; ok {
6✔
837
                if p.Enabled {
4✔
838
                        p.ClientId = cast.Val(remoteConfig.ExternalGitlabClientId, "")
1✔
839
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalGitlabSecret, "")
1✔
840
                        p.Url = cast.Val(remoteConfig.ExternalGitlabUrl, "")
1✔
841
                }
1✔
842
                p.Enabled = cast.Val(remoteConfig.ExternalGitlabEnabled, false)
3✔
843
                e["gitlab"] = p
3✔
844
        }
845

846
        if p, ok := e["google"]; ok {
6✔
847
                if p.Enabled {
4✔
848
                        p.ClientId = cast.Val(remoteConfig.ExternalGoogleClientId, "")
1✔
849
                        if ids := cast.Val(remoteConfig.ExternalGoogleAdditionalClientIds, ""); len(ids) > 0 {
1✔
850
                                p.ClientId += "," + ids
×
851
                        }
×
852
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalGoogleSecret, "")
1✔
853
                        p.SkipNonceCheck = cast.Val(remoteConfig.ExternalGoogleSkipNonceCheck, false)
1✔
854
                }
855
                p.Enabled = cast.Val(remoteConfig.ExternalGoogleEnabled, false)
3✔
856
                e["google"] = p
3✔
857
        }
858

859
        if p, ok := e["kakao"]; ok {
5✔
860
                if p.Enabled {
3✔
861
                        p.ClientId = cast.Val(remoteConfig.ExternalKakaoClientId, "")
1✔
862
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalKakaoSecret, "")
1✔
863
                }
1✔
864
                p.Enabled = cast.Val(remoteConfig.ExternalKakaoEnabled, false)
2✔
865
                e["kakao"] = p
2✔
866
        }
867

868
        if p, ok := e["keycloak"]; ok {
6✔
869
                if p.Enabled {
4✔
870
                        p.ClientId = cast.Val(remoteConfig.ExternalKeycloakClientId, "")
1✔
871
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalKeycloakSecret, "")
1✔
872
                        p.Url = cast.Val(remoteConfig.ExternalKeycloakUrl, "")
1✔
873
                }
1✔
874
                p.Enabled = cast.Val(remoteConfig.ExternalKeycloakEnabled, false)
3✔
875
                e["keycloak"] = p
3✔
876
        }
877

878
        if p, ok := e["linkedin_oidc"]; ok {
6✔
879
                if p.Enabled {
4✔
880
                        p.ClientId = cast.Val(remoteConfig.ExternalLinkedinOidcClientId, "")
1✔
881
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalLinkedinOidcSecret, "")
1✔
882
                }
1✔
883
                p.Enabled = cast.Val(remoteConfig.ExternalLinkedinOidcEnabled, false)
3✔
884
                e["linkedin_oidc"] = p
3✔
885
        }
886

887
        if p, ok := e["notion"]; ok {
6✔
888
                if p.Enabled {
4✔
889
                        p.ClientId = cast.Val(remoteConfig.ExternalNotionClientId, "")
1✔
890
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalNotionSecret, "")
1✔
891
                }
1✔
892
                p.Enabled = cast.Val(remoteConfig.ExternalNotionEnabled, false)
3✔
893
                e["notion"] = p
3✔
894
        }
895

896
        if p, ok := e["slack_oidc"]; ok {
6✔
897
                if p.Enabled {
4✔
898
                        p.ClientId = cast.Val(remoteConfig.ExternalSlackOidcClientId, "")
1✔
899
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalSlackOidcSecret, "")
1✔
900
                }
1✔
901
                p.Enabled = cast.Val(remoteConfig.ExternalSlackOidcEnabled, false)
3✔
902
                e["slack_oidc"] = p
3✔
903
        }
904

905
        if p, ok := e["spotify"]; ok {
6✔
906
                if p.Enabled {
4✔
907
                        p.ClientId = cast.Val(remoteConfig.ExternalSpotifyClientId, "")
1✔
908
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalSpotifySecret, "")
1✔
909
                }
1✔
910
                p.Enabled = cast.Val(remoteConfig.ExternalSpotifyEnabled, false)
3✔
911
                e["spotify"] = p
3✔
912
        }
913

914
        if p, ok := e["twitch"]; ok {
6✔
915
                if p.Enabled {
4✔
916
                        p.ClientId = cast.Val(remoteConfig.ExternalTwitchClientId, "")
1✔
917
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalTwitchSecret, "")
1✔
918
                }
1✔
919
                p.Enabled = cast.Val(remoteConfig.ExternalTwitchEnabled, false)
3✔
920
                e["twitch"] = p
3✔
921
        }
922

923
        if p, ok := e["twitter"]; ok {
6✔
924
                if p.Enabled {
4✔
925
                        p.ClientId = cast.Val(remoteConfig.ExternalTwitterClientId, "")
1✔
926
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalTwitterSecret, "")
1✔
927
                }
1✔
928
                p.Enabled = cast.Val(remoteConfig.ExternalTwitterEnabled, false)
3✔
929
                e["twitter"] = p
3✔
930
        }
931

932
        if p, ok := e["workos"]; ok {
6✔
933
                if p.Enabled {
4✔
934
                        p.ClientId = cast.Val(remoteConfig.ExternalWorkosClientId, "")
1✔
935
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalWorkosSecret, "")
1✔
936
                        p.Url = cast.Val(remoteConfig.ExternalWorkosUrl, "")
1✔
937
                }
1✔
938
                p.Enabled = cast.Val(remoteConfig.ExternalWorkosEnabled, false)
3✔
939
                e["workos"] = p
3✔
940
        }
941

942
        if p, ok := e["zoom"]; ok {
6✔
943
                if p.Enabled {
4✔
944
                        p.ClientId = cast.Val(remoteConfig.ExternalZoomClientId, "")
1✔
945
                        p.Secret = hashPrefix + cast.Val(remoteConfig.ExternalZoomSecret, "")
1✔
946
                }
1✔
947
                p.Enabled = cast.Val(remoteConfig.ExternalZoomEnabled, false)
3✔
948
                e["zoom"] = p
3✔
949
        }
950
}
951

952
func (a *auth) DiffWithRemote(projectRef string, remoteConfig v1API.AuthConfigResponse) ([]byte, error) {
26✔
953
        copy := a.Clone()
26✔
954
        copy.HashSecrets(projectRef)
26✔
955
        // Convert the config values into easily comparable remoteConfig values
26✔
956
        currentValue, err := ToTomlBytes(copy)
26✔
957
        if err != nil {
26✔
958
                return nil, err
×
959
        }
×
960
        copy.FromRemoteAuthConfig(remoteConfig)
26✔
961
        remoteCompare, err := ToTomlBytes(copy)
26✔
962
        if err != nil {
26✔
963
                return nil, err
×
964
        }
×
965
        return diff.Diff("remote[auth]", remoteCompare, "local[auth]", currentValue), nil
26✔
966
}
967

968
const hashPrefix = "hash:"
969

970
func (a *auth) HashSecrets(key string) {
26✔
971
        hash := func(v string) string {
61✔
972
                if len(v) == 0 {
58✔
973
                        return hashPrefix
23✔
974
                }
23✔
975
                return hashPrefix + sha256Hmac(key, v)
12✔
976
        }
977
        if a.Email.Smtp != nil && a.Email.Smtp.IsEnabled() {
28✔
978
                a.Email.Smtp.Pass = hash(a.Email.Smtp.Pass)
2✔
979
        }
2✔
980
        // Only hash secrets for locally enabled providers because other envs won't be loaded
981
        switch {
26✔
982
        case a.Sms.Twilio.Enabled:
1✔
983
                a.Sms.Twilio.AuthToken = hash(a.Sms.Twilio.AuthToken)
1✔
984
        case a.Sms.TwilioVerify.Enabled:
×
985
                a.Sms.TwilioVerify.AuthToken = hash(a.Sms.TwilioVerify.AuthToken)
×
986
        case a.Sms.Messagebird.Enabled:
2✔
987
                a.Sms.Messagebird.AccessKey = hash(a.Sms.Messagebird.AccessKey)
2✔
988
        case a.Sms.Textlocal.Enabled:
×
989
                a.Sms.Textlocal.ApiKey = hash(a.Sms.Textlocal.ApiKey)
×
990
        case a.Sms.Vonage.Enabled:
×
991
                a.Sms.Vonage.ApiSecret = hash(a.Sms.Vonage.ApiSecret)
×
992
        }
993
        if a.Hook.MFAVerificationAttempt != nil && a.Hook.MFAVerificationAttempt.Enabled {
28✔
994
                a.Hook.MFAVerificationAttempt.Secrets = hash(a.Hook.MFAVerificationAttempt.Secrets)
2✔
995
        }
2✔
996
        if a.Hook.PasswordVerificationAttempt != nil && a.Hook.PasswordVerificationAttempt.Enabled {
27✔
997
                a.Hook.PasswordVerificationAttempt.Secrets = hash(a.Hook.PasswordVerificationAttempt.Secrets)
1✔
998
        }
1✔
999
        if a.Hook.CustomAccessToken != nil && a.Hook.CustomAccessToken.Enabled {
28✔
1000
                a.Hook.CustomAccessToken.Secrets = hash(a.Hook.CustomAccessToken.Secrets)
2✔
1001
        }
2✔
1002
        if a.Hook.SendSMS != nil && a.Hook.SendSMS.Enabled {
28✔
1003
                a.Hook.SendSMS.Secrets = hash(a.Hook.SendSMS.Secrets)
2✔
1004
        }
2✔
1005
        if a.Hook.SendEmail != nil && a.Hook.SendEmail.Enabled {
28✔
1006
                a.Hook.SendEmail.Secrets = hash(a.Hook.SendEmail.Secrets)
2✔
1007
        }
2✔
1008
        for name, provider := range a.External {
82✔
1009
                if provider.Enabled {
77✔
1010
                        provider.Secret = hash(provider.Secret)
21✔
1011
                }
21✔
1012
                a.External[name] = provider
56✔
1013
        }
1014
        // TODO: support SecurityCaptchaSecret in local config
1015
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc