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

oxyno-zeta / s3-proxy / 24586904258

17 Apr 2026 09:11PM UTC coverage: 86.051% (-0.04%) from 86.086%
24586904258

push

github

oxyno-zeta
fix(test): fixing and adding more test

3 of 3 new or added lines in 1 file covered. (100.0%)

64 existing lines in 3 files now uncovered.

5441 of 6323 relevant lines covered (86.05%)

46.68 hits per line

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

80.32
/pkg/s3-proxy/config/managerimpl.go
1
package config
2

3
import (
4
        "fmt"
5
        "net/http"
6
        "os"
7
        "path"
8
        "path/filepath"
9
        "regexp"
10
        "strings"
11
        "sync"
12
        "time"
13

14
        "emperror.dev/errors"
15
        "github.com/fsnotify/fsnotify"
16
        "github.com/go-playground/validator/v10"
17
        "github.com/spf13/viper"
18
        "github.com/thoas/go-funk"
19

20
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/log"
21
        "github.com/oxyno-zeta/s3-proxy/pkg/s3-proxy/utils/generalutils"
22
)
23

24
var validate = validator.New()
25

26
type managerimpl struct {
27
        cfg                       *Config
28
        configs                   []*viper.Viper
29
        onChangeHooks             []func()
30
        logger                    log.Logger
31
        internalFileWatchChannels []chan bool
32
}
33

34
func (impl *managerimpl) AddOnChangeHook(hook func()) {
5✔
35
        impl.onChangeHooks = append(impl.onChangeHooks, hook)
5✔
36
}
5✔
37

38
func (impl *managerimpl) Load(mainConfDir string) error {
28✔
39
        // List files
28✔
40
        files, err := os.ReadDir(mainConfDir)
28✔
41
        if err != nil {
28✔
42
                return errors.WithStack(err)
×
43
        }
×
44

45
        // Generate viper instances for static configs
46
        impl.configs = generateViperInstances(files, mainConfDir)
28✔
47

28✔
48
        // Load configuration
28✔
49
        err = impl.loadConfiguration()
28✔
50
        if err != nil {
35✔
51
                return err
7✔
52
        }
7✔
53

54
        // Loop over config files
55
        funk.ForEach(impl.configs, func(vip *viper.Viper) {
52✔
56
                // Add hooks for on change events
31✔
57
                vip.OnConfigChange(func(_ fsnotify.Event) {
34✔
58
                        impl.logger.Infof("Reload configuration detected for file %s", vip.ConfigFileUsed())
3✔
59

3✔
60
                        // Reload config
3✔
61
                        err2 := impl.loadConfiguration()
3✔
62
                        if err2 != nil {
4✔
63
                                impl.logger.Error(err2)
1✔
64
                                // Stop here and do not call hooks => configuration is unstable
1✔
65
                                return
1✔
66
                        }
1✔
67
                        // Call all hooks
68
                        funk.ForEach(impl.onChangeHooks, func(hook func()) { hook() })
4✔
69
                })
70
                // Watch for configuration changes
71
                vip.WatchConfig()
31✔
72
        })
73

74
        return nil
21✔
75
}
76

