polycule-connect/polyculeconnect/config/config.go
Melora Hugues f3060bee3b feat: start replacing dex with zitadel (#48)
Start the process of replacing dex with zitadel, this commit is
absolutely not prod-ready, basically we just added zitatel, and the
necessary elements to make it work to at least getting a client from the
DB

- replace logrus with zap
- start our own storage for the users
- instanciate zitaled on start
- allow getting client using the ID from the DB
2024-08-16 11:29:19 +02:00

225 lines
6 KiB
Go

package config
import (
"encoding/json"
"errors"
"fmt"
"io/fs"
"os"
"github.com/dexidp/dex/connector/oidc"
"github.com/kelseyhightower/envconfig"
"go.uber.org/zap"
)
const (
envConfigPrefix = "POLYCULECONNECT"
DefaultConfigPath = "/etc/polyculeconnect.json"
)
type ListeningMode string
const (
ModeUnix ListeningMode = "unix"
ModeNet ListeningMode = "net"
)
type BackendConfigType string
const (
Memory BackendConfigType = "memory"
SQLite BackendConfigType = "sqlite"
)
const (
defaultLogLevel = zap.InfoLevel
defaultServerMode = ModeNet
defaultServerHost = "0.0.0.0"
defaultServerPort = 5000
defaultServerSocket = ""
defaultServerStaticDir = "./"
defaultIssuer = "http://localhost:5000"
defaultStorageType = Memory
defaultStorageFile = "./polyculeconnect.db"
defaultStorageHost = "127.0.0.1"
defaultStoragePort = 5432
defaultStorageDB = "polyculeconnect"
defaultStorageUser = "polyculeconnect"
defaultStoragePassword = "polyculeconnect"
defaultStorageSSLMode = "disable"
defaultStorageSSLCaFile = ""
)
// Deprecated: remove when we finally drop the JSON config
type BackendConfig struct {
Config *oidc.Config `json:"config"`
Name string `json:"name"`
ID string `json:"ID"`
Type BackendConfigType `json:"type"`
Local bool `json:"local"`
}
type SSLStorageConfig struct {
Mode string `json:"mode" envconfig:"STORAGE_SSL_MODE"`
CaFile string `json:"ca_file" envconfig:"STORAGE_SSL_CA_FILE"`
}
type StorageConfig struct {
File string `envconfig:"STORAGE_PATH"`
Host string `envconfig:"STORAGE_HOST"`
Port int `envconfig:"STORAGE_PORT"`
Database string `envconfig:"STORAGE_DATABASE"`
User string `envconfig:"STORAGE_USER"`
Password string `envconfig:"STORAGE_PASSWORD"`
SSL SSLStorageConfig
}
type logConfig struct {
Level string `json:"level"`
}
type serverConfig struct {
Mode string `json:"mode"`
Host string `json:"host"`
Port int `json:"port"`
Sock string `json:"sock"`
}
type jsonStorageConfig struct {
Type string `json:"type"`
File string `json:"path"`
Host string `json:"host" `
Port int `json:"port"`
Database string `json:"database" `
User string `json:"user" `
Password string `json:"password" `
SSL SSLStorageConfig `json:"ssl"`
}
type jsonConf struct {
LogConfig logConfig `json:"log"`
ServerConfig serverConfig `json:"server"`
StorageConfig jsonStorageConfig `json:"storage"`
}
func (c *jsonConf) initValues(ac *AppConfig) {
c.LogConfig = logConfig{Level: ac.LogLevel.String()}
c.ServerConfig = serverConfig{
Mode: string(ac.ServerMode),
Host: ac.Host,
Port: ac.Port,
Sock: ac.SockPath,
}
c.StorageConfig = jsonStorageConfig{
Type: ac.StorageType,
File: ac.StorageConfig.File,
Host: ac.StorageConfig.Host,
Port: ac.StorageConfig.Port,
Database: ac.StorageConfig.Database,
User: ac.StorageConfig.User,
Password: ac.StorageConfig.Password,
SSL: ac.StorageConfig.SSL,
}
}
type AppConfig struct {
LogLevel zap.AtomicLevel `envconfig:"LOG_LEVEL"`
ServerMode ListeningMode `envconfig:"SERVER_MODE"`
Host string `envconfig:"SERVER_HOST"`
Port int `envconfig:"SERVER_PORT"`
SockPath string `envconfig:"SERVER_SOCK"`
StorageType string `envconfig:"STORAGE_TYPE"`
StorageConfig *StorageConfig
Issuer string
StaticDir string
}
func defaultConfig() AppConfig {
return AppConfig{
LogLevel: zap.NewAtomicLevelAt(defaultLogLevel),
ServerMode: defaultServerMode,
Host: defaultServerHost,
Port: defaultServerPort,
SockPath: defaultServerSocket,
StorageType: string(defaultStorageType),
StorageConfig: &StorageConfig{
File: defaultStorageFile,
Host: defaultStorageHost,
Port: defaultServerPort,
Database: defaultStorageDB,
User: defaultStorageUser,
Password: defaultStoragePassword,
SSL: SSLStorageConfig{
Mode: defaultStorageSSLMode,
CaFile: defaultStorageSSLCaFile,
},
},
Issuer: defaultIssuer,
StaticDir: defaultServerStaticDir,
}
}
func parseLevel(lvlStr string) zap.AtomicLevel {
var res zap.AtomicLevel
if err := res.UnmarshalText([]byte(lvlStr)); err != nil {
return zap.NewAtomicLevelAt(zap.InfoLevel)
}
return res
}
func (ac *AppConfig) UnmarshalJSON(data []byte) error {
var jsonConf jsonConf
jsonConf.initValues(ac)
if err := json.Unmarshal(data, &jsonConf); err != nil {
return fmt.Errorf("failed to read JSON: %w", err)
}
ac.LogLevel = parseLevel(jsonConf.LogConfig.Level)
ac.ServerMode = ListeningMode(jsonConf.ServerConfig.Mode)
ac.Host = jsonConf.ServerConfig.Host
ac.Port = jsonConf.ServerConfig.Port
ac.SockPath = jsonConf.ServerConfig.Sock
ac.StorageType = jsonConf.StorageConfig.Type
ac.StorageConfig.File = jsonConf.StorageConfig.File
ac.StorageConfig.Host = jsonConf.StorageConfig.Host
ac.StorageConfig.Port = jsonConf.StorageConfig.Port
ac.StorageConfig.Database = jsonConf.StorageConfig.Database
ac.StorageConfig.User = jsonConf.StorageConfig.User
ac.StorageConfig.Password = jsonConf.StorageConfig.Password
ac.StorageConfig.SSL = jsonConf.StorageConfig.SSL
return nil
}
func (ac *AppConfig) RedirectURI() string {
return ac.Issuer + "/callback"
}
func New(filepath string) (*AppConfig, error) {
conf := defaultConfig()
content, err := os.ReadFile(filepath)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("failed to read config file %q: %w", filepath, err)
}
} else {
if err := json.Unmarshal(content, &conf); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
}
if err := envconfig.Process(envConfigPrefix, &conf); err != nil {
return nil, err
}
if err := envconfig.Process(envConfigPrefix, conf.StorageConfig); err != nil {
return nil, err
}
if err := envconfig.Process(envConfigPrefix, &conf.StorageConfig.SSL); err != nil {
return nil, err
}
return &conf, nil
}