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) }) }