77
// Imported and modified from viper v1.7.0.
78
func (impl *managerimpl) watchInternalFile(filePath string, forceStop chan bool, onChange func()) {
9✔
79
        initWG := sync.WaitGroup{}
9✔
80
        initWG.Add(1)
9✔
81

9✔
82
        go func() {
18✔
83
                watcher, err := fsnotify.NewWatcher()
9✔
84
                if err != nil {
9✔
85
                        impl.logger.Fatal(errors.WithStack(err))
×
86
                }
×
87
                defer watcher.Close()
9✔
88

9✔
89
                configFile := filepath.Clean(filePath)
9✔
90
                configDir, _ := filepath.Split(configFile)
9✔
91
                realConfigFile, _ := filepath.EvalSymlinks(filePath)
9✔
92

9✔
93
                eventsWG := sync.WaitGroup{}
9✔
94
                eventsWG.Add(1)
9✔
95

9✔
96
                go func() {
18✔
97
                        for {
23✔
98
                                select {
14✔
99
                                case <-forceStop:
3✔
100
                                        eventsWG.Done()
3✔
101

3✔
102
                                        return
3✔
103
                                case event, ok := <-watcher.Events:
11✔
104
                                        if !ok { // 'Events' channel is closed
11✔
105
                                                eventsWG.Done()
×
106

×
107
                                                return
×
108
                                        }
×
109

110
                                        currentConfigFile, _ := filepath.EvalSymlinks(filePath)
11✔
111
                                        // we only care about the config file with the following cases:
11✔
112
                                        // 1 - if the config file was modified or created
11✔
113
                                        // 2 - if the real path to the config file changed (eg: k8s ConfigMap replacement)
11✔
114
                                        const writeOrCreateMask = fsnotify.Write | fsnotify.Create
11✔
115
                                        if (filepath.Clean(event.Name) == configFile &&
11✔
116
                                                event.Op&writeOrCreateMask != 0) ||
11✔
117
                                                (currentConfigFile != "" && currentConfigFile != realConfigFile) {
12✔
118
                                                realConfigFile = currentConfigFile
1✔
119

1✔
120
                                                // Call on change
1✔
121
                                                onChange()
1✔
122
                                        } else if filepath.Clean(event.Name) == configFile && event.Op&fsnotify.Remove&fsnotify.Remove != 0 {
17✔
123
                                                eventsWG.Done()
6✔
124

6✔
125
                                                return
6✔
126
                                        }
6✔
127

128
                                case err, ok := <-watcher.Errors:
×
129
                                        if ok { // 'Errors' channel is not closed
×
130
                                                impl.logger.Errorf("watcher error: %v\n", err)
×
131
                                        }
×
132

133
                                        eventsWG.Done()
×
134

×
135
                                        return
×
136
                                }
137
                        }
138
                }()
139

140
                _ = watcher.Add(configDir)
9✔
141

9✔
142
                initWG.Done()   // done initializing the watch in this go routine, so the parent routine can move on...
9✔
143
                eventsWG.Wait() // now, wait for event loop to end in this go-routine...
9✔
144
        }()
145

146
        initWG.Wait() // make sure that the go routine above fully ended before returning
9✔
147
}
148

149
func (*managerimpl) loadDefaultConfigurationValues(vip *viper.Viper) {
31✔
150
        // Load default configuration
31✔
151
        vip.SetDefault("log.level", DefaultLogLevel)
31✔
152
        vip.SetDefault("log.format", DefaultLogFormat)
31✔
153
        vip.SetDefault("server.port", DefaultPort)
31✔
154
        vip.SetDefault("server.compress.enabled", &DefaultServerCompressEnabled)
31✔
155
        vip.SetDefault("server.compress.level", DefaultServerCompressLevel)
31✔
156
        vip.SetDefault("server.compress.types", DefaultServerCompressTypes)
31✔
157
        vip.SetDefault("server.timeouts.readHeaderTimeout", DefaultServerTimeoutsReadHeaderTimeout)
31✔
158
        vip.SetDefault("internalServer.port", DefaultInternalPort)
31✔
159
        vip.SetDefault("internalServer.compress.enabled", &DefaultServerCompressEnabled)
31✔
160
        vip.SetDefault("internalServer.compress.level", DefaultServerCompressLevel)
31✔
161
        vip.SetDefault("internalServer.compress.types", DefaultServerCompressTypes)
31✔
162
        vip.SetDefault("internalServer.timeouts.readHeaderTimeout", DefaultServerTimeoutsReadHeaderTimeout)
31✔
163
        vip.SetDefault("templates.helpers", []string{DefaultTemplateHelpersPath})
31✔
164
        vip.SetDefault("templates.folderList.path", DefaultTemplateFolderListPath)
31✔
165
        vip.SetDefault("templates.folderList.headers", DefaultTemplateHeaders)
31✔
166
        vip.SetDefault("templates.folderList.status", DefaultTemplateStatusOk)
31✔
167
        vip.SetDefault("templates.targetList.path", DefaultTemplateTargetListPath)
31✔
168
        vip.SetDefault("templates.targetList.headers", DefaultTemplateHeaders)
31✔
169
        vip.SetDefault("templates.targetList.status", DefaultTemplateStatusOk)
31✔
170
        vip.SetDefault("templates.notFoundError.path", DefaultTemplateNotFoundErrorPath)
31✔
171
        vip.SetDefault("templates.notFoundError.headers", DefaultTemplateHeaders)
31✔
172
        vip.SetDefault("templates.notFoundError.status", DefaultTemplateStatusNotFound)
31✔
173
        vip.SetDefault("templates.internalServerError.path", DefaultTemplateInternalServerErrorPath)
31✔
174
        vip.SetDefault("templates.internalServerError.headers", DefaultTemplateHeaders)
31✔
175
        vip.SetDefault("templates.internalServerError.status", DefaultTemplateStatusInternalServerError)
31✔
176
        vip.SetDefault("templates.unauthorizedError.path", DefaultTemplateUnauthorizedErrorPath)
31✔
177
        vip.SetDefault("templates.unauthorizedError.headers", DefaultTemplateHeaders)
31✔
178
        vip.SetDefault("templates.unauthorizedError.status", DefaultTemplateStatusUnauthorized)
31✔
179
        vip.SetDefault("templates.forbiddenError.path", DefaultTemplateForbiddenErrorPath)
31✔
180
        vip.SetDefault("templates.forbiddenError.headers", DefaultTemplateHeaders)
31✔
181
        vip.SetDefault("templates.forbiddenError.status", DefaultTemplateStatusForbidden)
31✔
182
        vip.SetDefault("templates.badRequestError.path", DefaultTemplateBadRequestErrorPath)
31✔
183
        vip.SetDefault("templates.badRequestError.headers", DefaultTemplateHeaders)
31✔
184
        vip.SetDefault("templates.badRequestError.status", DefaultTemplateStatusBadRequest)
31✔
185
        vip.SetDefault("templates.put.path", DefaultTemplatePutPath)
31✔
186
        vip.SetDefault("templates.put.headers", DefaultEmptyTemplateHeaders)
31✔
187
        vip.SetDefault("templates.put.status", DefaultTemplateStatusNoContent)
31✔
188
        vip.SetDefault("templates.delete.path", DefaultTemplateDeletePath)
31✔
189
        vip.SetDefault("templates.delete.headers", DefaultEmptyTemplateHeaders)
31✔
190
        vip.SetDefault("templates.delete.status", DefaultTemplateStatusNoContent)
31✔
191
}
31✔
192

