Skip to content

Commit

Permalink
config: allow helper-specific configuration values to be passed down …
Browse files Browse the repository at this point in the history
…using context object
  • Loading branch information
malt3 committed Feb 20, 2025
1 parent c04de09 commit 0cc3207
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 18 deletions.
5 changes: 5 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ const (
PlaceholderHomedir = "~"
)

// HelperConfigKey is the key used to store the helper configuration in the context (context.Context) as []byte.
// The encoding is expected to be json.
// The schema of the configuration is defined by the helper.
const HelperConfigKey = "helper-config"

// HelperFactory chooses a credential helper (like s3, gcs, github, ...) based on the raw uri.
type HelperFactory func(string) (Helper, error)

Expand Down
9 changes: 9 additions & 0 deletions authenticate/internal/helperconfig/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@rules_go//go:def.bzl", "go_library")

go_library(
name = "helperconfig",
srcs = ["helperconfig.go"],
importpath = "github.com/tweag/credential-helper/authenticate/internal/helperconfig",
visibility = ["//authenticate:__subpackages__"],
deps = ["//api"],
)
21 changes: 21 additions & 0 deletions authenticate/internal/helperconfig/helperconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package helperconfig

import (
"bytes"
"context"
"encoding/json"

"github.com/tweag/credential-helper/api"
)

func FromContext[T any](ctx context.Context, defaultConfig T) (T, error) {
var config T
rawConfig, ok := ctx.Value(api.HelperConfigKey).([]byte)
if !ok {
return defaultConfig, nil
}
decoder := json.NewDecoder(bytes.NewReader(rawConfig))
decoder.DisallowUnknownFields()
err := decoder.Decode(&config)
return config, err
}
9 changes: 8 additions & 1 deletion cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,14 @@ func foreground(ctx context.Context, helperFactory api.HelperFactory, cache api.
cfg, err := configReader.Read()
if err == nil {
helperFactory = func(uri string) (api.Helper, error) {
return cfg.FindHelper(uri)
helper, helperConfig, err := cfg.FindHelper(uri)
if err != nil {
return nil, err
}
if len(helperConfig) > 0 {
ctx = context.WithValue(ctx, api.HelperConfigKey, helperConfig)
}
return helper, nil
}
} else if err != config.ErrConfigNotFound {
logging.Fatalf("reading config: %v", err)
Expand Down
1 change: 0 additions & 1 deletion config/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ go_library(
deps = [
"//agent/locate",
"//api",
"//authenticate/null",
"//helperfactory/string",
],
)
Expand Down
38 changes: 22 additions & 16 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,57 @@ import (

"github.com/tweag/credential-helper/agent/locate"
"github.com/tweag/credential-helper/api"
authenticateNull "github.com/tweag/credential-helper/authenticate/null"
helperstringfactory "github.com/tweag/credential-helper/helperfactory/string"
)

var ErrConfigNotFound = errors.New("config file not found")

type URLConfig struct {
Scheme string `json:"scheme,omitempty"`
Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"`
Helper string `json:"helper"`
Scheme string `json:"scheme,omitempty"`
Host string `json:"host,omitempty"`
Path string `json:"path,omitempty"`
Helper string `json:"helper"`
Config json.RawMessage `json:"config,omitempty"` // the schema of this field is defined by the helper
}

type Config struct {
URLs []URLConfig `json:"urls,omitempty"`
}

func (c Config) FindHelper(uri string) (api.Helper, error) {
func (c Config) FindHelper(uri string) (api.Helper, []byte, error) {
requested, err := url.Parse(uri)
if err != nil {
return nil, err
return nil, nil, err
}
if len(c.URLs) == 0 {
return nil, errors.New("invalid configuration file: no helpers configured")
return nil, nil, errors.New("invalid configuration file: no helpers configured")
}
for _, url := range c.URLs {
for _, urlConfig := range c.URLs {
if len(urlConfig.Helper) == 0 {
return nil, nil, errors.New("invalid configuration file: helper field is required")
}

// if a scheme is specified, it must match
if len(url.Scheme) > 0 && url.Scheme != requested.Scheme {
if len(urlConfig.Scheme) > 0 && urlConfig.Scheme != requested.Scheme {
continue
}
// if a host is specified, it must glob match
if len(url.Host) > 0 && !globMatch(url.Host, requested.Host) {
if len(urlConfig.Host) > 0 && !globMatch(urlConfig.Host, requested.Host) {
continue
}
// if a path is specified, it must glob match
if len(url.Path) > 0 && !globMatch(url.Path, requested.Path) {
if len(urlConfig.Path) > 0 && !globMatch(urlConfig.Path, requested.Path) {
continue
}
helper := helperstringfactory.HelperFromString(url.Helper)
helper := helperstringfactory.HelperFromString(urlConfig.Helper)
if helper != nil {
return helper, nil
return helper, urlConfig.Config, nil
}
return nil, fmt.Errorf("unknown helper: %s", url.Helper)
return nil, nil, fmt.Errorf("unknown helper: %s", urlConfig.Helper)
}
return &authenticateNull.Null{}, nil
// this is equivalent to null.Null{}
// but avoids the import of the null package
return helperstringfactory.HelperFromString("null"), nil, nil
}

type ConfigReader interface {
Expand Down

0 comments on commit 0cc3207

Please sign in to comment.