Skip to content

Commit 46c4663

Browse files
feat: config rework (#2)
1 parent 6c4ab42 commit 46c4663

23 files changed

+436
-207
lines changed

config.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package main
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"os"
7+
"path"
8+
"path/filepath"
9+
10+
"github.com/spf13/cobra"
11+
12+
"authelia.com/tools/ac/consts"
13+
)
14+
15+
func newConfigCmd(ctx *cmdctx) (cmd *cobra.Command) {
16+
cmd = &cobra.Command{
17+
Use: "config",
18+
Short: "Perform Config operations",
19+
}
20+
21+
cmd.AddCommand(newConfigGenerateCmd(ctx))
22+
23+
return cmd
24+
}
25+
26+
func newConfigGenerateCmd(ctx *cmdctx) (cmd *cobra.Command) {
27+
cmd = &cobra.Command{
28+
Use: "generate [path]",
29+
Short: "Generate a config from the templates",
30+
Args: cobra.MaximumNArgs(1),
31+
RunE: ctx.handleConfigGenerateRunE,
32+
}
33+
34+
return cmd
35+
}
36+
37+
func (ctx *cmdctx) handleConfigGenerateRunE(cmd *cobra.Command, args []string) (err error) {
38+
var pathOut string
39+
40+
if pathOut, err = ctx.getConfigPathSingle(cmd, args); err != nil {
41+
switch {
42+
case errors.Is(err, errConfigNoPath), errors.Is(err, errConfigPathMoreThanOne):
43+
return fmt.Errorf("error generating config: %w: specifying a path argument will avoid this error", err)
44+
default:
45+
return err
46+
}
47+
}
48+
49+
var (
50+
f *os.File
51+
data []byte
52+
)
53+
54+
switch ext := filepath.Ext(pathOut); ext {
55+
case ".json":
56+
if data, err = internalFS.ReadFile(path.Join("embed", "config", "config.json")); err != nil {
57+
return err
58+
}
59+
case ".yml", ".yaml":
60+
if data, err = internalFS.ReadFile(path.Join("embed", "config", "config.yaml")); err != nil {
61+
return err
62+
}
63+
case ".tml", ".toml":
64+
if data, err = internalFS.ReadFile(path.Join("embed", "config", "config.toml")); err != nil {
65+
return err
66+
}
67+
default:
68+
return fmt.Errorf("error generating config: extension '%s' for file '%s' is not supported", ext, pathOut)
69+
}
70+
71+
if f, err = os.OpenFile(pathOut, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0640); err != nil {
72+
return err
73+
}
74+
75+
defer f.Close()
76+
77+
if _, err = f.Write(data); err != nil {
78+
return err
79+
}
80+
81+
return nil
82+
}
83+
84+
func (ctx *cmdctx) getConfigPathSingle(cmd *cobra.Command, args []string) (path string, err error) {
85+
if len(args) == 1 {
86+
return args[0], nil
87+
}
88+
89+
var configs []string
90+
91+
if configs, err = cmd.Flags().GetStringSlice(consts.Config); err != nil {
92+
return "", err
93+
}
94+
95+
switch len(configs) {
96+
case 0:
97+
return "", errConfigNoPath
98+
case 1:
99+
return configs[0], nil
100+
default:
101+
return "", errConfigPathMoreThanOne
102+
}
103+
}
104+
105+
var (
106+
errConfigPathMoreThanOne = fmt.Errorf("error determining config output path: nore than one config path specified")
107+
errConfigNoPath = fmt.Errorf("error determining config output path: no config paths specified")
108+
)

config/config.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
"path/filepath"
6+
7+
"github.com/knadh/koanf/parsers/toml"
8+
"github.com/knadh/koanf/parsers/yaml"
9+
"github.com/knadh/koanf/providers/confmap"
10+
"github.com/knadh/koanf/providers/file"
11+
"github.com/knadh/koanf/providers/posflag"
12+
"github.com/knadh/koanf/v2"
13+
"github.com/spf13/pflag"
14+
)
15+
16+
func Load(paths []string, flags *pflag.FlagSet, flagsPrefix string) (config *Configuration, err error) {
17+
ko := koanf.New(".")
18+
19+
kdefault := confmap.Provider(defaultConfig, ".")
20+
21+
if err = ko.Load(kdefault, nil); err != nil {
22+
return nil, fmt.Errorf("error occurred loading default configuration: %w", err)
23+
}
24+
25+
for _, path := range paths {
26+
provider := file.Provider(path)
27+
28+
switch ext := filepath.Ext(path); ext {
29+
case ".yaml", ".yml", ".json":
30+
if err = ko.Load(provider, yaml.Parser()); err != nil {
31+
return nil, fmt.Errorf("error occurred loading file configuration: %w", err)
32+
}
33+
case ".tml", ".toml":
34+
if err = ko.Load(provider, toml.Parser()); err != nil {
35+
return nil, fmt.Errorf("error occurred loading file configuration: %w", err)
36+
}
37+
default:
38+
return nil, fmt.Errorf("error occurred loading file configuration: extension '%s' for file '%s' is not supported", ext, path)
39+
}
40+
}
41+
42+
kflags := posflag.ProviderWithFlag(flags, ".", ko, configCallbackPosFlag(flagsPrefix))
43+
44+
if err = ko.Load(kflags, nil); err != nil {
45+
return nil, fmt.Errorf("error occurred loading flags configuration: %w", err)
46+
}
47+
48+
config = &Configuration{}
49+
50+
if err = Unmarshal(ko, config); err != nil {
51+
return nil, fmt.Errorf("error occurred unmarshalling configuration: %w", err)
52+
}
53+
54+
return config, nil
55+
}
56+
57+
var (
58+
defaultConfig = map[string]any{
59+
"server.port": 9019,
60+
"storage": "storage.yml",
61+
}
62+
)