193
func generateViperInstances(files []os.DirEntry, mainConfDir string) []*viper.Viper {
28✔
194
        list := make([]*viper.Viper, 0)
28✔
195
        // Loop over static files to create viper instance for them
28✔
196
        funk.ForEach(files, func(file os.DirEntry) {
69✔
197
                filename := file.Name()
41✔
198
                // Create config file name
41✔
199
                cfgFileName := strings.TrimSuffix(filename, path.Ext(filename))
41✔
200
                // Test if config file name is compliant (ignore hidden files like .keep or directory)
41✔
201
                if !strings.HasPrefix(filename, ".") && cfgFileName != "" && !file.IsDir() {
79✔
202
                        // Create new viper instance
38✔
203
                        vip := viper.New()
38✔
204
                        // Set config name
38✔
205
                        vip.SetConfigName(cfgFileName)
38✔
206
                        // Add configuration path
38✔
207
                        vip.AddConfigPath(mainConfDir)
38✔
208
                        // Append it
38✔
209
                        list = append(list, vip)
38✔
210
                }
38✔
211
        })
212

213
        return list
28✔
214
}
215

216
func (impl *managerimpl) loadConfiguration() error {
31✔
217
        // Load must start by flushing all existing watcher on internal files
31✔
218
        for i := range len(impl.internalFileWatchChannels) {
34✔
219
                ch := impl.internalFileWatchChannels[i]
3✔
220
                // Send the force stop
3✔
221
                ch <- true
3✔
222
        }
3✔
223

224
        // Create a viper instance for default value and merging
225
        globalViper := viper.New()
31✔
226

31✔
227
        // Put default values
31✔
228
        impl.loadDefaultConfigurationValues(globalViper)
31✔
229

31✔
230
        // Loop over configs
31✔
231
        for _, vip := range impl.configs {
75✔
232
                err := vip.ReadInConfig()
44✔
233
                if err != nil {
50✔
234
                        return errors.WithStack(err)
6✔
235
                }
6✔
236

237
                err = globalViper.MergeConfigMap(vip.AllSettings())
38✔
238
                if err != nil {
38✔
239
                        return errors.WithStack(err)
×
240
                }
×
241
        }
242

243
        // Prepare configuration object
244
        var out Config
25✔
245
        // Quick unmarshal.
25✔
246
        err := globalViper.Unmarshal(&out)
25✔
247
        if err != nil {
25✔
248
                return errors.WithStack(err)
×
249
        }
×
250

251
        // Load default values
252
        err = loadBusinessDefaultValues(&out)
25✔
253
        if err != nil {
25✔
254
                return err
×
255
        }
×
256

257
        // Configuration validation
258
        err = validate.Struct(out)
25✔
259
        if err != nil {
25✔
260
                return errors.WithStack(err)
×
261
        }
×
262

263
        // Load all credentials
264
        credentials, err := loadAllCredentials(&out)
25✔
265
        if err != nil {
27✔
266
                return err
2✔
267
        }
2✔
268
        // Initialize or flush watch internal file channels
269
        internalFileWatchChannels := make([]chan bool, 0)
23✔
270
        impl.internalFileWatchChannels = internalFileWatchChannels
23✔
271
        // Loop over all credentials in order to watch file change
23✔
272
        funk.ForEach(credentials, func(cred *CredentialConfig) {
53✔
273
                // Check if credential is about a path
30✔
274
                if cred.Path != "" {
39✔
275
                        // Create channel
9✔
276
                        ch := make(chan bool)
9✔
277
                        // Run the watch file
9✔
278
                        impl.watchInternalFile(cred.Path, ch, func() {
10✔
279
                                // File change detected
1✔
280
                                impl.logger.Infof("Reload credential file detected for path %s", cred.Path)
1✔
281

1✔
282
                                // Reload config
1✔
283
                                err2 := loadCredential(cred)
1✔
284
                                if err2 != nil {
1✔
UNCOV
285
                                        impl.logger.Error(err2)
×
UNCOV
286
                                        // Stop here and do not call hooks => configuration is unstable
×
UNCOV
287
                                        return
×
UNCOV
288
                                }
×
289
                                // Call all hooks
290
                                funk.ForEach(impl.onChangeHooks, func(hook func()) { hook() })
2✔
291
                        })
292
                        // Add channel to list of channels
293
                        impl.internalFileWatchChannels = append(impl.internalFileWatchChannels, ch)
9✔
294
                }
295
        })
296

297
        err = validateBusinessConfig(&out)
23✔
298
        if err != nil {
23✔
299
                return err
×
300
        }
×
301

302
        impl.cfg = &out
23✔
303

23✔
304
        return nil
23✔
305
}
306

