From e4497cad8cabe72569fbe4bc5096e7eeef33d50f Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sun, 29 Oct 2023 13:27:13 +0100 Subject: [PATCH 1/4] Add envrc to facilitate development --- .envrc | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..cc47c63 --- /dev/null +++ b/.envrc @@ -0,0 +1,18 @@ +# Can be debug,info,warning,error +export LOG_LEVEL=debug + +# Can be net,unix +export SERVER_MODE=net +export SERVER_HOST="0.0.0.0" +export SERVER_PORT="5000" +# SERVER_SOCK_PATH = "" + +export STORAGE_TYPE="sqlite" +export 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 = "" \ No newline at end of file -- 2.45.2 From 550622e512fb3a560b646a86005a269a077641aa Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sun, 29 Oct 2023 13:29:48 +0100 Subject: [PATCH 2/4] feat #43: add service to handle backends in the storage --- polyculeconnect/connector/refuse_all.go | 10 + polyculeconnect/services/backend/backend.go | 123 ++++++++++ .../services/backend/backend_test.go | 215 ++++++++++++++++++ 3 files changed, 348 insertions(+) create mode 100644 polyculeconnect/services/backend/backend.go create mode 100644 polyculeconnect/services/backend/backend_test.go diff --git a/polyculeconnect/connector/refuse_all.go b/polyculeconnect/connector/refuse_all.go index edb862c..5e13970 100644 --- a/polyculeconnect/connector/refuse_all.go +++ b/polyculeconnect/connector/refuse_all.go @@ -6,8 +6,18 @@ import ( "github.com/dexidp/dex/connector" "github.com/dexidp/dex/pkg/log" + "github.com/dexidp/dex/storage" ) +const TypeRefuseAll = "refuseAll" + +var RefuseAllConnectorConfig storage.Connector = storage.Connector{ + ID: "null", + Name: "RefuseAll", + Type: TypeRefuseAll, + Config: nil, +} + type RefuseAllConfig struct{} func (c *RefuseAllConfig) Open(id string, logger log.Logger) (connector.Connector, error) { diff --git a/polyculeconnect/services/backend/backend.go b/polyculeconnect/services/backend/backend.go new file mode 100644 index 0000000..9f9d6f5 --- /dev/null +++ b/polyculeconnect/services/backend/backend.go @@ -0,0 +1,123 @@ +package backend + +import ( + "encoding/json" + "errors" + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" + "github.com/dexidp/dex/connector/oidc" + "github.com/dexidp/dex/storage" +) + +var ErrUnsupportedType = errors.New("unsupported connector type") + +type BackendConfig struct { + ID string + Name string + Issuer string + ClientID string + ClientSecret string + RedirectURI string +} + +func (bc *BackendConfig) OIDC() oidc.Config { + return oidc.Config{ + Issuer: bc.Issuer, + ClientID: bc.ClientID, + ClientSecret: bc.ClientSecret, + RedirectURI: bc.RedirectURI, + } +} + +func (bc *BackendConfig) Storage() (storage.Connector, error) { + oidcJSON, err := json.Marshal(bc.OIDC()) + if err != nil { + return storage.Connector{}, fmt.Errorf("failed to serialize oidc config: %w", err) + } + + return storage.Connector{ + ID: bc.ID, + Type: "oidc", + Name: bc.Name, + Config: oidcJSON, + }, nil +} + +func (bc *BackendConfig) FromConnector(connector storage.Connector) error { + var oidc oidc.Config + if connector.Type != "oidc" { + return ErrUnsupportedType + } + if err := json.Unmarshal(connector.Config, &oidc); err != nil { + return fmt.Errorf("invalid OIDC config: %w", err) + } + bc.ID = connector.ID + bc.Name = connector.Name + bc.ClientID = oidc.ClientID + bc.ClientSecret = oidc.ClientSecret + bc.Issuer = oidc.Issuer + bc.RedirectURI = oidc.RedirectURI + return nil +} + +type Service interface { + ListBackends() ([]BackendConfig, error) + GetBackend(id string) (BackendConfig, error) + AddBackend(config BackendConfig) error + RemoveBackend(id string) error +} + +type concreteBackendService struct { + s storage.Storage +} + +func (cbs *concreteBackendService) ListBackends() ([]BackendConfig, error) { + connectors, err := cbs.s.ListConnectors() + if err != nil { + return nil, fmt.Errorf("failed to get connectors from storage: %w", err) + } + var res []BackendConfig + for _, c := range connectors { + + // We know that this type is special, we don't want to use it at all here + if c.Type == connector.TypeRefuseAll { + continue + } + + var b BackendConfig + if err := b.FromConnector(c); err != nil { + return res, err + } + res = append(res, b) + } + return res, nil +} + +func (cbs *concreteBackendService) GetBackend(connectorID string) (BackendConfig, error) { + c, err := cbs.s.GetConnector(connectorID) + if err != nil { + return BackendConfig{}, fmt.Errorf("failed to get connector from storage: %w", err) + } + var res BackendConfig + if err := res.FromConnector(c); err != nil { + return BackendConfig{}, err + } + return res, nil +} + +func (cbs *concreteBackendService) AddBackend(config BackendConfig) error { + storageConf, err := config.Storage() + if err != nil { + return fmt.Errorf("failed to create storage configuration: %w", err) + } + return cbs.s.CreateConnector(storageConf) +} + +func (cbs *concreteBackendService) RemoveBackend(connectorID string) error { + return cbs.s.DeleteConnector(connectorID) +} + +func New(s storage.Storage) Service { + return &concreteBackendService{s} +} diff --git a/polyculeconnect/services/backend/backend_test.go b/polyculeconnect/services/backend/backend_test.go new file mode 100644 index 0000000..4fde675 --- /dev/null +++ b/polyculeconnect/services/backend/backend_test.go @@ -0,0 +1,215 @@ +package backend_test + +import ( + "encoding/json" + "fmt" + "testing" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/memory" + logt "github.com/sirupsen/logrus/hooks/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + testDomain string = "https://test.domain.com" + testClientID string = "this_is_an_id" + testClientSecret string = "this_is_a_secret" + testRedirectURI string = "http://127.0.0.1:5000/callback" + testConnectorName string = "Test connector" +) + +func generateConnector(id string) storage.Connector { + confJson := fmt.Sprintf(`{ + "issuer": "%s", + "clientID": "%s", + "clientSecret": "%s", + "redirectURI": "%s" +}`, testDomain, testClientID, testClientSecret, testRedirectURI) + storageConfig := storage.Connector{ + ID: id, + Name: testConnectorName, + Type: "oidc", + Config: []byte(confJson), + } + return storageConfig +} + +func generateConfig(id string) backend.BackendConfig { + return backend.BackendConfig{ + ID: id, + Name: testConnectorName, + Issuer: testDomain, + ClientID: testClientID, + ClientSecret: testClientSecret, + RedirectURI: testRedirectURI, + } +} + +func checkStrInMap(t *testing.T, vals map[string]interface{}, key, expected string) { + rawVal, ok := vals[key] + require.Truef(t, ok, "missing key %s", key) + strVal, ok := rawVal.(string) + require.Truef(t, ok, "invalid string format %v", rawVal) + assert.Equal(t, expected, strVal, "unexpected value") +} + +func initStorage(t *testing.T) storage.Storage { + logger, _ := logt.NewNullLogger() + s := memory.New(logger) + + require.NoError(t, s.CreateConnector(connector.RefuseAllConnectorConfig)) + require.NoError(t, s.CreateConnector(generateConnector("test0"))) + require.NoError(t, s.CreateConnector(generateConnector("test1"))) + + return s +} + +func TestBackendConfigFromConnector(t *testing.T) { + connector := generateConnector("test") + var bc backend.BackendConfig + require.NoError(t, bc.FromConnector(connector)) + + assert.Equal(t, testDomain, bc.Issuer) + assert.Equal(t, testClientID, bc.ClientID) + assert.Equal(t, testClientSecret, bc.ClientSecret) + assert.Equal(t, testRedirectURI, bc.RedirectURI) + assert.Equal(t, testConnectorName, bc.Name) + assert.Equal(t, "test", bc.ID) +} + +func TestBackendConfigInvalidType(t *testing.T) { + connector := generateConnector("test") + connector.Type = "test" + var bc backend.BackendConfig + assert.ErrorIs(t, bc.FromConnector(connector), backend.ErrUnsupportedType) +} + +func TestBackendConfigInvalidOIDCConfig(t *testing.T) { + connector := generateConnector("test") + connector.Config = []byte("toto") + var bc backend.BackendConfig + assert.ErrorContains(t, bc.FromConnector(connector), "invalid OIDC config") +} + +func TestOIDCConfigFromBackendConfig(t *testing.T) { + conf := generateConfig("test") + oidcConf := conf.OIDC() + + assert.Equal(t, testDomain, oidcConf.Issuer) + assert.Equal(t, testClientID, oidcConf.ClientID) + assert.Equal(t, testClientSecret, oidcConf.ClientSecret) + assert.Equal(t, testRedirectURI, oidcConf.RedirectURI) +} + +func TestConnectorConfigFromBackendConfig(t *testing.T) { + conf := generateConfig("test") + con, err := conf.Storage() + require.NoError(t, err) + + // The OIDC config is stored as JSON data, we just want the raw keys here + var oidcConf map[string]interface{} + require.NoError(t, json.Unmarshal(con.Config, &oidcConf)) + + assert.Equal(t, "oidc", con.Type) + assert.Equal(t, "test", con.ID) + assert.Equal(t, testConnectorName, con.Name) + checkStrInMap(t, oidcConf, "issuer", testDomain) + checkStrInMap(t, oidcConf, "clientID", testClientID) + checkStrInMap(t, oidcConf, "clientSecret", testClientSecret) + checkStrInMap(t, oidcConf, "redirectURI", testRedirectURI) +} + +func TestListBackendsEmpty(t *testing.T) { + logger, _ := logt.NewNullLogger() + s := memory.New(logger) + + // add the default refuse all connector, it should not be visible in the list + require.NoError(t, s.CreateConnector(connector.RefuseAllConnectorConfig)) + srv := backend.New(s) + + backends, err := srv.ListBackends() // empty list, and no error + require.NoError(t, err) + require.Len(t, backends, 0) +} + +func TestListBackendsNotEmpty(t *testing.T) { + s := initStorage(t) + srv := backend.New(s) + + backends, err := srv.ListBackends() // empty list, and no error + expectedIds := []string{"test0", "test1"} + require.NoError(t, err) + assert.Len(t, backends, 2) + for _, c := range backends { + assert.Contains(t, expectedIds, c.ID) + } +} + +func TestGetBackend(t *testing.T) { + s := initStorage(t) + srv := backend.New(s) + + t.Run("OK", func(t *testing.T) { + conf, err := srv.GetBackend("test0") + require.NoError(t, err) + assert.Equal(t, testDomain, conf.Issuer) + assert.Equal(t, testClientID, conf.ClientID) + assert.Equal(t, testClientSecret, conf.ClientSecret) + assert.Equal(t, testRedirectURI, conf.RedirectURI) + assert.Equal(t, testConnectorName, conf.Name) + assert.Equal(t, "test0", conf.ID) + }) + + t.Run("Not exist", func(t *testing.T) { + _, err := srv.GetBackend("toto") + assert.ErrorIs(t, err, storage.ErrNotFound) + }) + + t.Run("Invalid type", func(t *testing.T) { + _, err := srv.GetBackend("null") // null has a RefuseAll type, which is unsupported here + assert.ErrorIs(t, err, backend.ErrUnsupportedType) + }) +} + +func TestAddBackend(t *testing.T) { + s := initStorage(t) + srv := backend.New(s) + + t.Run("OK", func(t *testing.T) { + conf := generateConfig("test_add") + require.NoError(t, srv.AddBackend(conf)) + + var parsedConf backend.BackendConfig + storageConf, err := s.GetConnector("test_add") + require.NoError(t, err) + require.NoError(t, parsedConf.FromConnector(storageConf)) + assert.Equal(t, conf, parsedConf) + }) + + t.Run("Already exists", func(t *testing.T) { + require.ErrorIs(t, srv.AddBackend(generateConfig("test0")), storage.ErrAlreadyExists) + }) +} + +func TestRemoveBackend(t *testing.T) { + s := initStorage(t) + srv := backend.New(s) + + t.Run("OK", func(t *testing.T) { + require.NoError(t, srv.AddBackend(generateConfig("to_remove"))) + _, err := s.GetConnector("to_remove") + require.NoError(t, err) // no error means it's present + + require.NoError(t, srv.RemoveBackend("to_remove")) + _, err = s.GetConnector("to_remove") + assert.ErrorIs(t, err, storage.ErrNotFound) // means it's been deleted + }) + + t.Run("No present", func(t *testing.T) { + require.ErrorIs(t, srv.RemoveBackend("toto"), storage.ErrNotFound) + }) +} -- 2.45.2 From 69a07ce076e786b566fc0e693bf935a2b245e8e9 Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sun, 29 Oct 2023 13:31:52 +0100 Subject: [PATCH 3/4] feat #43: add cli command to manage backends --- polyculeconnect/cmd/backend/add.go | 66 +++++++++++++++++++++++++ polyculeconnect/cmd/backend/backend.go | 30 ++++++++++++ polyculeconnect/cmd/backend/remove.go | 38 +++++++++++++++ polyculeconnect/cmd/backend/show.go | 67 ++++++++++++++++++++++++++ polyculeconnect/cmd/root.go | 8 +-- polyculeconnect/cmd/utils/utils.go | 40 +++++++++++++++ polyculeconnect/config/config.go | 5 ++ polyculeconnect/main.go | 5 +- polyculeconnect/services/idsecret.go | 34 +++++++++++++ 9 files changed, 288 insertions(+), 5 deletions(-) create mode 100644 polyculeconnect/cmd/backend/add.go create mode 100644 polyculeconnect/cmd/backend/backend.go create mode 100644 polyculeconnect/cmd/backend/remove.go create mode 100644 polyculeconnect/cmd/backend/show.go create mode 100644 polyculeconnect/cmd/utils/utils.go create mode 100644 polyculeconnect/services/idsecret.go diff --git a/polyculeconnect/cmd/backend/add.go b/polyculeconnect/cmd/backend/add.go new file mode 100644 index 0000000..7577736 --- /dev/null +++ b/polyculeconnect/cmd/backend/add.go @@ -0,0 +1,66 @@ +package cmd + +import ( + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "github.com/spf13/cobra" +) + +var ( + backendID string + backendName string + backendIssuer string +) + +var backendAddCmd = &cobra.Command{ + Use: "add", + Short: "Add a new backend to the storage", + Long: `Add a new backend to the storage. + +Parameters to provide: +- id: Unique ID to represent the backend in the storage +- name: Human readable name to represent the backend. It will be used by + the user in the authentication page to select a backend during + authentication +- issuer: Full hostname of the OIDC provider, e.g. 'https://github.com'`, + Run: func(cmd *cobra.Command, args []string) { + addNewBackend() + }, +} + +func addNewBackend() { + c := utils.InitConfig("") + s := utils.InitStorage(c) + + clientID, clientSecret, err := services.GenerateClientIDSecret() + if err != nil { + utils.Failf("Failed to generate client id or secret: %s", err.Error()) + } + + backendConf := backend.BackendConfig{ + Issuer: backendIssuer, + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURI: c.RedirectURI(), + ID: backendID, + Name: backendName, + } + if err := backend.New(s).AddBackend(backendConf); err != nil { + utils.Failf("Failed to add new backend to storage: %s", err.Error()) + } + + fmt.Printf("New backend %s added.\n", backendName) + printProperty("Client ID", clientID) + printProperty("Client secret", clientSecret) +} + +func init() { + backendCmd.AddCommand(backendAddCmd) + + backendAddCmd.Flags().StringVarP(&backendID, "id", "i", "", "ID to identify the backend in the storage") + backendAddCmd.Flags().StringVarP(&backendName, "name", "n", "", "Name to represent the backend") + backendAddCmd.Flags().StringVarP(&backendIssuer, "issuer", "d", "", "Full hostname of the backend") +} diff --git a/polyculeconnect/cmd/backend/backend.go b/polyculeconnect/cmd/backend/backend.go new file mode 100644 index 0000000..a45fb6c --- /dev/null +++ b/polyculeconnect/cmd/backend/backend.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" + "github.com/spf13/cobra" +) + +var backendCmd = &cobra.Command{ + Use: "backend", + Short: "A brief description of your command", + Long: `A longer description that spans multiple lines and likely contains examples +and usage of using your command. For example: + +Cobra is a CLI library for Go that empowers applications. +This application is a tool to generate the needed files +to quickly create a Cobra application.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("backend called") + }, +} + +func printProperty(key, value string) { + fmt.Printf("\t- %s: %s\n", key, value) +} + +func init() { + cmd.RootCmd.AddCommand(backendCmd) +} diff --git a/polyculeconnect/cmd/backend/remove.go b/polyculeconnect/cmd/backend/remove.go new file mode 100644 index 0000000..de6fe0e --- /dev/null +++ b/polyculeconnect/cmd/backend/remove.go @@ -0,0 +1,38 @@ +package cmd + +import ( + "errors" + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "github.com/dexidp/dex/storage" + "github.com/spf13/cobra" +) + +var backendRemoveCmd = &cobra.Command{ + Use: "remove ", + Short: "Remove a backend", + Long: `Remove the backend with the given ID from the database. + +If the backend is not found in the database, no error is returned`, + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + removeBackend(args[0]) + }, +} + +func removeBackend(backendID string) { + s := utils.InitStorage(utils.InitConfig("")) + + if err := backend.New(s).RemoveBackend(backendID); err != nil { + if !errors.Is(err, storage.ErrNotFound) { + utils.Failf("Failed to remove backend: %s", err.Error()) + } + } + fmt.Println("Backend deleted") +} + +func init() { + backendCmd.AddCommand(backendRemoveCmd) +} diff --git a/polyculeconnect/cmd/backend/show.go b/polyculeconnect/cmd/backend/show.go new file mode 100644 index 0000000..e1a6ce6 --- /dev/null +++ b/polyculeconnect/cmd/backend/show.go @@ -0,0 +1,67 @@ +package cmd + +import ( + "errors" + "fmt" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services/backend" + "github.com/dexidp/dex/storage" + "github.com/spf13/cobra" +) + +var backendShowCmd = &cobra.Command{ + Use: "show [backend_id]", + Short: "Display installed backends", + Long: `Display the configuration for the backends. + +Pass the commands without arguments to display the list of currently installed backends +Pass the optional 'id' argument to display the configuration for this specific backend`, + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + s := utils.InitStorage(utils.InitConfig("")) + + if len(args) > 0 { + showBackend(args[0], backend.New(s)) + } else { + listBackends(backend.New(s)) + } + }, +} + +func showBackend(backendId string, backendService backend.Service) { + backendConfig, err := backendService.GetBackend(backendId) + if err != nil { + if errors.Is(err, storage.ErrNotFound) { + utils.Failf("Backend with ID %s does not exist\n", backendId) + } + utils.Failf("Failed to get config for backend %s: %q\n", backendId, err.Error()) + } + + fmt.Println("Backend config:") + printProperty("ID", backendConfig.ID) + printProperty("Name", backendConfig.Name) + printProperty("Issuer", backendConfig.Issuer) + printProperty("Client ID", backendConfig.ClientID) + printProperty("Client secret", backendConfig.ClientSecret) + printProperty("Redirect URI", backendConfig.RedirectURI) +} + +func listBackends(backendService backend.Service) { + backends, err := backendService.ListBackends() + if err != nil { + utils.Failf("Failed to list backends: %q\n", err.Error()) + } + + if len(backends) == 0 { + fmt.Println("No backend configured") + return + } + for _, b := range backends { + fmt.Printf("\t - %s: (%s) - %s\n", b.ID, b.Name, b.Issuer) + } +} + +func init() { + backendCmd.AddCommand(backendShowCmd) +} diff --git a/polyculeconnect/cmd/root.go b/polyculeconnect/cmd/root.go index 31e70d5..3e792a3 100644 --- a/polyculeconnect/cmd/root.go +++ b/polyculeconnect/cmd/root.go @@ -6,8 +6,8 @@ import ( "github.com/spf13/cobra" ) -// rootCmd represents the base command when called without any subcommands -var rootCmd = &cobra.Command{ +// RootCmd represents the base command when called without any subcommands +var RootCmd = &cobra.Command{ Use: "polyculeconnect", Short: "You're in their DMs, I'm in their SSO", Long: `PolyculeConnect is a SSO OpenIDConnect provider which allows multiple authentication backends, @@ -20,7 +20,7 @@ and enables authentication federation among several infrastructures.`, // Execute adds all child commands to the root command and sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { - err := rootCmd.Execute() + err := RootCmd.Execute() if err != nil { os.Exit(1) } @@ -38,5 +38,5 @@ func init() { // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") // Disable the default `completion` command to generate the autocompletion files - rootCmd.Root().CompletionOptions.DisableDefaultCmd = true + RootCmd.Root().CompletionOptions.DisableDefaultCmd = true } diff --git a/polyculeconnect/cmd/utils/utils.go b/polyculeconnect/cmd/utils/utils.go new file mode 100644 index 0000000..137d768 --- /dev/null +++ b/polyculeconnect/cmd/utils/utils.go @@ -0,0 +1,40 @@ +package utils + +import ( + "fmt" + "os" + + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services" + "github.com/dexidp/dex/storage" +) + +// Fail displays the given error to stderr and exits the program with a returncode 1 +func Fail(errMsg string) { + fmt.Fprintln(os.Stderr, errMsg) + os.Exit(1) +} + +// Fail displays the given formatted error to stderr and exits the program with a returncode 1 +func Failf(msg string, args ...any) { + fmt.Fprintf(os.Stderr, msg+"\n", args...) + os.Exit(1) +} + +// InitConfig inits the configuration, and fails the program if an error occurs +func InitConfig(configPath string) *config.AppConfig { + conf, err := config.New(configPath) + if err != nil { + Failf("Failed to load the configuration: %s", err.Error()) + } + return conf +} + +// Initstorage inits the storage, and fails the program if an error occurs +func InitStorage(config *config.AppConfig) storage.Storage { + s, err := services.InitStorage(config) + if err != nil { + Failf("Failed to init the storage: %s", err.Error()) + } + return s +} diff --git a/polyculeconnect/config/config.go b/polyculeconnect/config/config.go index 8fdb135..c833165 100644 --- a/polyculeconnect/config/config.go +++ b/polyculeconnect/config/config.go @@ -66,6 +66,7 @@ const ( defaultStorageSSLCaFile = "" ) +// Deprecated: remove when we finally drop the JSON config type BackendConfig struct { Config *oidc.Config `json:"config"` Name string `json:"name"` @@ -145,6 +146,10 @@ func (ac *AppConfig) getConfFromEnv() { ac.StorageConfig.Ssl.Mode = getStringFromEnv(varStorageSSLMode, defaultStorageSSLMode) } +func (ac *AppConfig) RedirectURI() string { + return ac.OpenConnectConfig.Issuer + "/callback" +} + func New(filepath string) (*AppConfig, error) { var conf AppConfig conf.StorageConfig = &StorageConfig{} diff --git a/polyculeconnect/main.go b/polyculeconnect/main.go index 51129fe..d7b8e78 100644 --- a/polyculeconnect/main.go +++ b/polyculeconnect/main.go @@ -1,6 +1,9 @@ package main -import "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" +import ( + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" + _ "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/backend" +) func main() { cmd.Execute() diff --git a/polyculeconnect/services/idsecret.go b/polyculeconnect/services/idsecret.go new file mode 100644 index 0000000..373a24f --- /dev/null +++ b/polyculeconnect/services/idsecret.go @@ -0,0 +1,34 @@ +package services + +import ( + "crypto/rand" + "encoding/hex" + "fmt" +) + +// size in bytes of the client ids and secrets +const idSecretSize = 32 + +func generateRandomHex(size int) (string, error) { + raw := make([]byte, size) + n, err := rand.Read(raw) + if err != nil { + return "", fmt.Errorf("failed to read from random generator: %w", err) + } + if n != size { + return "", fmt.Errorf("failed to read from random generator (%d/%d)", n, size) + } + return hex.EncodeToString(raw), nil +} + +func GenerateClientIDSecret() (string, string, error) { + clientID, err := generateRandomHex(idSecretSize) + if err != nil { + return "", "", fmt.Errorf("failed to generate client id: %w", err) + } + clientSecret, err := generateRandomHex(idSecretSize) + if err != nil { + return "", "", fmt.Errorf("failed to generate client secret: %w", err) + } + return clientID, clientSecret, nil +} -- 2.45.2 From 9d2d49425d58ac7d53184111b24ef3436ef1719c Mon Sep 17 00:00:00 2001 From: Melora Hugues Date: Sun, 29 Oct 2023 13:32:09 +0100 Subject: [PATCH 4/4] chore: refactor serve command --- polyculeconnect/cmd/{ => serve}/serve.go | 30 +++++++----------------- polyculeconnect/main.go | 1 + polyculeconnect/services/storage.go | 9 +++++++ 3 files changed, 18 insertions(+), 22 deletions(-) rename polyculeconnect/cmd/{ => serve}/serve.go (84%) diff --git a/polyculeconnect/cmd/serve.go b/polyculeconnect/cmd/serve/serve.go similarity index 84% rename from polyculeconnect/cmd/serve.go rename to polyculeconnect/cmd/serve/serve.go index c12c7fe..57badd8 100644 --- a/polyculeconnect/cmd/serve.go +++ b/polyculeconnect/cmd/serve/serve.go @@ -1,4 +1,4 @@ -package cmd +package serve import ( "context" @@ -6,12 +6,12 @@ import ( "os/signal" "time" - "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/utils" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/server" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/services" - "github.com/dexidp/dex/connector/oidc" dex_server "github.com/dexidp/dex/server" "github.com/prometheus/client_golang/prometheus" "github.com/spf13/cobra" @@ -35,18 +35,11 @@ variables`, func serve() { mainCtx, cancel := context.WithCancel(context.Background()) - conf, err := config.New(configPath) - if err != nil { - panic(err) - } - + conf := utils.InitConfig(configPath) logger.Init(conf.LogLevel) logger.L.Infof("Initialized logger with level %v", conf.LogLevel) - storageType, err := services.InitStorage(conf) - if err != nil { - logger.L.Fatalf("Failed to initialize storage backend: %s", err.Error()) - } + storageType := utils.InitStorage(conf) logger.L.Infof("Initialized storage backend %q", conf.StorageType) dexConf := dex_server.Config{ Web: dex_server.WebConfig{ @@ -64,7 +57,7 @@ func serve() { logger.L.Info("Initializing authentication backends") - dex_server.ConnectorsConfig["refuseAll"] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) } + dex_server.ConnectorsConfig[connector.TypeRefuseAll] = func() dex_server.ConnectorConfig { return new(connector.RefuseAllConfig) } connectors, err := dexConf.Storage.ListConnectors() if err != nil { logger.L.Fatalf("Failed to get existing connectors: %s", err.Error()) @@ -74,14 +67,7 @@ func serve() { connectorIDs = append(connectorIDs, conn.ID) } - backend := config.BackendConfig{ - Config: &oidc.Config{}, - Name: "RefuseAll", - ID: "null", - Type: "refuseAll", - } - - if err := services.CreateConnector(&backend, &dexConf, connectorIDs); err != nil { + if err := services.AddDefaultBackend(storageType); err != nil { logger.L.Errorf("Failed to add connector for backend RefuseAll to stage: %s", err.Error()) } @@ -140,7 +126,7 @@ func serve() { } func init() { - rootCmd.AddCommand(serveCmd) + cmd.RootCmd.AddCommand(serveCmd) // Here you will define your flags and configuration settings. diff --git a/polyculeconnect/main.go b/polyculeconnect/main.go index d7b8e78..51395ff 100644 --- a/polyculeconnect/main.go +++ b/polyculeconnect/main.go @@ -3,6 +3,7 @@ package main import ( "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd" _ "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/backend" + _ "git.faercol.me/faercol/polyculeconnect/polyculeconnect/cmd/serve" ) func main() { diff --git a/polyculeconnect/services/storage.go b/polyculeconnect/services/storage.go index df6160a..e85d1a6 100644 --- a/polyculeconnect/services/storage.go +++ b/polyculeconnect/services/storage.go @@ -1,9 +1,11 @@ package services import ( + "errors" "fmt" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/config" + "git.faercol.me/faercol/polyculeconnect/polyculeconnect/connector" "git.faercol.me/faercol/polyculeconnect/polyculeconnect/logger" "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/memory" @@ -29,3 +31,10 @@ func InitStorage(conf *config.AppConfig) (storage.Storage, error) { } return storageType, nil } + +func AddDefaultBackend(s storage.Storage) error { + if err := s.CreateConnector(connector.RefuseAllConnectorConfig); err != nil && !errors.Is(err, storage.ErrAlreadyExists) { + return fmt.Errorf("failed to add default backend: %w", err) + } + return nil +} -- 2.45.2