Compare commits

..

4 commits

Author SHA1 Message Date
0083624204 feat #6: Add style for auth page
Some checks failed
ci/woodpecker/push/test Pipeline failed
ci/woodpecker/push/deploy unknown status
2023-11-04 09:01:26 +01:00
fd8caf98a6 feat #6: Add logo to all page headers 2023-11-04 09:01:26 +01:00
4909df4b04 feat #37: Use environment variables for most of the configuration
All checks were successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2023-10-28 14:20:40 +02:00
3bc17d6aba feat #35: Add sqlite3 storage backend
All checks were successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful
2023-10-22 22:02:54 +02:00
8 changed files with 307 additions and 220 deletions

View file

@ -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 CGO_ENABLED=0 make build RUN make build
# Replace with from scratch later on # Replace with from scratch later on
FROM --platform=$TARGETPLATFORM alpine:latest FROM --platform=$TARGETPLATFORM alpine:latest

View file

@ -10,21 +10,13 @@ TODO
## Configuration ## Configuration
Here is an example config file As a temporary solution, the list of backends and applications, as well as the openconnect configuration
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", // hostname of your polyculeconnect server "issuer": "https://polyculeconnect.domain",
"clients": [ "clients": [
{ {
"name": "<name>", "name": "<name>",
@ -40,19 +32,42 @@ Here is an example config file
"id": "<unique_id>", "id": "<unique_id>",
"name": "<human_readable_name>", "name": "<human_readable_name>",
"local": true, "local": true,
"type": "oidc", // must be "oidc" for now "type": "oidc",
"config": { "config": {
"issuer": "https://polyculeconnect.domain", // must be the same as current issuer "issuer": "https://polyculeconnect.domain",
"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

View file

@ -12,32 +12,32 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
type ListeningMode int64 type envVar string
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 (
ModeUnix ListeningMode = iota varLogLevel envVar = "LOG_LEVEL"
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,6 +47,25 @@ 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"`
@ -62,32 +81,19 @@ type OpenConnectConfig struct {
} }
type StorageConfig struct { type StorageConfig struct {
File string `json:"file"` File string
Host string `json:"host"` Host string
Port int `json:"port"` Port int
Database string `json:"database"` Database string
User string `json:"user"` User string
Password string `json:"password"` Password string
Ssl struct { Ssl struct {
Mode string `json:"mode"` Mode string
CaFile string `json:"caFile"` CaFile string
} `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"`
} }
@ -116,43 +122,42 @@ 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
} }
var defaultConfig AppConfig = AppConfig{ func (ac *AppConfig) getConfFromEnv() {
LogLevel: logrus.InfoLevel, ac.LogLevel = parseLevel(getStringFromEnv(varLogLevel, defaultLogLevel.String()))
ServerMode: ModeNet,
Host: "0.0.0.0", ac.ServerMode = ListeningMode(getStringFromEnv(varServerMode, string(defaultServerMode)))
Port: 5000, ac.Host = getStringFromEnv(varServerHost, defaultServerHost)
StorageType: "memory", ac.Port = getIntFromEnv(varServerPort, defaultServerPort)
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)
} }
var conf AppConfig } else {
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
} }

View file

@ -10,128 +10,129 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestListeningModeString(t *testing.T) { var defaultConfig = AppConfig{
assert.Equal(t, "net", ModeNet.String(), "Unexpected string value") LogLevel: defaultLogLevel,
assert.Equal(t, "unix", ModeUnix.String(), "Unexpected string value") ServerMode: defaultServerMode,
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")
if assert.Nil(t, err, "Unexpected error") { require.NoError(t, err)
assert.Equal(t, defaultConfig, *conf, "Unexpected config") assert.Equal(t, defaultConfig, *conf)
} })
}
// Test creating a valid config (net mode) t.Run("empty config", func(t *testing.T) {
func TestOKNet(t *testing.T) { confPath := initJson(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)
if assert.Nil(t, err, "Unexpected error") { require.NoError(t, err)
assert.Equal(t, expectedConf, *conf, "Unexpected config") assert.Equal(t, defaultConfig, *conf)
} })
}
// Test creating a valid config (unix mode)
func TestOKUnix(t *testing.T) {
tmpPath := t.TempDir()
content := `{
"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)
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")
}
} }
// Since we still use a JSON conf for the OIDC config, we still need to check this for now
// But as soon as the config file is not necessary, this will probably disappear
func TestInvalidJSON(t *testing.T) { func TestInvalidJSON(t *testing.T) {
tmpPath := t.TempDir() confPath := initJson(t, "toto")
content := "toto"
confPath := path.Join(tmpPath, "config.json")
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')" errMsg := "failed to parse config file: invalid character 'o' in literal true (expecting 'r')"
assert.Equal(t, errMsg, err.Error(), "Unexpected error message") _, err := New(confPath)
assert.ErrorContains(t, err, errMsg)
} }
func TestHostNetMode(t *testing.T) {
envVars := map[string]string{
string(varServerMode): string(ModeNet),
string(varServerHost): "127.0.0.1",
string(varServerPort): "8888",
}
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)
} }

View file

@ -0,0 +1,26 @@
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
}

View file

@ -27,6 +27,7 @@ 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
@ -38,7 +39,9 @@ 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

View file

@ -51,6 +51,8 @@ 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=
@ -106,8 +108,12 @@ 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=

View file

@ -13,9 +13,11 @@ 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"
) )
@ -37,15 +39,43 @@ 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()
@ -81,27 +111,28 @@ 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) }
if err := dexConf.Storage.CreateConnector(storage.Connector{ connectors, err := dexConf.Storage.ListConnectors()
ID: "null", 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", Name: "RefuseAll",
ID: "null",
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 {
backendConfJson, err := json.Marshal(backend.Config) if err := createConnector(backend, &dexConf, connectorIDs); err != nil {
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
} }