307
// GetConfig allow to get configuration object.
308
func (impl *managerimpl) GetConfig() *Config {
32✔
309
        return impl.cfg
32✔
310
}
32✔
311

312
func loadAllCredentials(out *Config) ([]*CredentialConfig, error) {
32✔
313
        // Initialize answer
32✔
314
        result := make([]*CredentialConfig, 0)
32✔
315

32✔
316
        // Load credentials from declaration
32✔
317
        for _, item := range out.Targets {
60✔
318
                // Check if resources are declared
28✔
319
                if item.Resources != nil {
30✔
320
                        for j := range len(item.Resources) {
4✔
321
                                // Store ressource
2✔
322
                                res := item.Resources[j]
2✔
323
                                // Check if basic auth configuration exists
2✔
324
                                if res.Basic != nil && res.Basic.Credentials != nil {
3✔
325
                                        // Loop over creds
1✔
326
                                        for k := range len(res.Basic.Credentials) {
2✔
327
                                                it := res.Basic.Credentials[k]
1✔
328
                                                // Load credential
1✔
329
                                                err := loadCredential(it.Password)
1✔
330
                                                if err != nil {
1✔
331
                                                        return nil, err
×
332
                                                }
×
333
                                                // Save credential
334
                                                result = append(result, it.Password)
1✔
335
                                        }
336
                                }
337
                        }
338
                }
339
                // Check if actions are declared
340
                if item.Actions != nil {
52✔
341
                        // Check if GET actions are declared and webhook configs
24✔
342
                        if item.Actions.GET != nil && item.Actions.GET.Config != nil {
26✔
343
                                // Load webhook secrets
2✔
344
                                res, err := loadWebhookCfgCredentials(item.Actions.GET.Config.Webhooks)
2✔
345
                                // Check error
2✔
346
                                if err != nil {
2✔
347
                                        return nil, err
×
348
                                }
×
349
                                // Save credential
350
                                result = append(result, res...)
2✔
351
                        }
352

353
                        // Check if PUT actions are declared and webhook configs
354
                        if item.Actions.PUT != nil && item.Actions.PUT.Config != nil {
24✔
355
                                // Load webhook secrets
×
356
                                res, err := loadWebhookCfgCredentials(item.Actions.PUT.Config.Webhooks)
×
357
                                // Check error
×
358
                                if err != nil {
×
359
                                        return nil, err
×
360
                                }
×
361
                                // Save credential
362
                                result = append(result, res...)
×
363
                        }
364

365
                        // Check if DELETE actions are declared and webhook configs
366
                        if item.Actions.DELETE != nil && item.Actions.DELETE.Config != nil {
24✔
367
                                // Load webhook secrets
×
368
                                res, err := loadWebhookCfgCredentials(item.Actions.DELETE.Config.Webhooks)
×
369
                                // Check error
×
370
                                if err != nil {
×
371
                                        return nil, err
×
372
                                }
×
373
                                // Save credential
374
                                result = append(result, res...)
×
375
                        }
376
                }
377
                // Load credentials for access key and secret key
378
                if item.Bucket != nil &&
28✔
379
                        item.Bucket.Credentials != nil &&
28✔
380
                        item.Bucket.Credentials.AccessKey != nil &&
28✔
381
                        item.Bucket.Credentials.SecretKey != nil {
46✔
382
                        // Manage access key
18✔
383
                        err := loadCredential(item.Bucket.Credentials.AccessKey)
18✔
384
                        if err != nil {
19✔
385
                                return nil, err
1✔
386
                        }
1✔
387
                        // Manage secret key
388
                        err = loadCredential(item.Bucket.Credentials.SecretKey)
17✔
389
                        if err != nil {
18✔
390
                                return nil, err
1✔
391
                        }
1✔
392
                        // Save credential
393
                        result = append(result, item.Bucket.Credentials.AccessKey, item.Bucket.Credentials.SecretKey)
16✔
394
                }
395
        }
396

397
        // Load auth credentials
398
        if out.AuthProviders != nil {
34✔
399
                // Load credentials for oidc auth if needed
4✔
400
                if out.AuthProviders.OIDC != nil {
5✔
401
                        // Load credentials for oidc auth if needed
1✔
402
                        for _, v := range out.AuthProviders.OIDC {
2✔
403
                                // Check if client secret exists
1✔
404
                                if v.ClientSecret != nil {
2✔
405
                                        err := loadCredential(v.ClientSecret)
1✔
406
                                        if err != nil {
1✔
407
                                                return nil, err
×
408
                                        }
×
409
                                        // Save credential
410
                                        result = append(result, v.ClientSecret)
1✔
411
                                }
412
                        }
413
                }
414
        }
415

416
        // Load auth credentials from list targets with basic auth
417
        if out.ListTargets != nil && out.ListTargets.Resource != nil &&
30✔
418
                out.ListTargets.Resource.Basic != nil && out.ListTargets.Resource.Basic.Credentials != nil {
31✔
419
                // Loop over credentials declared
1✔
420
                for i := range len(out.ListTargets.Resource.Basic.Credentials) {
2✔
421
                        // Store item access
1✔
422
                        it := out.ListTargets.Resource.Basic.Credentials[i]
1✔
423
                        // Load credential
1✔
424
                        err := loadCredential(it.Password)
1✔
425
                        if err != nil {
1✔
426
                                return nil, err
×
427
                        }
×
428
                        // Save credential
429
                        result = append(result, it.Password)
1✔
430
                }
431
        }
432

433
        // Load SSL S3 credentials from server/internal server
434
        if out.Server != nil {
53✔
435
                serverCreds, err := loadServerSSLCredentials(out.Server)
23✔
436
                if err != nil {
23✔
437
                        return nil, err
×
438
                }
×
439

440
                result = append(result, serverCreds...)
23✔
441
        }
442

443
        if out.InternalServer != nil {
53✔
444
                serverCreds, err := loadServerSSLCredentials(out.InternalServer)
23✔
445
                if err != nil {
23✔
446
                        return nil, err
×
447
                }
×
448

449
                result = append(result, serverCreds...)
23✔
450
        }
451

452
        return result, nil
30✔
453
}
454

