diff --git a/src/jetstream/auth_test.go b/src/jetstream/auth_test.go index b3b4c9ec29..3e83c47b29 100644 --- a/src/jetstream/auth_test.go +++ b/src/jetstream/auth_test.go @@ -183,8 +183,8 @@ func TestLoginToCNSI(t *testing.T) { DopplerLoggingEndpoint: mockDopplerEndpoint, } - expectedCNSIRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso"}). - AddRow(mockCNSIGUID, mockCNSI.Name, stringCFType, mockUAA.URL, mockCNSI.AuthorizationEndpoint, mockCNSI.TokenEndpoint, mockCNSI.DopplerLoggingEndpoint, true, mockCNSI.ClientId, cipherClientSecret, true) + expectedCNSIRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso", "sub_type", "meta_data"}). + AddRow(mockCNSIGUID, mockCNSI.Name, stringCFType, mockUAA.URL, mockCNSI.AuthorizationEndpoint, mockCNSI.TokenEndpoint, mockCNSI.DopplerLoggingEndpoint, true, mockCNSI.ClientId, cipherClientSecret, true, "", "") mock.ExpectQuery(selectAnyFromCNSIs). WithArgs(mockCNSIGUID). diff --git a/src/jetstream/cnsi.go b/src/jetstream/cnsi.go index 6698503fc7..3660326fc7 100644 --- a/src/jetstream/cnsi.go +++ b/src/jetstream/cnsi.go @@ -57,13 +57,14 @@ func (p *portalProxy) RegisterEndpoint(c echo.Context, fetchInfo interfaces.Info cnsiClientId := c.FormValue("cnsi_client_id") cnsiClientSecret := c.FormValue("cnsi_client_secret") + subType := c.FormValue("sub_type") if cnsiClientId == "" { cnsiClientId = p.GetConfig().CFClient cnsiClientSecret = p.GetConfig().CFClientSecret } - newCNSI, err := p.DoRegisterEndpoint(cnsiName, apiEndpoint, skipSSLValidation, cnsiClientId, cnsiClientSecret, ssoAllowed, fetchInfo) + newCNSI, err := p.DoRegisterEndpoint(cnsiName, apiEndpoint, skipSSLValidation, cnsiClientId, cnsiClientSecret, ssoAllowed, subType, fetchInfo) if err != nil { return err } @@ -72,7 +73,7 @@ func (p *portalProxy) RegisterEndpoint(c echo.Context, fetchInfo interfaces.Info return nil } -func (p *portalProxy) DoRegisterEndpoint(cnsiName string, apiEndpoint string, skipSSLValidation bool, clientId string, clientSecret string, ssoAllowed bool, fetchInfo interfaces.InfoFunc) (interfaces.CNSIRecord, error) { +func (p *portalProxy) DoRegisterEndpoint(cnsiName string, apiEndpoint string, skipSSLValidation bool, clientId string, clientSecret string, ssoAllowed bool, subType string, fetchInfo interfaces.InfoFunc) (interfaces.CNSIRecord, error) { if len(cnsiName) == 0 || len(apiEndpoint) == 0 { return interfaces.CNSIRecord{}, interfaces.NewHTTPShadowError( @@ -129,6 +130,7 @@ func (p *portalProxy) DoRegisterEndpoint(cnsiName string, apiEndpoint string, sk newCNSI.ClientId = clientId newCNSI.ClientSecret = clientSecret newCNSI.SSOAllowed = ssoAllowed + newCNSI.SubType = subType err = p.setCNSIRecord(guid, newCNSI) @@ -264,6 +266,24 @@ func marshalClusterList(clusterList []*interfaces.ConnectedEndpoint) ([]byte, er return jsonString, nil } +func (p *portalProxy) UpdateEndointMetadata(guid string, metadata string) error { + log.Debug("UpdateEndointMetadata") + cnsiRepo, err := cnsis.NewPostgresCNSIRepository(p.DatabaseConnectionPool) + if err != nil { + log.Errorf(dbReferenceError, err) + return fmt.Errorf(dbReferenceError, err) + } + + err = cnsiRepo.UpdateMetadata(guid, metadata) + if err != nil { + msg := "Unable to update endpoint metadata: %v" + log.Errorf(msg, err) + return fmt.Errorf(msg, err) + } + + return nil +} + func (p *portalProxy) GetCNSIRecord(guid string) (interfaces.CNSIRecord, error) { log.Debug("GetCNSIRecord") cnsiRepo, err := cnsis.NewPostgresCNSIRepository(p.DatabaseConnectionPool) diff --git a/src/jetstream/cnsi_test.go b/src/jetstream/cnsi_test.go index a085f4aa1e..75d06549d2 100644 --- a/src/jetstream/cnsi_test.go +++ b/src/jetstream/cnsi_test.go @@ -33,7 +33,7 @@ func TestRegisterCFCluster(t *testing.T) { defer db.Close() mock.ExpectExec(insertIntoCNSIs). - WithArgs(sqlmock.AnyArg(), "Some fancy CF Cluster", "cf", mockV2Info.URL, mockAuthEndpoint, mockTokenEndpoint, mockDopplerEndpoint, true, mockClientId, sqlmock.AnyArg(), false). + WithArgs(sqlmock.AnyArg(), "Some fancy CF Cluster", "cf", mockV2Info.URL, mockAuthEndpoint, mockTokenEndpoint, mockDopplerEndpoint, true, mockClientId, sqlmock.AnyArg(), false, "", ""). WillReturnResult(sqlmock.NewResult(1, 1)) if err := pp.RegisterEndpoint(ctx, getCFPlugin(pp, "cf").Info); err != nil { diff --git a/src/jetstream/datastore/20190305144600_EndpointSubtype.go b/src/jetstream/datastore/20190305144600_EndpointSubtype.go new file mode 100644 index 0000000000..ba9a4fbc3a --- /dev/null +++ b/src/jetstream/datastore/20190305144600_EndpointSubtype.go @@ -0,0 +1,26 @@ +package datastore + +import ( + "database/sql" + + "bitbucket.org/liamstask/goose/lib/goose" +) + +func init() { + RegisterMigration(20190305144600, "EndpointSubtype", func(txn *sql.Tx, conf *goose.DBConf) error { + + addColumn := "ALTER TABLE cnsis ADD sub_type VARCHAR(64) DEFAULT NULL" + _, err := txn.Exec(addColumn) + if err != nil { + return err + } + + addColumn = "ALTER TABLE cnsis ADD meta_data TEXT DEFAULT NULL" + _, err = txn.Exec(addColumn) + if err != nil { + return err + } + + return nil + }) +} diff --git a/src/jetstream/mock_server_test.go b/src/jetstream/mock_server_test.go index 08e719a80d..cdedcce327 100644 --- a/src/jetstream/mock_server_test.go +++ b/src/jetstream/mock_server_test.go @@ -156,18 +156,18 @@ func expectOneRow() sqlmock.Rows { func expectCFRow() sqlmock.Rows { return sqlmock.NewRows(rowFieldsForCNSI). - AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, true) + AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, true, "", "") } func expectCERow() sqlmock.Rows { return sqlmock.NewRows(rowFieldsForCNSI). - AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, "", true, mockClientId, cipherClientSecret, true) + AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, "", true, mockClientId, cipherClientSecret, true, "", "") } func expectCFAndCERows() sqlmock.Rows { return sqlmock.NewRows(rowFieldsForCNSI). - AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret). - AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, "", true, mockClientId, cipherClientSecret) + AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, false, "", ""). + AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, "", true, mockClientId, cipherClientSecret, false, "", "") } func expectTokenRow() sqlmock.Rows { @@ -276,7 +276,7 @@ const ( getDbVersion = `SELECT version_id FROM goose_db_version WHERE is_applied = '1' ORDER BY id DESC LIMIT 1` ) -var rowFieldsForCNSI = []string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso"} +var rowFieldsForCNSI = []string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso", "sub_type", "meta_data"} var mockEncryptionKey = make([]byte, 32) diff --git a/src/jetstream/oauth_requests_test.go b/src/jetstream/oauth_requests_test.go index fba2d8ac49..926cf00c9b 100644 --- a/src/jetstream/oauth_requests_test.go +++ b/src/jetstream/oauth_requests_test.go @@ -107,8 +107,8 @@ func TestDoOauthFlowRequestWithValidToken(t *testing.T) { // p.GetCNSIRecord(r.GUID) -> cnsiRepo.Find(guid) - expectedCNSIRecordRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso"}). - AddRow(mockCNSI.GUID, mockCNSI.Name, mockCNSI.CNSIType, mockURLasString, mockCNSI.AuthorizationEndpoint, mockCNSI.TokenEndpoint, mockCNSI.DopplerLoggingEndpoint, true, mockCNSI.ClientId, cipherClientSecret, true) + expectedCNSIRecordRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso", "sub_type", "meta_data"}). + AddRow(mockCNSI.GUID, mockCNSI.Name, mockCNSI.CNSIType, mockURLasString, mockCNSI.AuthorizationEndpoint, mockCNSI.TokenEndpoint, mockCNSI.DopplerLoggingEndpoint, true, mockCNSI.ClientId, cipherClientSecret, true, "", "") mock.ExpectQuery(selectAnyFromCNSIs). WithArgs(mockCNSIGUID). WillReturnRows(expectedCNSIRecordRow) @@ -238,8 +238,8 @@ func TestDoOauthFlowRequestWithExpiredToken(t *testing.T) { WillReturnRows(expectedCNSITokenRow) // p.GetCNSIRecord(r.GUID) -> cnsiRepo.Find(guid) - expectedCNSIRecordRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso"}). - AddRow(mockCNSI.GUID, mockCNSI.Name, mockCNSI.CNSIType, mockURLasString, mockCNSI.AuthorizationEndpoint, mockCNSI.TokenEndpoint, mockCNSI.DopplerLoggingEndpoint, true, mockCNSI.ClientId, cipherClientSecret, true) + expectedCNSIRecordRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso", "sub_type", "meta_data"}). + AddRow(mockCNSI.GUID, mockCNSI.Name, mockCNSI.CNSIType, mockURLasString, mockCNSI.AuthorizationEndpoint, mockCNSI.TokenEndpoint, mockCNSI.DopplerLoggingEndpoint, true, mockCNSI.ClientId, cipherClientSecret, true, "", "") mock.ExpectQuery(selectAnyFromCNSIs). WithArgs(mockCNSIGUID). WillReturnRows(expectedCNSIRecordRow) diff --git a/src/jetstream/passthrough_test.go b/src/jetstream/passthrough_test.go index 6fd31cd846..5aa2f2e756 100644 --- a/src/jetstream/passthrough_test.go +++ b/src/jetstream/passthrough_test.go @@ -272,8 +272,8 @@ func TestValidateCNSIListWithValidGUID(t *testing.T) { _, _, _, pp, db, mock := setupHTTPTest(req) defer db.Close() - expectedCNSIRecordRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso"}). - AddRow("valid-guid-abc123", "mock-name", "cf", "http://localhost", "http://localhost", "http://localhost", mockDopplerEndpoint, true, mockClientId, cipherClientSecret, true) + expectedCNSIRecordRow := sqlmock.NewRows([]string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "allow_sso", "sub_type", "meta_data"}). + AddRow("valid-guid-abc123", "mock-name", "cf", "http://localhost", "http://localhost", "http://localhost", mockDopplerEndpoint, true, mockClientId, cipherClientSecret, true, "", "") mock.ExpectQuery(selectAnyFromCNSIs). WithArgs("valid-guid-abc123"). WillReturnRows(expectedCNSIRecordRow) diff --git a/src/jetstream/plugins/cloudfoundry/main.go b/src/jetstream/plugins/cloudfoundry/main.go index 115dca1d99..ccc1850065 100644 --- a/src/jetstream/plugins/cloudfoundry/main.go +++ b/src/jetstream/plugins/cloudfoundry/main.go @@ -111,7 +111,7 @@ func (c *CloudFoundrySpecification) cfLoginHook(context echo.Context) error { log.Infof("Auto-registering cloud foundry endpoint %s as \"%s\"", cfAPI, autoRegName) // Auto-register the Cloud Foundry - cfCnsi, err = c.portalProxy.DoRegisterEndpoint(autoRegName, cfAPI, true, c.portalProxy.GetConfig().CFClient, c.portalProxy.GetConfig().CFClientSecret, false, cfEndpointSpec.Info) + cfCnsi, err = c.portalProxy.DoRegisterEndpoint(autoRegName, cfAPI, true, c.portalProxy.GetConfig().CFClient, c.portalProxy.GetConfig().CFClientSecret, false, "", cfEndpointSpec.Info) if err != nil { log.Fatal("Could not auto-register Cloud Foundry endpoint", err) return nil diff --git a/src/jetstream/repository/cnsis/cnsis.go b/src/jetstream/repository/cnsis/cnsis.go index a90e3e1580..1aad608178 100644 --- a/src/jetstream/repository/cnsis/cnsis.go +++ b/src/jetstream/repository/cnsis/cnsis.go @@ -13,6 +13,7 @@ type Repository interface { Delete(guid string) error Save(guid string, cnsiRecord interfaces.CNSIRecord, encryptionKey []byte) error Update(guid string, ssoAllowed bool) error + UpdateMetadata(guid string, metadata string) error } type Endpoint interface { diff --git a/src/jetstream/repository/cnsis/pgsql_cnsis.go b/src/jetstream/repository/cnsis/pgsql_cnsis.go index e885f462f9..81201f6e3d 100644 --- a/src/jetstream/repository/cnsis/pgsql_cnsis.go +++ b/src/jetstream/repository/cnsis/pgsql_cnsis.go @@ -12,29 +12,32 @@ import ( log "github.com/sirupsen/logrus" ) -var listCNSIs = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed +var listCNSIs = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed, sub_type, meta_data FROM cnsis` -var listCNSIsByUser = `SELECT c.guid, c.name, c.cnsi_type, c.api_endpoint, c.doppler_logging_endpoint, t.user_guid, t.token_expiry, c.skip_ssl_validation, t.disconnected, t.meta_data +var listCNSIsByUser = `SELECT c.guid, c.name, c.cnsi_type, c.api_endpoint, c.doppler_logging_endpoint, t.user_guid, t.token_expiry, c.skip_ssl_validation, t.disconnected, t.meta_data, c.sub_type, c.meta_data as endpoint_metadata FROM cnsis c, tokens t WHERE c.guid = t.cnsi_guid AND t.token_type=$1 AND t.user_guid=$2 AND t.disconnected = '0'` -var findCNSI = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed +var findCNSI = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed, sub_type, meta_data FROM cnsis WHERE guid=$1` -var findCNSIByAPIEndpoint = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed +var findCNSIByAPIEndpoint = `SELECT guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed, sub_type, meta_data FROM cnsis WHERE api_endpoint=$1` -var saveCNSI = `INSERT INTO cnsis (guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)` +var saveCNSI = `INSERT INTO cnsis (guid, name, cnsi_type, api_endpoint, auth_endpoint, token_endpoint, doppler_logging_endpoint, skip_ssl_validation, client_id, client_secret, sso_allowed, sub_type, meta_data) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)` var deleteCNSI = `DELETE FROM cnsis WHERE guid = $1` // Just update the SSO Allowed state for now var updateCNSI = `UPDATE cnsis SET sso_allowed = $1 WHERE guid = $2` +// Update the metadata +var updateCNSIMetadata = `UPDATE cnsis SET metadata = $1 WHERE guid = $2` + // PostgresCNSIRepository is a PostgreSQL-backed CNSI repository type PostgresCNSIRepository struct { db *sql.DB @@ -55,6 +58,7 @@ func InitRepositoryProvider(databaseProvider string) { saveCNSI = datastore.ModifySQLStatement(saveCNSI, databaseProvider) deleteCNSI = datastore.ModifySQLStatement(deleteCNSI, databaseProvider) updateCNSI = datastore.ModifySQLStatement(updateCNSI, databaseProvider) + updateCNSIMetadata = datastore.ModifySQLStatement(updateCNSIMetadata, databaseProvider) } // List - Returns a list of CNSI Records @@ -74,11 +78,13 @@ func (p *PostgresCNSIRepository) List(encryptionKey []byte) ([]*interfaces.CNSIR pCNSIType string pURL string cipherTextClientSecret []byte + subType sql.NullString + metadata sql.NullString ) cnsi := new(interfaces.CNSIRecord) - err := rows.Scan(&cnsi.GUID, &cnsi.Name, &pCNSIType, &pURL, &cnsi.AuthorizationEndpoint, &cnsi.TokenEndpoint, &cnsi.DopplerLoggingEndpoint, &cnsi.SkipSSLValidation, &cnsi.ClientId, &cipherTextClientSecret, &cnsi.SSOAllowed) + err := rows.Scan(&cnsi.GUID, &cnsi.Name, &pCNSIType, &pURL, &cnsi.AuthorizationEndpoint, &cnsi.TokenEndpoint, &cnsi.DopplerLoggingEndpoint, &cnsi.SkipSSLValidation, &cnsi.ClientId, &cipherTextClientSecret, &cnsi.SSOAllowed, &subType, &metadata) if err != nil { return nil, fmt.Errorf("Unable to scan CNSI records: %v", err) } @@ -89,6 +95,14 @@ func (p *PostgresCNSIRepository) List(encryptionKey []byte) ([]*interfaces.CNSIR return nil, fmt.Errorf("Unable to parse API Endpoint: %v", err) } + if subType.Valid { + cnsi.SubType = subType.String + } + + if metadata.Valid { + cnsi.Metadata = metadata.String + } + if len(cipherTextClientSecret) > 0 { plaintextClientSecret, err := crypto.DecryptToken(encryptionKey, cipherTextClientSecret) if err != nil { @@ -129,14 +143,25 @@ func (p *PostgresCNSIRepository) ListByUser(userGUID string) ([]*interfaces.Conn pCNSIType string pURL string disconnected bool + subType sql.NullString + metadata sql.NullString ) cluster := new(interfaces.ConnectedEndpoint) - err := rows.Scan(&cluster.GUID, &cluster.Name, &pCNSIType, &pURL, &cluster.DopplerLoggingEndpoint, &cluster.Account, &cluster.TokenExpiry, &cluster.SkipSSLValidation, &disconnected, &cluster.TokenMetadata) + err := rows.Scan(&cluster.GUID, &cluster.Name, &pCNSIType, &pURL, &cluster.DopplerLoggingEndpoint, &cluster.Account, &cluster.TokenExpiry, &cluster.SkipSSLValidation, + &disconnected, &cluster.TokenMetadata, &subType, &metadata) if err != nil { return nil, fmt.Errorf("Unable to scan cluster records: %v", err) } + if subType.Valid { + cluster.SubType = subType.String + } + + if metadata.Valid { + cluster.EndpointMetadata = metadata.String + } + cluster.CNSIType = pCNSIType if cluster.APIEndpoint, err = url.Parse(pURL); err != nil { @@ -173,12 +198,14 @@ func (p *PostgresCNSIRepository) findBy(query, match string, encryptionKey []byt pCNSIType string pURL string cipherTextClientSecret []byte + subType sql.NullString + metadata sql.NullString ) cnsi := new(interfaces.CNSIRecord) err := p.db.QueryRow(query, match).Scan(&cnsi.GUID, &cnsi.Name, &pCNSIType, &pURL, - &cnsi.AuthorizationEndpoint, &cnsi.TokenEndpoint, &cnsi.DopplerLoggingEndpoint, &cnsi.SkipSSLValidation, &cnsi.ClientId, &cipherTextClientSecret, &cnsi.SSOAllowed) + &cnsi.AuthorizationEndpoint, &cnsi.TokenEndpoint, &cnsi.DopplerLoggingEndpoint, &cnsi.SkipSSLValidation, &cnsi.ClientId, &cipherTextClientSecret, &cnsi.SSOAllowed, &subType, &metadata) switch { case err == sql.ErrNoRows: @@ -189,6 +216,14 @@ func (p *PostgresCNSIRepository) findBy(query, match string, encryptionKey []byt // do nothing } + if subType.Valid { + cnsi.SubType = subType.String + } + + if metadata.Valid { + cnsi.Metadata = metadata.String + } + cnsi.CNSIType = pCNSIType if cnsi.APIEndpoint, err = url.Parse(pURL); err != nil { @@ -218,7 +253,7 @@ func (p *PostgresCNSIRepository) Save(guid string, cnsi interfaces.CNSIRecord, e } if _, err := p.db.Exec(saveCNSI, guid, cnsi.Name, fmt.Sprintf("%s", cnsi.CNSIType), fmt.Sprintf("%s", cnsi.APIEndpoint), cnsi.AuthorizationEndpoint, cnsi.TokenEndpoint, cnsi.DopplerLoggingEndpoint, cnsi.SkipSSLValidation, - cnsi.ClientId, cipherTextClientSecret, cnsi.SSOAllowed); err != nil { + cnsi.ClientId, cipherTextClientSecret, cnsi.SSOAllowed, cnsi.SubType, cnsi.Metadata); err != nil { return fmt.Errorf("Unable to Save CNSI record: %v", err) } @@ -271,3 +306,40 @@ func (p *PostgresCNSIRepository) Update(guid string, ssoAllowed bool) error { return nil } + +// UpdateMetadata - Update an endpoint's metadata +func (p *PostgresCNSIRepository) UpdateMetadata(guid string, metadata string) error { + log.Debug("UpdateMetadata") + + if guid == "" { + msg := "Unable to update Endpoint without a valid guid." + log.Debug(msg) + return errors.New(msg) + } + + var err error + + result, err := p.db.Exec(updateCNSIMetadata, metadata, guid) + if err != nil { + msg := "Unable to UPDATE endpoint: %v" + log.Debugf(msg, err) + return fmt.Errorf(msg, err) + } + + rowsUpdates, err := result.RowsAffected() + if err != nil { + return errors.New("Unable to UPDATE endpoint: could not determine number of rows that were updated") + } + + if rowsUpdates < 1 { + return errors.New("Unable to UPDATE endpoint: no rows were updated") + } + + if rowsUpdates > 1 { + log.Warn("UPDATE endpoint: More than 1 row was updated (expected only 1)") + } + + log.Debug("Endpoint UPDATE complete") + + return nil +} diff --git a/src/jetstream/repository/cnsis/pgsql_cnsis_test.go b/src/jetstream/repository/cnsis/pgsql_cnsis_test.go index 7c82be5dae..b097cd7bea 100644 --- a/src/jetstream/repository/cnsis/pgsql_cnsis_test.go +++ b/src/jetstream/repository/cnsis/pgsql_cnsis_test.go @@ -32,7 +32,7 @@ func TestPgSQLCNSIs(t *testing.T) { insertIntoCNSIs = `INSERT INTO cnsis` deleteFromCNSIs = `DELETE FROM cnsis WHERE (.+)` rowFieldsForCNSI = []string{"guid", "name", "cnsi_type", "api_endpoint", "auth_endpoint", - "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "sso_allowed"} + "token_endpoint", "doppler_logging_endpoint", "skip_ssl_validation", "client_id", "client_secret", "sso_allowed", "sub_type", "meta_data"} mockEncryptionKey = make([]byte, 32) ) cipherClientSecret, _ := crypto.EncryptToken(mockEncryptionKey, mockClientSecret) @@ -116,8 +116,8 @@ func TestPgSQLCNSIs(t *testing.T) { expectedList = append(expectedList, r1, r2) mockCFAndCERows = sqlmock.NewRows(rowFieldsForCNSI). - AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, false). - AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, "", true, mockClientId, cipherClientSecret, false) + AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, false, "", ""). + AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, "", true, mockClientId, cipherClientSecret, false, "", "") mock.ExpectQuery(selectAnyFromCNSIs). WillReturnRows(mockCFAndCERows) @@ -182,7 +182,7 @@ func TestPgSQLCNSIs(t *testing.T) { var ( //SELECT c.guid, c.name, c.cnsi_type, c.api_endpoint, c.doppler_logging_endpoint, t.user_guid, t.token_expiry, c.skip_ssl_validation, t.disconnected, t.meta_data //rowFieldsForCluster = []string{"guid", "name", "cnsi_type", "api_endpoint", "account", "token_expiry", "skip_ssl_validation"} - rowFieldsForCluster = []string{"guid", "name", "cnsi_type", "api_endpoint", "doppler_logging_endpoint", "account", "token_expiry", "skip_ssl_validation", "disconnected", "meta_data"} + rowFieldsForCluster = []string{"guid", "name", "cnsi_type", "api_endpoint", "doppler_logging_endpoint", "account", "token_expiry", "skip_ssl_validation", "disconnected", "meta_data", "sub_type", "endpoint_metadata"} expectedList []*interfaces.ConnectedEndpoint mockAccount = "asd-gjfg-bob" ) @@ -244,8 +244,8 @@ func TestPgSQLCNSIs(t *testing.T) { expectedList = append(expectedList, r1, r2) mockClusterList = sqlmock.NewRows(rowFieldsForCluster). - AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockDopplerEndpoint, mockAccount, mockTokenExpiry, true, false, ""). - AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockDopplerEndpoint, mockAccount, mockTokenExpiry, true, false, "") + AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockDopplerEndpoint, mockAccount, mockTokenExpiry, true, false, "", "", ""). + AddRow(mockCEGUID, "Some fancy HCE Cluster", "hce", mockAPIEndpoint, mockDopplerEndpoint, mockAccount, mockTokenExpiry, true, false, "", "", "") mock.ExpectQuery(selectFromCNSIandTokensWhere). WillReturnRows(mockClusterList) @@ -320,7 +320,7 @@ func TestPgSQLCNSIs(t *testing.T) { expectedCNSIRecord := interfaces.CNSIRecord{GUID: mockCFGUID, Name: "Some fancy CF Cluster", CNSIType: "cf", APIEndpoint: u, AuthorizationEndpoint: mockAuthEndpoint, TokenEndpoint: mockAuthEndpoint, DopplerLoggingEndpoint: mockDopplerEndpoint, SkipSSLValidation: true, ClientId: mockClientId, ClientSecret: mockClientSecret, SSOAllowed: false} rs := sqlmock.NewRows(rowFieldsForCNSI). - AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, false) + AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, false, "", "") mock.ExpectQuery(selectFromCNSIsWhere). WillReturnRows(rs) @@ -417,7 +417,7 @@ func TestPgSQLCNSIs(t *testing.T) { expectedCNSIRecord := interfaces.CNSIRecord{GUID: mockCFGUID, Name: "Some fancy CF Cluster", CNSIType: "cf", APIEndpoint: u, AuthorizationEndpoint: mockAuthEndpoint, TokenEndpoint: mockAuthEndpoint, DopplerLoggingEndpoint: mockDopplerEndpoint, SkipSSLValidation: true, ClientId: mockClientId, ClientSecret: mockClientSecret, SSOAllowed: true} rs := sqlmock.NewRows(rowFieldsForCNSI). - AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, true) + AddRow(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, cipherClientSecret, true, "", "") mock.ExpectQuery(selectFromCNSIsWhere). WillReturnRows(rs) @@ -515,7 +515,7 @@ func TestPgSQLCNSIs(t *testing.T) { cnsi := interfaces.CNSIRecord{GUID: mockCFGUID, Name: "Some fancy CF Cluster", CNSIType: "cf", APIEndpoint: u, AuthorizationEndpoint: mockAuthEndpoint, TokenEndpoint: mockAuthEndpoint, DopplerLoggingEndpoint: mockDopplerEndpoint, SkipSSLValidation: true, ClientId: mockClientId, ClientSecret: mockClientSecret, SSOAllowed: true} mock.ExpectExec(insertIntoCNSIs). - WithArgs(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, sqlmock.AnyArg(), true). + WithArgs(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, sqlmock.AnyArg(), true, sqlmock.AnyArg(), sqlmock.AnyArg()). WillReturnResult(sqlmock.NewResult(1, 1)) Convey("there should be no error returned", func() { @@ -536,7 +536,7 @@ func TestPgSQLCNSIs(t *testing.T) { expectedErrorMessage := fmt.Sprintf("Unable to Save CNSI record: %s", unknownDBError) mock.ExpectExec(insertIntoCNSIs). - WithArgs(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, sqlmock.AnyArg(), true). + WithArgs(mockCFGUID, "Some fancy CF Cluster", "cf", mockAPIEndpoint, mockAuthEndpoint, mockAuthEndpoint, mockDopplerEndpoint, true, mockClientId, sqlmock.AnyArg(), true, sqlmock.AnyArg(), sqlmock.AnyArg()). WillReturnError(errors.New(unknownDBError)) Convey("there should be an error returned", func() { diff --git a/src/jetstream/repository/interfaces/portal_proxy.go b/src/jetstream/repository/interfaces/portal_proxy.go index 8a81113513..29e7919081 100644 --- a/src/jetstream/repository/interfaces/portal_proxy.go +++ b/src/jetstream/repository/interfaces/portal_proxy.go @@ -15,7 +15,7 @@ type PortalProxy interface { GetHttpClientForRequest(req *http.Request, skipSSLValidation bool) http.Client RegisterEndpoint(c echo.Context, fetchInfo InfoFunc) error - DoRegisterEndpoint(cnsiName string, apiEndpoint string, skipSSLValidation bool, clientId string, clientSecret string, ssoAllowed bool, fetchInfo InfoFunc) (CNSIRecord, error) + DoRegisterEndpoint(cnsiName string, apiEndpoint string, skipSSLValidation bool, clientId string, clientSecret string, ssoAllowed bool, subType string, fetchInfo InfoFunc) (CNSIRecord, error) GetEndpointTypeSpec(typeName string) (EndpointPlugin, error) @@ -45,6 +45,7 @@ type PortalProxy interface { GetConfig() *PortalConfig Env() *env.VarSet ListEndpointsByUser(userGUID string) ([]*ConnectedEndpoint, error) + UpdateEndointMetadata(guid string, metadata string) error // UAA Token GetUAATokenRecord(userGUID string) (TokenRecord, error) diff --git a/src/jetstream/repository/interfaces/structs.go b/src/jetstream/repository/interfaces/structs.go index ceab8b18e7..666a6aedfb 100644 --- a/src/jetstream/repository/interfaces/structs.go +++ b/src/jetstream/repository/interfaces/structs.go @@ -44,6 +44,8 @@ type CNSIRecord struct { ClientId string `json:"client_id"` ClientSecret string `json:"-"` SSOAllowed bool `json:"sso_allowed"` + SubType string `json:"sub_type"` + Metadata string `json:"metadata"` } // ConnectedEndpoint @@ -58,6 +60,8 @@ type ConnectedEndpoint struct { AuthorizationEndpoint string `json:"-"` SkipSSLValidation bool `json:"skip_ssl_validation"` TokenMetadata string `json:"-"` + SubType string `json:"sub_type"` + EndpointMetadata string `json:"metadata"` } const (