100 lines
2.5 KiB
Go
100 lines
2.5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/adrg/xdg"
|
|
toml "github.com/pelletier/go-toml/v2"
|
|
)
|
|
|
|
type Config struct {
|
|
Issuer string `toml:"issuer"`
|
|
ClientID string `toml:"client_id"`
|
|
ClientSecretFile string `toml:"client_secret_file,omitempty"`
|
|
CacheHost string `toml:"cache_host"`
|
|
NetrcPath string `toml:"netrc_path"`
|
|
|
|
// ClientSecret is populated at load time by reading ClientSecretFile.
|
|
ClientSecret string `toml:"-"`
|
|
}
|
|
|
|
// Load reads the config from the given path, or from the default XDG location.
|
|
func Load(path string) (*Config, error) {
|
|
if path == "" {
|
|
path = filepath.Join(xdg.ConfigHome, "nix-cache-login", "config.toml")
|
|
}
|
|
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading config file: %w", err)
|
|
}
|
|
|
|
var cfg Config
|
|
if err := toml.Unmarshal(data, &cfg); err != nil {
|
|
return nil, fmt.Errorf("parsing config file: %w", err)
|
|
}
|
|
|
|
cfg.NetrcPath = expandPath(cfg.NetrcPath)
|
|
cfg.ClientSecretFile = expandPath(cfg.ClientSecretFile)
|
|
|
|
if cfg.ClientSecretFile != "" {
|
|
secret, err := os.ReadFile(cfg.ClientSecretFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("reading client_secret_file: %w", err)
|
|
}
|
|
cfg.ClientSecret = strings.TrimSpace(string(secret))
|
|
}
|
|
|
|
if err := cfg.validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &cfg, nil
|
|
}
|
|
|
|
func (c *Config) validate() error {
|
|
if c.Issuer == "" {
|
|
return fmt.Errorf("config: issuer is required")
|
|
}
|
|
if c.ClientID == "" {
|
|
return fmt.Errorf("config: client_id is required")
|
|
}
|
|
if c.CacheHost == "" {
|
|
return fmt.Errorf("config: cache_host is required")
|
|
}
|
|
if c.NetrcPath == "" {
|
|
return fmt.Errorf("config: netrc_path is required")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// expandPath expands environment variables in a path. XDG base directory
|
|
// variables are resolved using the xdg library, which applies the XDG spec
|
|
// fallbacks (e.g. $HOME/.config when $XDG_CONFIG_HOME is unset). This ensures
|
|
// correct behaviour in systemd user services, which do not set XDG variables.
|
|
func expandPath(s string) string {
|
|
return os.Expand(s, func(key string) string {
|
|
switch key {
|
|
case "XDG_CONFIG_HOME":
|
|
return xdg.ConfigHome
|
|
case "XDG_DATA_HOME":
|
|
return xdg.DataHome
|
|
case "XDG_CACHE_HOME":
|
|
return xdg.CacheHome
|
|
case "XDG_STATE_HOME":
|
|
return xdg.StateHome
|
|
case "XDG_RUNTIME_DIR":
|
|
return xdg.RuntimeDir
|
|
default:
|
|
return os.Getenv(key)
|
|
}
|
|
})
|
|
}
|
|
|
|
// RefreshTokenPath returns the path to the stored refresh token.
|
|
func RefreshTokenPath() string {
|
|
return filepath.Join(xdg.ConfigHome, "nix-cache-login", "refresh-token")
|
|
}
|