455
// loadServerSSLCredentials is used for any bucket-specific credentials under the
456
// {server/internalServer}.ssl.certificates[*].certificateUrlConfig / privateKeyUrlConfig branches.
457
func loadServerSSLCredentials(serverConfig *ServerConfig) ([]*CredentialConfig, error) {
46✔
458
        if serverConfig.SSL == nil {
92✔
459
                return nil, nil
46✔
460
        }
46✔
461

462
        res := make([]*CredentialConfig, 0)
×
463

×
464
        for _, cert := range serverConfig.SSL.Certificates {
×
465
                if cert.CertificateURLConfig != nil && cert.CertificateURLConfig.AWSCredentials != nil {
×
466
                        s3Creds := cert.CertificateURLConfig.AWSCredentials
×
467

×
468
                        if s3Creds.AccessKey != nil {
×
469
                                err := loadCredential(s3Creds.AccessKey)
×
470
                                if err != nil {
×
471
                                        return nil, err
×
472
                                }
×
473
                        }
474

475
                        if s3Creds.SecretKey != nil {
×
476
                                err := loadCredential(s3Creds.SecretKey)
×
477
                                if err != nil {
×
478
                                        return nil, err
×
479
                                }
×
480
                        }
481
                }
482

483
                if cert.PrivateKeyURLConfig != nil && cert.PrivateKeyURLConfig.AWSCredentials != nil {
×
484
                        s3Creds := cert.PrivateKeyURLConfig.AWSCredentials
×
485

×
486
                        if s3Creds.AccessKey != nil {
×
487
                                err := loadCredential(s3Creds.AccessKey)
×
488
                                if err != nil {
×
489
                                        return nil, err
×
490
                                }
×
491
                        }
492

493
                        if s3Creds.SecretKey != nil {
×
494
                                err := loadCredential(s3Creds.SecretKey)
×
495
                                if err != nil {
×
496
                                        return nil, err
×
497
                                }
×
498
                        }
499
                }
500
        }
501

502
        return res, nil
×
503
}
504