config/const.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package config
2+
3+
const (
4+
errFmtRequiredOptionOAuth2Bearer = "error validating configuration: oauth2: bearer: the '%s' value is required"
5+
errFmtNotKnownOptionOAuth2Bearer = "error validating configuration: oauth2: bearer: the '%s' value of '%s' is not known"
6+
)

config_hook.go renamed to config/hook.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
package main
1+
package config
22

33
import (
44
"fmt"
55
"net/url"
66
"reflect"
77
"strings"
88

9-
"github.com/mitchellh/mapstructure"
9+
"github.com/go-viper/mapstructure/v2"
1010
"github.com/spf13/pflag"
1111
)
1212

koanf.go renamed to config/koanf.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
package main
1+
package config
22

33
import (
4+
"github.com/go-viper/mapstructure/v2"
45
"github.com/knadh/koanf/v2"
5-
"github.com/mitchellh/mapstructure"
66
)
77

8-
func koUnmarshal(ko *koanf.Koanf, data any) (err error) {
8+
func Unmarshal(ko *koanf.Koanf, data any) (err error) {
99
c := koanf.UnmarshalConf{
1010
DecoderConfig: &mapstructure.DecoderConfig{
1111
DecodeHook: mapstructure.ComposeDecodeHookFunc(

config_schema.go renamed to config/schema.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1-
package main
1+
package config
22

33
import (
44
"net/url"
55
)
66

7-
type Schema struct {
8-
AutheliaURL *url.URL `koanf:"authelia_url"`
9-
Storage string `koanf:"storage"`
10-
OAuth2 SchemaOAuth2 `koanf:"oauth2"`
7+
type Configuration struct {
8+
AutheliaURL *url.URL `koanf:"authelia_url"`
9+
Storage string `koanf:"storage"`
10+
OAuth2 OAuth2 `koanf:"oauth2"`
11+
Server Server `koanf:"server"`
1112
}
1213

13-
type SchemaOAuth2 struct {
14-
Bearer SchemaOAuth2Bearer `koanf:"bearer"`
14+
type Server struct {
15+
Port uint16 `koanf:"port"`
1516
}
1617

17-
type SchemaOAuth2Bearer struct {
18+
type OAuth2 struct {
19+
Bearer OAuth2Bearer `koanf:"bearer"`
20+
}
21+
22+
type OAuth2Bearer struct {
1823
ID string `koanf:"id"`
1924
Secret string `koanf:"secret"`
2025
PAR bool `koanf:"par"`

config/validate.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
6+
"authelia.com/tools/ac/consts"
7+
)
8+
9+
func (config *Configuration) Validate() (err error) {
10+
if config.AutheliaURL == nil {
11+
return fmt.Errorf("error validating configuration: the 'authelia_url' value is required")
12+
}
13+
14+
if len(config.Storage) == 0 {
15+
return fmt.Errorf("error validating configuration: the 'storage' value is required")
16+
}
17+
18+
return nil
19+
}
20+
21+
func (config *OAuth2Bearer) Validate() (err error) {
22+
if len(config.ID) == 0 {
23+
return fmt.Errorf(errFmtRequiredOptionOAuth2Bearer, consts.ID)
24+
}
25+
26+
if len(config.Secret) == 0 {
27+
return fmt.Errorf(errFmtRequiredOptionOAuth2Bearer, consts.Secret)
28+
}
29+
30+
if len(config.Scope) == 0 {
31+
return fmt.Errorf(errFmtRequiredOptionOAuth2Bearer, consts.Scope)
32+
}
33+
34+
switch config.GrantType {
35+
case "":
36+
return fmt.Errorf(errFmtRequiredOptionOAuth2Bearer, consts.GrantType)
37+
case consts.AuthorizationCode, consts.RefreshToken, consts.ClientCredentials:
38+
break
39+
default:
40+
return fmt.Errorf(errFmtNotKnownOptionOAuth2Bearer, consts.GrantType, config.GrantType)
41+
}
42+
43+
switch config.TokenEndpointAuthMethod {
44+
case "":
45+
return fmt.Errorf(errFmtRequiredOptionOAuth2Bearer, consts.TokenEndpointAuthMethod)
46+
case consts.ClientSecretPost, consts.ClientSecretBasic:
47+
break
48+
default:
49+
return fmt.Errorf(errFmtNotKnownOptionOAuth2Bearer, consts.TokenEndpointAuthMethod, config.TokenEndpointAuthMethod)
50+
}
51+
52+
return nil
53+
}

config_load.go

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)