Compare commits
2 commits
0083624204
...
aa2a4d42d8
Author | SHA1 | Date | |
---|---|---|---|
aa2a4d42d8 | |||
2404156e91 |
8 changed files with 217 additions and 304 deletions
|
@ -3,7 +3,7 @@ ARG TARGETPLATFORM
|
||||||
ARG BUILDPLATFORM
|
ARG BUILDPLATFORM
|
||||||
WORKDIR /go/src/git.faercol.me/polyculeconnect
|
WORKDIR /go/src/git.faercol.me/polyculeconnect
|
||||||
COPY polyculeconnect ./
|
COPY polyculeconnect ./
|
||||||
RUN make build
|
RUN CGO_ENABLED=0 make build
|
||||||
|
|
||||||
# Replace with from scratch later on
|
# Replace with from scratch later on
|
||||||
FROM --platform=$TARGETPLATFORM alpine:latest
|
FROM --platform=$TARGETPLATFORM alpine:latest
|
||||||
|
|
43
README.md
43
README.md
|
@ -10,13 +10,21 @@ TODO
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
As a temporary solution, the list of backends and applications, as well as the openconnect configuration
|
Here is an example config file
|
||||||
can only be handled through the JSON config file.
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"log": {
|
||||||
|
"level": "debug" // debug,info,warn,error
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"port": 5000, // only used in net mode
|
||||||
|
"host": "0.0.0.0", // only used in net mode
|
||||||
|
// "sock": "/your/sock.sock" // path to your unix sock if "mode" is set to "unix"
|
||||||
|
"mode": "net" // net,unix
|
||||||
|
},
|
||||||
"openconnect": {
|
"openconnect": {
|
||||||
"issuer": "https://polyculeconnect.domain",
|
"issuer": "https://polyculeconnect.domain", // hostname of your polyculeconnect server
|
||||||
"clients": [
|
"clients": [
|
||||||
{
|
{
|
||||||
"name": "<name>",
|
"name": "<name>",
|
||||||
|
@ -32,42 +40,19 @@ can only be handled through the JSON config file.
|
||||||
"id": "<unique_id>",
|
"id": "<unique_id>",
|
||||||
"name": "<human_readable_name>",
|
"name": "<human_readable_name>",
|
||||||
"local": true,
|
"local": true,
|
||||||
"type": "oidc",
|
"type": "oidc", // must be "oidc" for now
|
||||||
"config": {
|
"config": {
|
||||||
"issuer": "https://polyculeconnect.domain",
|
"issuer": "https://polyculeconnect.domain", // must be the same as current issuer
|
||||||
"clientID": "<client_id>",
|
"clientID": "<client_id>",
|
||||||
"clientSecret": "<client_secret>",
|
"clientSecret": "<client_secret>",
|
||||||
"redirectURI": "<redirect_uri>"
|
"redirectURI": "<redirect_uri>"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The rest of the configuration is handled through environment variables
|
|
||||||
|
|
||||||
```ini
|
|
||||||
# Can be debug,info,warning,error
|
|
||||||
LOG_LEVEL = "info"
|
|
||||||
|
|
||||||
# Can be net,unix
|
|
||||||
SERVER_MODE = "net"
|
|
||||||
SERVER_HOST = "0.0.0.0"
|
|
||||||
SERVER_PORT = "5000"
|
|
||||||
# SERVER_SOCK_PATH = ""
|
|
||||||
|
|
||||||
STORAGE_TYPE = "sqlite"
|
|
||||||
STORAGE_FILEPATH = "./build/polyculeconnect.db"
|
|
||||||
# STORAGE_HOST = "127.0.0.1"
|
|
||||||
# STORAGE_PORT = "5432"
|
|
||||||
# STORAGE_DB = "polyculeconnect"
|
|
||||||
# STORAGE_USER = "polyculeconnect"
|
|
||||||
# STORAGE_PASSWORD = "polyculeconnect"
|
|
||||||
# STORAGE_SSL_MODE = "disable"
|
|
||||||
# STORAGE_SSL_CA_FILE = ""
|
|
||||||
```
|
|
||||||
|
|
||||||
You can register multiple backend and multiple clients (applications)
|
You can register multiple backend and multiple clients (applications)
|
||||||
|
|
||||||
## Running the server
|
## Running the server
|
||||||
|
|
|
@ -12,32 +12,32 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type envVar string
|
type ListeningMode int64
|
||||||
|
|
||||||
|
func (lm ListeningMode) String() string {
|
||||||
|
mapping := map[ListeningMode]string{
|
||||||
|
ModeNet: "net",
|
||||||
|
ModeUnix: "unix",
|
||||||
|
}
|
||||||
|
val := mapping[lm]
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func listeningModeFromString(rawVal string) (ListeningMode, error) {
|
||||||
|
mapping := map[string]ListeningMode{
|
||||||
|
"unix": ModeUnix,
|
||||||
|
"net": ModeNet,
|
||||||
|
}
|
||||||
|
if typedVal, ok := mapping[rawVal]; !ok {
|
||||||
|
return ModeNet, fmt.Errorf("invalid listening mode %s", rawVal)
|
||||||
|
} else {
|
||||||
|
return typedVal, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
varLogLevel envVar = "LOG_LEVEL"
|
ModeUnix ListeningMode = iota
|
||||||
|
ModeNet
|
||||||
varServerMode envVar = "SERVER_MODE"
|
|
||||||
varServerHost envVar = "SERVER_HOST"
|
|
||||||
varServerPort envVar = "SERVER_PORT"
|
|
||||||
varServerSocket envVar = "SERVER_SOCK_PATH"
|
|
||||||
|
|
||||||
varStorageType envVar = "STORAGE_TYPE"
|
|
||||||
varStorageFile envVar = "STORAGE_FILEPATH"
|
|
||||||
varStorageHost envVar = "STORAGE_HOST"
|
|
||||||
varStoragePort envVar = "STORAGE_PORT"
|
|
||||||
varStorageDB envVar = "STORAGE_DB"
|
|
||||||
varStorageUser envVar = "STORAGE_USER"
|
|
||||||
varStoragePassword envVar = "STORAGE_PASSWORD"
|
|
||||||
varStorageSSLMode envVar = "STORAGE_SSL_MODE"
|
|
||||||
varStorageSSLCaFile envVar = "STORAGE_SSL_CA_FILE"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListeningMode string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ModeUnix ListeningMode = "unix"
|
|
||||||
ModeNet ListeningMode = "net"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BackendConfigType string
|
type BackendConfigType string
|
||||||
|
@ -47,25 +47,6 @@ const (
|
||||||
SQLite BackendConfigType = "sqlite"
|
SQLite BackendConfigType = "sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
defaultLogLevel = logrus.InfoLevel
|
|
||||||
|
|
||||||
defaultServerMode = ModeNet
|
|
||||||
defaultServerHost = "0.0.0.0"
|
|
||||||
defaultServerPort = 5000
|
|
||||||
defaultServerSocket = ""
|
|
||||||
|
|
||||||
defaultStorageType = Memory
|
|
||||||
defaultStorageFile = "./polyculeconnect.db"
|
|
||||||
defaultStorageHost = "127.0.0.1"
|
|
||||||
defaultStoragePort = 5432
|
|
||||||
defaultStorageDB = "polyculeconnect"
|
|
||||||
defaultStorageUser = "polyculeconnect"
|
|
||||||
defaultStoragePassword = "polyculeconnect"
|
|
||||||
defaultStorageSSLMode = "disable"
|
|
||||||
defaultStorageSSLCaFile = ""
|
|
||||||
)
|
|
||||||
|
|
||||||
type BackendConfig struct {
|
type BackendConfig struct {
|
||||||
Config *oidc.Config `json:"config"`
|
Config *oidc.Config `json:"config"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
@ -81,19 +62,32 @@ type OpenConnectConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type StorageConfig struct {
|
type StorageConfig struct {
|
||||||
File string
|
File string `json:"file"`
|
||||||
Host string
|
Host string `json:"host"`
|
||||||
Port int
|
Port int `json:"port"`
|
||||||
Database string
|
Database string `json:"database"`
|
||||||
User string
|
User string `json:"user"`
|
||||||
Password string
|
Password string `json:"password"`
|
||||||
Ssl struct {
|
Ssl struct {
|
||||||
Mode string
|
Mode string `json:"mode"`
|
||||||
CaFile string
|
CaFile string `json:"caFile"`
|
||||||
}
|
} `json:"ssl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type jsonConf struct {
|
type jsonConf struct {
|
||||||
|
Log struct {
|
||||||
|
Level string `json:"level"`
|
||||||
|
} `json:"log"`
|
||||||
|
Server struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
SockPath string `json:"sock"`
|
||||||
|
} `json:"server"`
|
||||||
|
Storage struct {
|
||||||
|
StorageType string `json:"type"`
|
||||||
|
Config *StorageConfig `json:"config"`
|
||||||
|
} `json:"storage"`
|
||||||
OpenConnectConfig *OpenConnectConfig `json:"openconnect"`
|
OpenConnectConfig *OpenConnectConfig `json:"openconnect"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,42 +116,43 @@ func (ac *AppConfig) UnmarshalJSON(data []byte) error {
|
||||||
if err := json.Unmarshal(data, &jsonConf); err != nil {
|
if err := json.Unmarshal(data, &jsonConf); err != nil {
|
||||||
return fmt.Errorf("failed to read JSON: %w", err)
|
return fmt.Errorf("failed to read JSON: %w", err)
|
||||||
}
|
}
|
||||||
|
ac.LogLevel = parseLevel(jsonConf.Log.Level)
|
||||||
|
|
||||||
|
lm, err := listeningModeFromString(jsonConf.Server.Mode)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to parse server listening mode: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ac.ServerMode = lm
|
||||||
|
ac.SockPath = jsonConf.Server.SockPath
|
||||||
|
ac.Host = jsonConf.Server.Host
|
||||||
|
ac.Port = jsonConf.Server.Port
|
||||||
ac.OpenConnectConfig = jsonConf.OpenConnectConfig
|
ac.OpenConnectConfig = jsonConf.OpenConnectConfig
|
||||||
|
ac.StorageType = jsonConf.Storage.StorageType
|
||||||
|
ac.StorageConfig = jsonConf.Storage.Config
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *AppConfig) getConfFromEnv() {
|
var defaultConfig AppConfig = AppConfig{
|
||||||
ac.LogLevel = parseLevel(getStringFromEnv(varLogLevel, defaultLogLevel.String()))
|
LogLevel: logrus.InfoLevel,
|
||||||
|
ServerMode: ModeNet,
|
||||||
ac.ServerMode = ListeningMode(getStringFromEnv(varServerMode, string(defaultServerMode)))
|
Host: "0.0.0.0",
|
||||||
ac.Host = getStringFromEnv(varServerHost, defaultServerHost)
|
Port: 5000,
|
||||||
ac.Port = getIntFromEnv(varServerPort, defaultServerPort)
|
StorageType: "memory",
|
||||||
ac.SockPath = getStringFromEnv(varServerSocket, defaultServerSocket)
|
|
||||||
|
|
||||||
ac.StorageType = getStringFromEnv(varStorageType, string(defaultStorageType))
|
|
||||||
ac.StorageConfig.Database = getStringFromEnv(varStorageDB, defaultStorageDB)
|
|
||||||
ac.StorageConfig.File = getStringFromEnv(varStorageFile, defaultStorageFile)
|
|
||||||
ac.StorageConfig.Host = getStringFromEnv(varStorageHost, defaultStorageHost)
|
|
||||||
ac.StorageConfig.Port = getIntFromEnv(varStoragePort, defaultStoragePort)
|
|
||||||
ac.StorageConfig.User = getStringFromEnv(varStorageUser, defaultStorageUser)
|
|
||||||
ac.StorageConfig.Password = getStringFromEnv(varStoragePassword, defaultStoragePassword)
|
|
||||||
ac.StorageConfig.Ssl.CaFile = getStringFromEnv(varStorageSSLCaFile, defaultStorageSSLCaFile)
|
|
||||||
ac.StorageConfig.Ssl.Mode = getStringFromEnv(varStorageSSLMode, defaultStorageSSLMode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(filepath string) (*AppConfig, error) {
|
func New(filepath string) (*AppConfig, error) {
|
||||||
var conf AppConfig
|
|
||||||
conf.StorageConfig = &StorageConfig{}
|
|
||||||
content, err := os.ReadFile(filepath)
|
content, err := os.ReadFile(filepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, fs.ErrNotExist) {
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
conf := defaultConfig
|
||||||
|
return &conf, nil
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("failed to read config file %q: %w", filepath, err)
|
return nil, fmt.Errorf("failed to read config file %q: %w", filepath, err)
|
||||||
}
|
}
|
||||||
} else {
|
var conf AppConfig
|
||||||
if err := json.Unmarshal(content, &conf); err != nil {
|
if err := json.Unmarshal(content, &conf); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
conf.getConfFromEnv()
|
|
||||||
return &conf, nil
|
return &conf, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,129 +10,128 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultConfig = AppConfig{
|
func TestListeningModeString(t *testing.T) {
|
||||||
LogLevel: defaultLogLevel,
|
assert.Equal(t, "net", ModeNet.String(), "Unexpected string value")
|
||||||
ServerMode: defaultServerMode,
|
assert.Equal(t, "unix", ModeUnix.String(), "Unexpected string value")
|
||||||
Host: defaultServerHost,
|
|
||||||
Port: defaultServerPort,
|
|
||||||
SockPath: defaultServerSocket,
|
|
||||||
StorageType: string(defaultStorageType),
|
|
||||||
StorageConfig: &StorageConfig{
|
|
||||||
File: defaultStorageFile,
|
|
||||||
Host: defaultStorageHost,
|
|
||||||
Port: defaultStoragePort,
|
|
||||||
Database: defaultStorageDB,
|
|
||||||
User: defaultStorageUser,
|
|
||||||
Password: defaultStoragePassword,
|
|
||||||
Ssl: struct {
|
|
||||||
Mode string
|
|
||||||
CaFile string
|
|
||||||
}{Mode: defaultStorageSSLMode, CaFile: defaultStorageSSLCaFile},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func initJson(t *testing.T, content string) string {
|
|
||||||
tmpPath := t.TempDir()
|
|
||||||
confPath := path.Join(tmpPath, "config.json")
|
|
||||||
err := os.WriteFile(confPath, []byte(content), 0o644)
|
|
||||||
require.NoError(t, err)
|
|
||||||
return confPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEnv(t *testing.T, envVars map[string]string) {
|
|
||||||
for key, val := range envVars {
|
|
||||||
t.Setenv(key, val)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test returning a default config when providing a path that does not exist
|
||||||
func TestDefault(t *testing.T) {
|
func TestDefault(t *testing.T) {
|
||||||
t.Run("no file", func(t *testing.T) {
|
|
||||||
conf, err := New("/this/path/does/not/exist")
|
conf, err := New("/this/path/does/not/exist")
|
||||||
require.NoError(t, err)
|
if assert.Nil(t, err, "Unexpected error") {
|
||||||
assert.Equal(t, defaultConfig, *conf)
|
assert.Equal(t, defaultConfig, *conf, "Unexpected config")
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("empty config", func(t *testing.T) {
|
// Test creating a valid config (net mode)
|
||||||
confPath := initJson(t, `{}`)
|
func TestOKNet(t *testing.T) {
|
||||||
|
tmpPath := t.TempDir()
|
||||||
|
content := `{
|
||||||
|
"log": {
|
||||||
|
"level": "error"
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"mode": "net",
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8888
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
confPath := path.Join(tmpPath, "config.json")
|
||||||
|
require.Nil(t, os.WriteFile(confPath, []byte(content), 0o644), "Failed to write config")
|
||||||
|
|
||||||
|
expectedConf := AppConfig{
|
||||||
|
LogLevel: logrus.ErrorLevel,
|
||||||
|
ServerMode: ModeNet,
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: 8888,
|
||||||
|
}
|
||||||
conf, err := New(confPath)
|
conf, err := New(confPath)
|
||||||
require.NoError(t, err)
|
if assert.Nil(t, err, "Unexpected error") {
|
||||||
assert.Equal(t, defaultConfig, *conf)
|
assert.Equal(t, expectedConf, *conf, "Unexpected config")
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since we still use a JSON conf for the OIDC config, we still need to check this for now
|
// Test creating a valid config (unix mode)
|
||||||
// But as soon as the config file is not necessary, this will probably disappear
|
func TestOKUnix(t *testing.T) {
|
||||||
func TestInvalidJSON(t *testing.T) {
|
tmpPath := t.TempDir()
|
||||||
confPath := initJson(t, "toto")
|
content := `{
|
||||||
errMsg := "failed to parse config file: invalid character 'o' in literal true (expecting 'r')"
|
"log": {
|
||||||
|
"level": "error"
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"mode": "unix",
|
||||||
|
"sock": "/run/toto.sock"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
confPath := path.Join(tmpPath, "config.json")
|
||||||
|
require.Nil(t, os.WriteFile(confPath, []byte(content), 0o644), "Failed to write config")
|
||||||
|
|
||||||
|
expectedConf := AppConfig{
|
||||||
|
LogLevel: logrus.ErrorLevel,
|
||||||
|
ServerMode: ModeUnix,
|
||||||
|
SockPath: "/run/toto.sock",
|
||||||
|
}
|
||||||
|
conf, err := New(confPath)
|
||||||
|
if assert.Nil(t, err, "Unexpected error") {
|
||||||
|
assert.Equal(t, expectedConf, *conf, "Unexpected config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test creating a valid config, no log level provided, should be info
|
||||||
|
func TestOKNoLogLevel(t *testing.T) {
|
||||||
|
tmpPath := t.TempDir()
|
||||||
|
content := `{
|
||||||
|
"server": {
|
||||||
|
"mode": "net",
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8888
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
confPath := path.Join(tmpPath, "config.json")
|
||||||
|
require.Nil(t, os.WriteFile(confPath, []byte(content), 0o644), "Failed to write config")
|
||||||
|
|
||||||
|
expectedConf := AppConfig{
|
||||||
|
LogLevel: logrus.InfoLevel,
|
||||||
|
ServerMode: ModeNet,
|
||||||
|
Host: "127.0.0.1",
|
||||||
|
Port: 8888,
|
||||||
|
}
|
||||||
|
conf, err := New(confPath)
|
||||||
|
if assert.Nil(t, err, "Unexpected error") {
|
||||||
|
assert.Equal(t, expectedConf, *conf, "Unexpected config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test giving an invalid server mode
|
||||||
|
func TestErrMode(t *testing.T) {
|
||||||
|
tmpPath := t.TempDir()
|
||||||
|
content := `{
|
||||||
|
"log": {
|
||||||
|
"level": "error"
|
||||||
|
},
|
||||||
|
"server": {
|
||||||
|
"mode": "toto",
|
||||||
|
"sock": "/run/toto.sock"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
confPath := path.Join(tmpPath, "config.json")
|
||||||
|
require.Nil(t, os.WriteFile(confPath, []byte(content), 0o644), "Failed to write config")
|
||||||
|
|
||||||
_, err := New(confPath)
|
_, err := New(confPath)
|
||||||
assert.ErrorContains(t, err, errMsg)
|
if assert.Error(t, err, "Unexpected nil error") {
|
||||||
|
errMsg := "failed to parse config file: failed to parse server listening mode: invalid listening mode toto"
|
||||||
|
assert.Equal(t, errMsg, err.Error(), "Unexpected error message")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHostNetMode(t *testing.T) {
|
func TestInvalidJSON(t *testing.T) {
|
||||||
envVars := map[string]string{
|
tmpPath := t.TempDir()
|
||||||
string(varServerMode): string(ModeNet),
|
content := "toto"
|
||||||
string(varServerHost): "127.0.0.1",
|
confPath := path.Join(tmpPath, "config.json")
|
||||||
string(varServerPort): "8888",
|
require.Nil(t, os.WriteFile(confPath, []byte(content), 0o644), "Failed to write config")
|
||||||
|
_, err := New(confPath)
|
||||||
|
if assert.Error(t, err, "Unexpected nil error") {
|
||||||
|
errMsg := "failed to parse config file: invalid character 'o' in literal true (expecting 'r')"
|
||||||
|
assert.Equal(t, errMsg, err.Error(), "Unexpected error message")
|
||||||
}
|
}
|
||||||
setEnv(t, envVars)
|
|
||||||
|
|
||||||
conf, err := New("")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, ModeNet, conf.ServerMode)
|
|
||||||
assert.Equal(t, "127.0.0.1", conf.Host)
|
|
||||||
assert.Equal(t, 8888, conf.Port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHostSocketMode(t *testing.T) {
|
|
||||||
envVars := map[string]string{
|
|
||||||
string(varServerMode): string(ModeUnix),
|
|
||||||
string(varServerSocket): "/run/polyculeconnect.sock",
|
|
||||||
}
|
|
||||||
setEnv(t, envVars)
|
|
||||||
|
|
||||||
conf, err := New("")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, ModeUnix, conf.ServerMode)
|
|
||||||
assert.Equal(t, "/run/polyculeconnect.sock", conf.SockPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLogLevel(t *testing.T) {
|
|
||||||
envVars := map[string]string{
|
|
||||||
string(varLogLevel): "error",
|
|
||||||
}
|
|
||||||
setEnv(t, envVars)
|
|
||||||
|
|
||||||
conf, err := New("")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, logrus.ErrorLevel, conf.LogLevel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLogLevelInvalidValue(t *testing.T) {
|
|
||||||
envVars := map[string]string{
|
|
||||||
string(varLogLevel): "toto",
|
|
||||||
}
|
|
||||||
setEnv(t, envVars)
|
|
||||||
|
|
||||||
conf, err := New("")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, logrus.InfoLevel, conf.LogLevel) // if invalid, no error should occur, but info level should be used
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSqliteConfig(t *testing.T) {
|
|
||||||
envVars := map[string]string{
|
|
||||||
string(varStorageType): "sqlite",
|
|
||||||
string(varStorageFile): "/data/polyculeconnect.db",
|
|
||||||
}
|
|
||||||
setEnv(t, envVars)
|
|
||||||
|
|
||||||
conf, err := New("")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, string(SQLite), conf.StorageType)
|
|
||||||
assert.Equal(t, "/data/polyculeconnect.db", conf.StorageConfig.File)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getStringFromEnv(key envVar, defaultValue string) string {
|
|
||||||
val, ok := os.LookupEnv(string(key))
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIntFromEnv(key envVar, defaultValue int) int {
|
|
||||||
rawVal, ok := os.LookupEnv(string(key))
|
|
||||||
if !ok {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
val, err := strconv.Atoi(rawVal)
|
|
||||||
if err != nil {
|
|
||||||
return defaultValue
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
|
@ -27,7 +27,6 @@ require (
|
||||||
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6 // indirect
|
github.com/go-ldap/ldap/v3 v3.4.6 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/s2a-go v0.1.7 // indirect
|
github.com/google/s2a-go v0.1.7 // indirect
|
||||||
|
@ -39,9 +38,7 @@ require (
|
||||||
github.com/huandu/xstrings v1.3.3 // indirect
|
github.com/huandu/xstrings v1.3.3 // indirect
|
||||||
github.com/imdario/mergo v0.3.11 // indirect
|
github.com/imdario/mergo v0.3.11 // indirect
|
||||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||||
github.com/lib/pq v1.10.9 // indirect
|
|
||||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/mitchellh/copystructure v1.0.0 // indirect
|
github.com/mitchellh/copystructure v1.0.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
github.com/mitchellh/reflectwalk v1.0.0 // indirect
|
||||||
|
|
|
@ -51,8 +51,6 @@ github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyM
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
|
||||||
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
|
@ -108,12 +106,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
|
||||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
||||||
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||||
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
|
||||||
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
|
||||||
|
|
|
@ -13,11 +13,9 @@ import (
|
||||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector"
|
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector"
|
||||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger"
|
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger"
|
||||||
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/server"
|
"git.faercol.me/faercol/polyculeconnect/polyculeconnect/server"
|
||||||
"github.com/dexidp/dex/connector/oidc"
|
|
||||||
dex_server "github.com/dexidp/dex/server"
|
dex_server "github.com/dexidp/dex/server"
|
||||||
"github.com/dexidp/dex/storage"
|
"github.com/dexidp/dex/storage"
|
||||||
"github.com/dexidp/dex/storage/memory"
|
"github.com/dexidp/dex/storage/memory"
|
||||||
"github.com/dexidp/dex/storage/sql"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,43 +37,15 @@ func parseArgs() *cliArgs {
|
||||||
|
|
||||||
func initStorage(conf *config.AppConfig) (storage.Storage, error) {
|
func initStorage(conf *config.AppConfig) (storage.Storage, error) {
|
||||||
var storageType storage.Storage
|
var storageType storage.Storage
|
||||||
var err error
|
|
||||||
switch conf.StorageType {
|
switch conf.StorageType {
|
||||||
case "memory":
|
case "memory":
|
||||||
storageType = memory.New(logger.L)
|
storageType = memory.New(logger.L)
|
||||||
case "sqlite":
|
|
||||||
sqlconfig := sql.SQLite3{
|
|
||||||
File: conf.StorageConfig.File,
|
|
||||||
}
|
|
||||||
storageType, err = sqlconfig.Open(logger.L)
|
|
||||||
if err != nil {
|
|
||||||
logger.L.Fatalf("Failed to initialize sqlite backend: %s", err.Error())
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return storageType, fmt.Errorf("unsupported storage backend type: %s", conf.StorageType)
|
return storageType, fmt.Errorf("unsupported storage backend type: %s", conf.StorageType)
|
||||||
}
|
}
|
||||||
return storageType, nil
|
return storageType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createConnector(backend *config.BackendConfig, dexConf *dex_server.Config, connectorIDs []string) error {
|
|
||||||
for _, id := range connectorIDs {
|
|
||||||
if id == backend.ID {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
backendConfJson, err := json.Marshal(backend.Config)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to serialize oidc config for backend %q: %s", backend.Name, err.Error())
|
|
||||||
}
|
|
||||||
return dexConf.Storage.CreateConnector(storage.Connector{
|
|
||||||
ID: backend.ID,
|
|
||||||
Name: backend.Name,
|
|
||||||
Type: string(backend.Type),
|
|
||||||
Config: backendConfJson,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
args := parseArgs()
|
args := parseArgs()
|
||||||
|
|
||||||
|
@ -111,28 +81,27 @@ func main() {
|
||||||
logger.L.Info("Initializing authentication backends")
|
logger.L.Info("Initializing authentication backends")
|
||||||
|
|
||||||
dex_server.ConnectorsConfig["refuseAll"] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) }
|
dex_server.ConnectorsConfig["refuseAll"] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) }
|
||||||
connectors, err := dexConf.Storage.ListConnectors()
|
if err := dexConf.Storage.CreateConnector(storage.Connector{
|
||||||
if err != nil {
|
|
||||||
logger.L.Fatalf("Failed to get existing connectors: %s", err.Error())
|
|
||||||
}
|
|
||||||
var connectorIDs []string
|
|
||||||
for _, conn := range connectors {
|
|
||||||
connectorIDs = append(connectorIDs, conn.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
backend := config.BackendConfig{
|
|
||||||
Config: &oidc.Config{},
|
|
||||||
Name: "RefuseAll",
|
|
||||||
ID: "null",
|
ID: "null",
|
||||||
|
Name: "RefuseAll",
|
||||||
Type: "refuseAll",
|
Type: "refuseAll",
|
||||||
}
|
Config: nil,
|
||||||
|
}); err != nil {
|
||||||
if err := createConnector(&backend, &dexConf, connectorIDs); err != nil {
|
|
||||||
logger.L.Errorf("Failed to add connector for backend RefuseAll to stage: %s", err.Error())
|
logger.L.Errorf("Failed to add connector for backend RefuseAll to stage: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, backend := range conf.OpenConnectConfig.BackendConfigs {
|
for _, backend := range conf.OpenConnectConfig.BackendConfigs {
|
||||||
if err := createConnector(backend, &dexConf, connectorIDs); err != nil {
|
backendConfJson, err := json.Marshal(backend.Config)
|
||||||
|
if err != nil {
|
||||||
|
logger.L.Errorf("Failed to serialize oidc config for backend %q: %s", backend.Name, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := dexConf.Storage.CreateConnector(storage.Connector{
|
||||||
|
ID: backend.ID,
|
||||||
|
Name: backend.Name,
|
||||||
|
Type: string(backend.Type),
|
||||||
|
Config: backendConfJson,
|
||||||
|
}); err != nil {
|
||||||
logger.L.Errorf("Failed to add connector for backend %q to stage: %s", backend.Name, err.Error())
|
logger.L.Errorf("Failed to add connector for backend %q to stage: %s", backend.Name, err.Error())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue