Melora Hugues
ce8bd4ee7c
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
225 lines
6 KiB
Go
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
|
|
}
|