505
func loadWebhookCfgCredentials(cfgList []*WebhookConfig) ([]*CredentialConfig, error) {
2✔
506
        // Create result
2✔
507
        res := make([]*CredentialConfig, 0)
2✔
508
        // Loop over the list
2✔
509
        for _, wbCfg := range cfgList {
2✔
510
                // Loop over the secret header
×
511
                for _, secCre := range wbCfg.SecretHeaders {
×
512
                        // Loop credential
×
513
                        err := loadCredential(secCre)
×
514
                        // Check error
×
515
                        if err != nil {
×
516
                                return nil, err
×
517
                        }
×
518

519
                        // Save
520
                        res = append(res, secCre)
×
521
                }
522
        }
523

524
        // Default
525
        return res, nil
2✔
526
}
527

528
func loadCredential(credCfg *CredentialConfig) error {
39✔
529
        if credCfg.Path != "" {
50✔
530
                // Secret file
11✔
531
                databytes, err := os.ReadFile(credCfg.Path)
11✔
532
                if err != nil {
12✔
533
                        return errors.WithStack(err)
1✔
534
                }
1✔
535
                // Store val
536
                val := string(databytes)
10✔
537
                // Clean new lines
10✔
538
                val = generalutils.NewLineMatcherRegex.ReplaceAllString(val, "")
10✔
539

10✔
540
                credCfg.Value = val
10✔
541
        } else if credCfg.Env != "" {
32✔
542
                // Environment variable
4✔
543
                envValue := os.Getenv(credCfg.Env)
4✔
544
                if envValue == "" {
5✔
545
                        err := fmt.Errorf(TemplateErrLoadingEnvCredentialEmpty, credCfg.Env)
1✔
546

1✔
547
                        return errors.WithStack(err)
1✔
548
                }
1✔
549
                // Store value
550
                credCfg.Value = envValue
3✔
551
        }
552
        // Default value
553
        return nil
37✔
554
}
555

556
func loadResourceValues(res *Resource) error {
6✔
557
        // Check if resource has methods
6✔
558
        if res.Methods == nil {
12✔
559
                // Set default values
6✔
560
                res.Methods = []string{http.MethodGet}
6✔
561
        }
6✔
562

563
        // Check if regexp is enabled in OIDC Authorization groups
564
        if res.OIDC != nil && res.OIDC.AuthorizationAccesses != nil {
9✔
565
                for _, item := range res.OIDC.AuthorizationAccesses {
6✔
566
                        err2 := loadRegexOIDCAuthorizationAccess(item)
3✔
567
                        if err2 != nil {
3✔
568
                                return err2
×
569
                        }
×
570
                }
571
        }
572

573
        // Check if tags are set in OPA server authorizations
574
        if res.OIDC != nil && res.OIDC.AuthorizationOPAServer != nil && res.OIDC.AuthorizationOPAServer.Tags == nil {
7✔
575
                res.OIDC.AuthorizationOPAServer.Tags = map[string]string{}
1✔
576
        }
1✔
577

578
        return nil
6✔
579
}
580

