Skip to content

Commit ad7c084

Browse files
committed
added token configs
* removed `api.token` in favor of `api.tokens` * added configs for tokens to override settings from main config
1 parent 05370ba commit ad7c084

File tree

5 files changed

+241
-128
lines changed

5 files changed

+241
-128
lines changed

.github/templates/README.template.md

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Get the latest version of the `docker-compose.yaml` file:
1010
{ { file.docker-compose.yaml } }
1111
```
1212

13-
And add secure Token(s) to `api.tokens`. See [API TOKEN(s)](#api-tokens).
13+
And add secure Token(s) to `api.tokens`. See [API TOKENs](#api-tokens).
1414

1515
> [!IMPORTANT]
1616
> This Documentation will be using `sec-signal-api:8880` as the service host,
@@ -130,7 +130,7 @@ Supported types include **strings**, **ints** and **arrays**. See [Formatting](#
130130

131131
There are multiple ways to configure Secured Signal API, you can optionally use `config.yml` aswell as Environment Variables to override the config.
132132

133-
### Config File
133+
### Config Files
134134

135135
Config files allow **YML** formatting and also `${ENV}` to get Environment Variables.
136136

@@ -142,6 +142,18 @@ This example config shows all of the individual settings that can be applied:
142142
{ { file.examples/config.yml } }
143143
```
144144