581
func loadBusinessDefaultValues(out *Config) error {
38✔
582
        // Manage default values for targets
38✔
583
        for key, item := range out.Targets {
67✔
584
                // Put target name in structure with key as value
29✔
585
                item.Name = key
29✔
586

29✔
587
                // Manage default configuration for target region
29✔
588
                if item.Bucket != nil && item.Bucket.Region == "" {
30✔
589
                        item.Bucket.Region = DefaultBucketRegion
1✔
590
                }
1✔
591
                // Manage default configuration for bucket S3 List Max Keys
592
                if item.Bucket != nil && item.Bucket.S3ListMaxKeys == 0 {
54✔
593
                        item.Bucket.S3ListMaxKeys = DefaultBucketS3ListMaxKeys
25✔
594
                }
25✔
595
                // Manage default s3 max upload parts
596
                if item.Bucket != nil && item.Bucket.S3MaxUploadParts == 0 {
53✔
597
                        item.Bucket.S3MaxUploadParts = DefaultS3MaxUploadParts
24✔
598
                }
24✔
599
                // Manage default s3 upload part size
600
                if item.Bucket != nil && item.Bucket.S3UploadPartSize == 0 {
53✔
601
                        item.Bucket.S3UploadPartSize = DefaultS3UploadPartSize
24✔
602
                }
24✔
603
                // Manage default s3 upload concurrency
604
                if item.Bucket != nil && item.Bucket.S3UploadConcurrency == 0 {
53✔
605
                        item.Bucket.S3UploadConcurrency = DefaultS3UploadConcurrency
24✔
606
                }
24✔
607
                // Manage default s3 path-style addresing (nil = omited in config)
608
                if item.Bucket != nil && item.Bucket.S3ForcePathStyle == nil {
55✔
609
                        item.Bucket.S3ForcePathStyle = new(DefaultBucketS3ForcePathStyle)
26✔
610
                }
26✔
611
                // Manage default configuration for target actions
612
                if item.Actions == nil {
52✔
613
                        item.Actions = &ActionsConfig{GET: &GetActionConfig{Enabled: true}}
23✔
614
                }
23✔
615
                // Manage values for signed url
616
                if item.Actions != nil && item.Actions.GET != nil && item.Actions.GET.Config != nil {
31✔
617
                        // Check if expiration is set
2✔
618
                        if item.Actions.GET.Config.SignedURLExpirationString != "" {
3✔
619
                                // Parse it
1✔
620
                                dur, err := time.ParseDuration(item.Actions.GET.Config.SignedURLExpirationString)
1✔
621
                                // Check error
1✔
622
                                if err != nil {
1✔
UNCOV
623
                                        return errors.WithStack(err)
×
UNCOV
624
                                }
×
625
                                // Save
626
                                item.Actions.GET.Config.SignedURLExpiration = dur
1✔
627
                        } else {
1✔
628
                                // Set default one
1✔
629
                                item.Actions.GET.Config.SignedURLExpiration = DefaultTargetActionsGETConfigSignedURLExpiration
1✔
630
                        }
1✔
631
                }
632
                // Manage default for target templates configurations
633
                // Else put default headers for template override
634
                if item.Templates == nil {
51✔
635
                        item.Templates = &TargetTemplateConfig{}
22✔
636
                } else {
29✔
637
                        // Check if folder list template have been override and not headers
7✔
638
                        if item.Templates.FolderList != nil && item.Templates.FolderList.Headers == nil {
7✔
UNCOV
639
                                item.Templates.FolderList.Headers = DefaultTemplateHeaders
×
UNCOV
640
                        }
×
641
                        // Check if not found error template have been override and not headers
642
                        if item.Templates.NotFoundError != nil && item.Templates.NotFoundError.Headers == nil {
9✔
643
                                item.Templates.NotFoundError.Headers = DefaultTemplateHeaders
2✔
644
                        }
2✔
645
                        // Check if internal server error template have been override and not headers
646
                        if item.Templates.InternalServerError != nil && item.Templates.InternalServerError.Headers == nil {
9✔
647
                                item.Templates.InternalServerError.Headers = DefaultTemplateHeaders
2✔
648
                        }
2✔
649
                        // Check if forbidden error template have been override and not headers
650
                        if item.Templates.ForbiddenError != nil && item.Templates.ForbiddenError.Headers == nil {
9✔
651
                                item.Templates.ForbiddenError.Headers = DefaultTemplateHeaders
2✔
652
                        }
2✔
653
                        // Check if unauthorized error template have been override and not headers
654
                        if item.Templates.UnauthorizedError != nil && item.Templates.UnauthorizedError.Headers == nil {
9✔
655
                                item.Templates.UnauthorizedError.Headers = DefaultTemplateHeaders
2✔
656
                        }
2✔
657
                        // Check if bad request error template have been override and not headers
658
                        if item.Templates.BadRequestError != nil && item.Templates.BadRequestError.Headers == nil {
9✔
659
                                item.Templates.BadRequestError.Headers = DefaultTemplateHeaders
2✔
660
                        }
2✔
661

662
                        // Check if put template have been override and not headers
663
                        if item.Templates.Put != nil && item.Templates.Put.Headers == nil {
8✔
664
                                item.Templates.Put.Headers = DefaultEmptyTemplateHeaders
1✔
665
                        }
1✔
666

667
                        // Check if delete template have been override and not headers
668
                        if item.Templates.Delete != nil && item.Templates.Delete.Headers == nil {
8✔
669
                                item.Templates.Delete.Headers = DefaultEmptyTemplateHeaders
1✔
670
                        }
1✔
671
                }
672
                // Manage default value for resources methods
673
                if item.Resources != nil {
30✔
674
                        for _, res := range item.Resources {
2✔
675
                                // Load default resource values
1✔
676
                                err := loadResourceValues(res)
1✔
677
                                if err != nil {
1✔
UNCOV
678
                                        return err
×
UNCOV
679
                                }
×
680
                        }
681
                }
682
                // Manage key write list
683
                if item.KeyRewriteList != nil {
30✔
684
                        // Loop over keys
1✔
685
                        for _, it := range item.KeyRewriteList {
2✔
686
                                // Load key rewrite
1✔
687
                                err := loadKeyRewriteValues(it)
1✔
688
                                // Check error
1✔
689
                                if err != nil {
1✔
UNCOV
690
                                        return err
×
UNCOV
691
                                }
×
692
                        }
693
                }
694
        }
695

696
        // Manage default value for list targets resources
697
        if out.ListTargets != nil && out.ListTargets.Resource != nil {
41✔
698
                // Store resource object
3✔
699
                res := out.ListTargets.Resource
3✔
700

3✔
701
                // Load default resource values
3✔
702
                err := loadResourceValues(res)
3✔
703
                if err != nil {
3✔
UNCOV
704
                        return err
×
UNCOV
705
                }
×
706
        }
707

708
        // Manage default values for auth providers
709
        if out.AuthProviders != nil && out.AuthProviders.OIDC != nil {
40✔
710
                for k, v := range out.AuthProviders.OIDC {
4✔
711
                        // Manage default scopes
2✔
712
                        if len(v.Scopes) == 0 {
3✔
713
                                v.Scopes = DefaultOIDCScopes
1✔
714
                        }
1✔
715
                        // Manage default group claim
716
                        if v.GroupClaim == "" {
3✔
717
                                v.GroupClaim = DefaultOIDCGroupClaim
1✔
718
                        }
1✔
719
                        // Manage default oidc cookie name
720
                        if v.CookieName == "" {
3✔
721
                                v.CookieName = DefaultOIDCCookieName
1✔
722
                        }
1✔
723
                        // Check if login path is defined
724
                        if v.LoginPath == "" {
3✔
725
                                v.LoginPath = fmt.Sprintf(oidcLoginPathTemplate, k)
1✔
726
                        }
1✔
727
                        // Check if callback path is defined
728
                        if v.CallbackPath == "" {
3✔
729
                                v.CallbackPath = fmt.Sprintf(oidcCallbackPathTemplate, k)
1✔
730
                        }
1✔
731
                }
732
        }
733

734
        // Manage default value for list targets
735
        if out.ListTargets == nil {
70✔
736
                out.ListTargets = &ListTargetsConfig{Enabled: false}
32✔
737
        }
32✔
738

739
        // Manage default value for tracing
740
        if out.Tracing == nil {
74✔
741
                out.Tracing = &TracingConfig{Enabled: false}
36✔
742
        }
36✔
743

744
        // Manage default values for metrics configuration
745
        if out.Metrics == nil {
76✔
746
                out.Metrics = &MetricsConfig{}
38✔
747
        }
38✔
748

749
        return nil
38✔
750
}
751

752
func loadKeyRewriteValues(item *TargetKeyRewriteConfig) error {
1✔
753
        // Check if target type is set, if not, put REGEX type as default
1✔
754
        if item.TargetType == "" {
2✔
755
                item.TargetType = RegexTargetKeyRewriteTargetType
1✔
756
        }
1✔
757

758
        // Parse source regex
759
        rs, err := regexp.Compile(item.Source)
1✔
760
        // Check error
1✔
761
        if err != nil {
1✔
UNCOV
762
                return errors.WithStack(err)
×
UNCOV
763
        }
×
764
        // Save source
765
        item.SourceRegex = rs
1✔
766

1✔
767
        // Default value
1✔
768
        return nil
1✔
769
}
770

771
// Load Regex in OIDC Authorization access objects.
772
func loadRegexOIDCAuthorizationAccess(item *HeaderOIDCAuthorizationAccess) error {
3✔
773
        if item.Regexp {
6✔
774
                // Try to compile regex for group or email
3✔
775
                // Group case
3✔
776
                if item.Group != "" {
4✔
777
                        // Compile Regexp
1✔
778
                        reg, err2 := regexp.Compile(item.Group)
1✔
779
                        // Check error
1✔
780
                        if err2 != nil {
1✔
UNCOV
781
                                return errors.WithStack(err2)
×
UNCOV
782
                        }
×
783
                        // Save regexp
784
                        item.GroupRegexp = reg
1✔
785
                }
786

787
                // Email case
788
                if item.Email != "" {
5✔
789
                        // Compile regexp
2✔
790
                        reg, err2 := regexp.Compile(item.Email)
2✔
791
                        // Check error
2✔
792
                        if err2 != nil {
2✔
UNCOV
793
                                return errors.WithStack(err2)
×
UNCOV
794
                        }
×
795
                        // Save regexp
796
                        item.EmailRegexp = reg
2✔
797
                }
798
        }
799

800
        return nil
3✔
801
}
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

© 2026 Coveralls, Inc