145+
#### Token Configs
146+
147+
You can also override the `config.yml` file for each individual token by adding configs under `TOKENS_PATH` (default: `config/tokens/`)
148+
149+
This way you can permission tokens by further restricting or adding [Endpoints](#blocked-endpoints), [Placeholders](#variables), etc.
150+
151+
Here is an example:
152+
153+
```yaml
154+
{ { file.examples/token.yml } }
155+
```
156+
145157
### Environment
146158

147159
Suppose you want to set a new [Placeholder](#placeholders) `NUMBER` in your Environment...
@@ -182,12 +194,8 @@ If you are using Environment Variables as an example you won't be able to specif
182194

183195
During Authentication Secured Signal API will try to match the given Token against the list of Tokens inside of these Variables.
184196

185-
> [!NOTE]
186-
> Both `api.token` and `api.tokens` support multiple Tokens.
187-
188197
```yaml
189198
api:
190-
token: [token1, token2, token3]
191199
tokens: [token1, token2, token3]
192200
```
193201

examples/token.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
token: LOOOONG_STRING
2+
3+
overrides:
4+
variables: # Disable Placeholder
5+
blockedEndpoints: # Disable Sending
6+
- /v2/send
7+
messageAliases: # Disable Aliases

utils/config/config.go

Lines changed: 39 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1,161 +1,92 @@
11
package config
22

33
import (
4-
"errors"
5-
"io/fs"
64
"os"
5+
"path/filepath"
76
"strconv"
87
"strings"
98
"sync"
109

11-
middlewares "github.com/codeshelldev/secured-signal-api/internals/proxy/middlewares"
12-
"github.com/codeshelldev/secured-signal-api/utils"
1310
log "github.com/codeshelldev/secured-signal-api/utils/logger"
1411
"github.com/codeshelldev/secured-signal-api/utils/safestrings"
1512

16-
"github.com/knadh/koanf/parsers/yaml"
1713
"github.com/knadh/koanf/providers/confmap"
1814
"github.com/knadh/koanf/providers/env/v2"
1915
"github.com/knadh/koanf/providers/file"
2016
"github.com/knadh/koanf/v2"
2117
)
2218

23-
type ENV_ struct {
24-
CONFIG_PATH string
25-
DEFAULTS_PATH string
26-
TOKENS_DIR string
27-
LOG_LEVEL string
28-
PORT string
29-
API_URL string
30-
API_TOKENS []string
31-
INSECURE bool
32-
BLOCKED_ENDPOINTS []string
33-
VARIABLES map[string]any
34-
MESSAGE_ALIASES []middlewares.MessageAlias
35-
}
36-
37-
var ENV ENV_ = ENV_{
38-
CONFIG_PATH: os.Getenv("CONFIG_PATH"),
39-
DEFAULTS_PATH: os.Getenv("DEFAULTS_PATH"),
40-
TOKENS_DIR: os.Getenv("TOKENS_DIR"),
41-
API_TOKENS: []string{},
42-
BLOCKED_ENDPOINTS: []string{},
43-
MESSAGE_ALIASES: []middlewares.MessageAlias{},
44-
VARIABLES: map[string]any{},
45-
INSECURE: false,
46-
}
47-
4819
var defaultsLayer = koanf.New(".")
4920
var userLayer = koanf.New(".")
21+
var tokensLayer = koanf.New(".")
5022

5123
var config *koanf.Koanf
5224

5325
var configLock sync.Mutex
5426

55-
func InitEnv() {
56-
ENV.PORT = strconv.Itoa(config.Int("server.port"))
57-
58-
ENV.LOG_LEVEL = config.String("loglevel")
59-
60-
ENV.API_URL = config.String("api.url")
61-
62-
apiTokens := config.Strings("api.tokens")
63-
64-
if len(apiTokens) <= 0 {
65-
apiTokens = config.Strings("api.token")
66-
67-
if len(apiTokens) <= 0 {
68-
log.Warn("No API TOKEN provided this is NOT recommended")
27+
func LoadFile(path string, config *koanf.Koanf, parser koanf.Parser) (koanf.Provider, error) {
28+
f := file.Provider(path)
6929

70-
log.Info("Disabling Security Features due to incomplete Congfiguration")
30+
err := config.Load(f, parser)
7131

72-
ENV.INSECURE = true
32+
if err != nil {
33+
return nil, err
34+
}
7335

74-
// Set Blocked Endpoints on Config to User Layer Value
75-
// => effectively ignoring Default Layer
76-
config.Set("blockedendpoints", userLayer.Strings("blockeendpoints"))
36+
f.Watch(func(event any, err error) {
37+
if err != nil {
38+
return
7739
}
78-
}
7940

80-
if len(apiTokens) > 0 {
81-
log.Debug("Registered " + strconv.Itoa(len(apiTokens)) + " Tokens")
41+
log.Info(path, " changed, Reloading...")
8242

83-
ENV.API_TOKENS = apiTokens
84-
}
85-
86-
config.Unmarshal("messagealiases", &ENV.MESSAGE_ALIASES)
43+
configLock.Lock()
44+
defer configLock.Unlock()
8745

88-
transformChildren(config, "variables", func(key string, value any) (string, any) {
89-
return strings.ToUpper(key), value
46+
Load()
9047
})
9148

92-
config.Unmarshal("variables", &ENV.VARIABLES)
93-
94-
ENV.BLOCKED_ENDPOINTS = config.Strings("blockedendpoints")
49+
return f, err
9550
}
9651

97-
func Load() {
98-
log.Debug("Loading Config ", ENV.DEFAULTS_PATH)
99-
100-
_, defErr := LoadFile(ENV.DEFAULTS_PATH, defaultsLayer, yaml.Parser())
52+
func LoadDir(path string, dir string, config *koanf.Koanf, parser koanf.Parser) error {
53+
files, err := filepath.Glob(filepath.Join(dir, "*.yml"))
10154

102-
if defErr != nil {
103-
log.Warn("Could not Load Defaults", ENV.DEFAULTS_PATH)
104-
}
105-
106-
log.Debug("Loading Config ", ENV.CONFIG_PATH)
55+
if err != nil {
56+
return err
57+
}
10758

108-
_, conErr := LoadFile(ENV.CONFIG_PATH, userLayer, yaml.Parser())
59+
for i, file := range files {
60+
tmp := koanf.New(".")
10961

110-
if conErr != nil {
111-
_, err := os.Stat(ENV.CONFIG_PATH)
62+
_, err := LoadFile(file, tmp, parser)
11263

113-
if !errors.Is(err, fs.ErrNotExist) {
114-
log.Error("Could not Load Config ", ENV.CONFIG_PATH, ": ", conErr.Error())
64+
if err != nil {
65+
return err
11566
}
116-
}
117-
118-
log.Debug("Loading DotEnv")
119-
120-
LoadEnv(userLayer)
121-
122-
config = mergeLayers()
12367

124-
normalizeKeys(config)
125-
126-
templateConfig(config)
127-
128-
InitEnv()
129-
130-
log.Info("Finished Loading Configuration")
68+
config.Set(path + "." + strconv.Itoa(i), tmp.All())
69+
}
13170

132-
log.Dev(utils.ToJson(config.All()))
133-
log.Dev(utils.ToJson(ENV))
71+
return nil
13472
}
13573

136-
func LoadFile(path string, config *koanf.Koanf, parser koanf.Parser) (koanf.Provider, error) {
137-
f := file.Provider(path)
74+
func LoadEnv(config *koanf.Koanf) (koanf.Provider, error) {
75+
e := env.Provider(".", env.Opt{
76+
TransformFunc: normalizeEnv,
77+
})
13878

139-
err := config.Load(f, parser)
79+
err := config.Load(e, nil)
14080

14181
if err != nil {
142-
return nil, err
82+
log.Fatal("Error loading env: ", err.Error())
14383
}
14484

145-
f.Watch(func(event any, err error) {
146-
if err != nil {
147-
return
148-
}
149-
150-
log.Info("Config changed, Reloading...")
151-
152-
configLock.Lock()
153-
defer configLock.Unlock()
154-
155-
Load()
156-
})
85+
return e, err
86+
}
15787

158-
return f, err
88+
func mergeConfig(path string, mergeInto *koanf.Koanf, mergeFrom *koanf.Koanf) {
89+
mergeInto.MergeAt(mergeFrom, path)
15990
}
16091

16192
func templateConfig(config *koanf.Koanf) {
@@ -176,20 +107,6 @@ func templateConfig(config *koanf.Koanf) {
176107
config.Load(confmap.Provider(data, "."), nil)
177108
}
178109

179-
func LoadEnv(config *koanf.Koanf) (koanf.Provider, error) {
180-
e := env.Provider(".", env.Opt{
181-
TransformFunc: normalizeEnv,
182-
})
183-
184-
err := config.Load(e, nil)
185-
186-
if err != nil {
187-
log.Fatal("Error loading env: ", err.Error())
188-
}
189-
190-
return e, err
191-
}
192-
193110
func mergeLayers() *koanf.Koanf {
194111
final := koanf.New(".")
195112

utils/config/loader.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package config
2+
3+
import (
4+
"errors"
5+
"io/fs"
6+
"os"
7+
"strconv"
8+
"strings"
9+
10+
"github.com/codeshelldev/secured-signal-api/internals/proxy/middlewares"
11+
"github.com/codeshelldev/secured-signal-api/utils"
12+
log "github.com/codeshelldev/secured-signal-api/utils/logger"
13+
"github.com/knadh/koanf/parsers/yaml"
14+
)
15+
16+
type ENV_ struct {
17+
CONFIG_PATH string
18+
DEFAULTS_PATH string
19+
TOKENS_DIR string
20+
LOG_LEVEL string
21+
PORT string
22+
API_URL string
23+
API_TOKENS []string
24+
SETTINGS map[string]*SETTING_
25+
INSECURE bool
26+
}
27+
28+
type SETTING_ struct {
29+
BLOCKED_ENDPOINTS []string
30+
VARIABLES map[string]any
31+
MESSAGE_ALIASES []middlewares.MessageAlias
32+
}
33+
34+
var ENV ENV_ = ENV_{
35+
CONFIG_PATH: os.Getenv("CONFIG_PATH"),
36+
DEFAULTS_PATH: os.Getenv("DEFAULTS_PATH"),
37+
TOKENS_DIR: os.Getenv("TOKENS_DIR"),
38+
API_TOKENS: []string{},
39+
SETTINGS: map[string]*SETTING_{
40+
"*": &SETTING_{
41+
BLOCKED_ENDPOINTS: []string{},
42+
MESSAGE_ALIASES: []middlewares.MessageAlias{},
43+
VARIABLES: map[string]any{},
44+
},
45+
},
46+
INSECURE: false,
47+
}
48+
49+
func InitEnv() {
50+
ENV.PORT = strconv.Itoa(config.Int("server.port"))
51+
52+
ENV.LOG_LEVEL = config.String("loglevel")
53+
54+
ENV.API_URL = config.String("api.url")
55+
56+
InitTokens()
57+
58+
defaultSettings := ENV.SETTINGS["*"]
59+
60+
config.Unmarshal("messagealiases", &defaultSettings.MESSAGE_ALIASES)
61+
62+
transformChildren(config, "variables", func(key string, value any) (string, any) {
63+
return strings.ToUpper(key), value
64+
})
65+
66+
config.Unmarshal("variables", &defaultSettings.VARIABLES)
67+
68+
defaultSettings.BLOCKED_ENDPOINTS = config.Strings("blockedendpoints")
69+
}
70+
71+
func Load() {
72+
LoadDefaults()
73+
74+
LoadConfig()
75+
76+
LoadTokens()
77+
78+
log.Debug("Loading DotEnv")
79+
80+
LoadEnv(userLayer)
81+
82+
config = mergeLayers()
83+
84+
normalizeKeys(config)
85+
86+
templateConfig(config)
87+
88+
InitEnv()
89+
90+
log.Info("Finished Loading Configuration")
91+
92+
log.Dev(utils.ToJson(config.All()))
93+
log.Dev(utils.ToJson(ENV))
94+
}
95+
96+
func LoadDefaults() {
97+
log.Debug("Loading Config ", ENV.DEFAULTS_PATH)
98+
99+
_, defErr := LoadFile(ENV.DEFAULTS_PATH, defaultsLayer, yaml.Parser())
100+
101+
if defErr != nil {
102+
log.Warn("Could not Load Defaults", ENV.DEFAULTS_PATH)
103+
}
104+
}
105+
106+
func LoadConfig() {
107+
log.Debug("Loading Config ", ENV.CONFIG_PATH)
108+
109+
_, conErr := LoadFile(ENV.CONFIG_PATH, userLayer, yaml.Parser())
110+
111+
if conErr != nil {
112+
_, err := os.Stat(ENV.CONFIG_PATH)
113+
114+
if !errors.Is(err, fs.ErrNotExist) {
115+
log.Error("Could not Load Config ", ENV.CONFIG_PATH, ": ", conErr.Error())
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)