From 9c1ffe8c20e9414176ad5f390d713a734d5de0e8 Mon Sep 17 00:00:00 2001 From: Gabriel Adrian Samfira Date: Wed, 17 Apr 2024 12:10:00 +0000 Subject: [PATCH] Enforce same endpoint when updating credentials When updating credentials on an entity, we must ensure that the new credentials belong to the same endpoint as the entity. When an entity is created, the endpoint is determined by the credentials that were used during the create operation. From that point forward the entity is associated with an endpoint, and that cannot change. Signed-off-by: Gabriel Adrian Samfira --- database/sql/enterprise.go | 23 +++++++++++++- database/sql/enterprise_test.go | 48 +++++++++++++++++++++-------- database/sql/organizations.go | 22 +++++++++++++- database/sql/organizations_test.go | 49 ++++++++++++++++++++++-------- database/sql/repositories.go | 23 +++++++++++++- database/sql/repositories_test.go | 49 ++++++++++++++++++++++-------- database/sql/util.go | 16 +++++++++- params/params.go | 3 ++ util/util.go | 2 +- 9 files changed, 191 insertions(+), 44 deletions(-) diff --git a/database/sql/enterprise.go b/database/sql/enterprise.go index 9fa3c73c..53be988d 100644 --- a/database/sql/enterprise.go +++ b/database/sql/enterprise.go @@ -45,7 +45,12 @@ func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name, credentialsNam if err != nil { return errors.Wrap(err, "creating enterprise") } + if creds.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") + } newEnterprise.CredentialsID = &creds.ID + newEnterprise.CredentialsName = creds.Name + newEnterprise.EndpointName = creds.EndpointName q := tx.Create(&newEnterprise) if q.Error != nil { @@ -53,6 +58,7 @@ func (s *sqlDatabase) CreateEnterprise(ctx context.Context, name, credentialsNam } newEnterprise.Credentials = creds + newEnterprise.Endpoint = creds.Endpoint return nil }) @@ -132,16 +138,27 @@ func (s *sqlDatabase) UpdateEnterprise(ctx context.Context, enterpriseID string, var creds GithubCredentials err := s.conn.Transaction(func(tx *gorm.DB) error { var err error - enterprise, err = s.getEnterpriseByID(ctx, tx, enterpriseID, "Credentials", "Endpoint") + enterprise, err = s.getEnterpriseByID(ctx, tx, enterpriseID) if err != nil { return errors.Wrap(err, "fetching enterprise") } + if enterprise.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "enterprise has no endpoint") + } + if param.CredentialsName != "" { creds, err = s.getGithubCredentialsByName(ctx, tx, param.CredentialsName, false) if err != nil { return errors.Wrap(err, "fetching credentials") } + if creds.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") + } + + if *creds.EndpointName != *enterprise.EndpointName { + return errors.Wrap(runnerErrors.ErrBadRequest, "endpoint mismatch") + } enterprise.CredentialsID = &creds.ID } if param.WebhookSecret != "" { @@ -171,6 +188,10 @@ func (s *sqlDatabase) UpdateEnterprise(ctx context.Context, enterpriseID string, return params.Enterprise{}, errors.Wrap(err, "updating enterprise") } + enterprise, err = s.getEnterpriseByID(ctx, s.conn, enterpriseID, "Endpoint", "Credentials") + if err != nil { + return params.Enterprise{}, errors.Wrap(err, "updating enterprise") + } newParams, err := s.sqlToCommonEnterprise(enterprise, true) if err != nil { return params.Enterprise{}, errors.Wrap(err, "updating enterprise") diff --git a/database/sql/enterprise_test.go b/database/sql/enterprise_test.go index 3509f946..b442ec86 100644 --- a/database/sql/enterprise_test.go +++ b/database/sql/enterprise_test.go @@ -218,7 +218,11 @@ func (s *EnterpriseTestSuite) TestCreateEnterpriseDBCreateErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). WithArgs(s.Fixtures.Enterprises[0].CredentialsName). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.testCreds.ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}).AddRow(s.testCreds.ID, s.testCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.testCreds.Endpoint)) s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `enterprises`")). WillReturnError(fmt.Errorf("creating enterprise mock error")) @@ -346,11 +350,17 @@ func (s *EnterpriseTestSuite) TestUpdateEnterpriseDBEncryptErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT ?")). WithArgs(s.Fixtures.Enterprises[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Enterprises[0].ID, s.Fixtures.Enterprises[0].Endpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock.ExpectRollback() _, err := s.StoreSQLMocked.UpdateEnterprise(s.adminCtx, s.Fixtures.Enterprises[0].ID, s.Fixtures.UpdateRepoParams) @@ -365,11 +375,17 @@ func (s *EnterpriseTestSuite) TestUpdateEnterpriseDBSaveErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT ?")). WithArgs(s.Fixtures.Enterprises[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Enterprises[0].ID, s.Fixtures.Enterprises[0].Endpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock. ExpectExec(("UPDATE `enterprises` SET")). WillReturnError(fmt.Errorf("saving enterprise mock error")) @@ -390,11 +406,17 @@ func (s *EnterpriseTestSuite) TestUpdateEnterpriseDBDecryptingErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `enterprises` WHERE id = ? AND `enterprises`.`deleted_at` IS NULL ORDER BY `enterprises`.`id` LIMIT ?")). WithArgs(s.Fixtures.Enterprises[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Enterprises[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Enterprises[0].ID, s.Fixtures.Enterprises[0].Endpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock.ExpectRollback() _, err := s.StoreSQLMocked.UpdateEnterprise(s.adminCtx, s.Fixtures.Enterprises[0].ID, s.Fixtures.UpdateRepoParams) diff --git a/database/sql/organizations.go b/database/sql/organizations.go index 419f81b5..2e76cb18 100644 --- a/database/sql/organizations.go +++ b/database/sql/organizations.go @@ -47,7 +47,12 @@ func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsN if err != nil { return errors.Wrap(err, "creating org") } + if creds.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") + } newOrg.CredentialsID = &creds.ID + newOrg.CredentialsName = creds.Name + newOrg.EndpointName = creds.EndpointName q := tx.Create(&newOrg) if q.Error != nil { @@ -55,6 +60,7 @@ func (s *sqlDatabase) CreateOrganization(ctx context.Context, name, credentialsN } newOrg.Credentials = creds + newOrg.Endpoint = creds.Endpoint return nil }) @@ -123,10 +129,13 @@ func (s *sqlDatabase) UpdateOrganization(ctx context.Context, orgID string, para var creds GithubCredentials err := s.conn.Transaction(func(tx *gorm.DB) error { var err error - org, err = s.getOrgByID(ctx, tx, orgID, "Credentials", "Endpoint") + org, err = s.getOrgByID(ctx, tx, orgID) if err != nil { return errors.Wrap(err, "fetching org") } + if org.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "org has no endpoint") + } if param.CredentialsName != "" { org.CredentialsName = param.CredentialsName @@ -134,6 +143,13 @@ func (s *sqlDatabase) UpdateOrganization(ctx context.Context, orgID string, para if err != nil { return errors.Wrap(err, "fetching credentials") } + if creds.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") + } + + if *creds.EndpointName != *org.EndpointName { + return errors.Wrap(runnerErrors.ErrBadRequest, "endpoint mismatch") + } org.CredentialsID = &creds.ID } @@ -164,6 +180,10 @@ func (s *sqlDatabase) UpdateOrganization(ctx context.Context, orgID string, para return params.Organization{}, errors.Wrap(err, "saving org") } + org, err = s.getOrgByID(ctx, s.conn, orgID, "Endpoint", "Credentials") + if err != nil { + return params.Organization{}, errors.Wrap(err, "updating enterprise") + } newParams, err := s.sqlToCommonOrganization(org, true) if err != nil { return params.Organization{}, errors.Wrap(err, "saving org") diff --git a/database/sql/organizations_test.go b/database/sql/organizations_test.go index 55e791db..c0524ff7 100644 --- a/database/sql/organizations_test.go +++ b/database/sql/organizations_test.go @@ -219,7 +219,12 @@ func (s *OrgTestSuite) TestCreateOrganizationDBCreateErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). WithArgs(s.Fixtures.Orgs[0].CredentialsName). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.testCreds.ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.testCreds.ID, s.githubEndpoint.Name)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.githubEndpoint.Name)) s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `organizations`")). WillReturnError(fmt.Errorf("creating org mock error")) @@ -347,11 +352,17 @@ func (s *OrgTestSuite) TestUpdateOrganizationDBEncryptErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT ?")). WithArgs(s.Fixtures.Orgs[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Orgs[0].ID, s.Fixtures.Orgs[0].Endpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock.ExpectRollback() _, err := s.StoreSQLMocked.UpdateOrganization(s.adminCtx, s.Fixtures.Orgs[0].ID, s.Fixtures.UpdateRepoParams) @@ -366,11 +377,17 @@ func (s *OrgTestSuite) TestUpdateOrganizationDBSaveErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT ?")). WithArgs(s.Fixtures.Orgs[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Orgs[0].ID, s.Fixtures.Orgs[0].Endpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock. ExpectExec(("UPDATE `organizations` SET")). WillReturnError(fmt.Errorf("saving org mock error")) @@ -391,11 +408,17 @@ func (s *OrgTestSuite) TestUpdateOrganizationDBDecryptingErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `organizations` WHERE id = ? AND `organizations`.`deleted_at` IS NULL ORDER BY `organizations`.`id` LIMIT ?")). WithArgs(s.Fixtures.Orgs[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Orgs[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Orgs[0].ID, s.Fixtures.Orgs[0].Endpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock.ExpectRollback() _, err := s.StoreSQLMocked.UpdateOrganization(s.adminCtx, s.Fixtures.Orgs[0].ID, s.Fixtures.UpdateRepoParams) diff --git a/database/sql/repositories.go b/database/sql/repositories.go index 26e0d0bc..a938dd80 100644 --- a/database/sql/repositories.go +++ b/database/sql/repositories.go @@ -47,7 +47,12 @@ func (s *sqlDatabase) CreateRepository(ctx context.Context, owner, name, credent if err != nil { return errors.Wrap(err, "creating repository") } + if creds.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") + } newRepo.CredentialsID = &creds.ID + newRepo.CredentialsName = creds.Name + newRepo.EndpointName = creds.EndpointName q := tx.Create(&newRepo) if q.Error != nil { @@ -55,6 +60,8 @@ func (s *sqlDatabase) CreateRepository(ctx context.Context, owner, name, credent } newRepo.Credentials = creds + newRepo.Endpoint = creds.Endpoint + return nil }) if err != nil { @@ -121,10 +128,13 @@ func (s *sqlDatabase) UpdateRepository(ctx context.Context, repoID string, param var creds GithubCredentials err := s.conn.Transaction(func(tx *gorm.DB) error { var err error - repo, err = s.getRepoByID(ctx, tx, repoID, "Credentials", "Endpoint") + repo, err = s.getRepoByID(ctx, tx, repoID) if err != nil { return errors.Wrap(err, "fetching repo") } + if repo.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "repository has no endpoint") + } if param.CredentialsName != "" { repo.CredentialsName = param.CredentialsName @@ -132,6 +142,13 @@ func (s *sqlDatabase) UpdateRepository(ctx context.Context, repoID string, param if err != nil { return errors.Wrap(err, "fetching credentials") } + if creds.EndpointName == nil { + return errors.Wrap(runnerErrors.ErrUnprocessable, "credentials have no endpoint") + } + + if *creds.EndpointName != *repo.EndpointName { + return errors.Wrap(runnerErrors.ErrBadRequest, "endpoint mismatch") + } repo.CredentialsID = &creds.ID } @@ -161,6 +178,10 @@ func (s *sqlDatabase) UpdateRepository(ctx context.Context, repoID string, param return params.Repository{}, errors.Wrap(err, "saving repo") } + repo, err = s.getRepoByID(ctx, s.conn, repoID, "Endpoint", "Credentials") + if err != nil { + return params.Repository{}, errors.Wrap(err, "updating enterprise") + } newParams, err := s.sqlToCommonRepository(repo, true) if err != nil { return params.Repository{}, errors.Wrap(err, "saving repo") diff --git a/database/sql/repositories_test.go b/database/sql/repositories_test.go index 09f65a7a..7fb272e9 100644 --- a/database/sql/repositories_test.go +++ b/database/sql/repositories_test.go @@ -235,7 +235,12 @@ func (s *RepoTestSuite) TestCreateRepositoryInvalidDBCreateErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). WithArgs(s.Fixtures.Repos[0].CredentialsName). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.testCreds.ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.testCreds.ID, s.githubEndpoint.Name)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.githubEndpoint.Name)) s.Fixtures.SQLMock. ExpectExec(regexp.QuoteMeta("INSERT INTO `repositories`")). WillReturnError(fmt.Errorf("creating repo mock error")) @@ -385,11 +390,17 @@ func (s *RepoTestSuite) TestUpdateRepositoryDBEncryptErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT ?")). WithArgs(s.Fixtures.Repos[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Repos[0].ID, s.githubEndpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock.ExpectRollback() _, err := s.StoreSQLMocked.UpdateRepository(s.adminCtx, s.Fixtures.Repos[0].ID, s.Fixtures.UpdateRepoParams) @@ -404,11 +415,17 @@ func (s *RepoTestSuite) TestUpdateRepositoryDBSaveErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT ?")). WithArgs(s.Fixtures.Repos[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Repos[0].ID, s.githubEndpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock. ExpectExec(("UPDATE `repositories` SET")). WillReturnError(fmt.Errorf("saving repo mock error")) @@ -428,11 +445,17 @@ func (s *RepoTestSuite) TestUpdateRepositoryDBDecryptingErr() { s.Fixtures.SQLMock. ExpectQuery(regexp.QuoteMeta("SELECT * FROM `repositories` WHERE id = ? AND `repositories`.`deleted_at` IS NULL ORDER BY `repositories`.`id` LIMIT ?")). WithArgs(s.Fixtures.Repos[0].ID, 1). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.Fixtures.Repos[0].ID)) + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.Fixtures.Repos[0].ID, s.githubEndpoint.Name)) s.Fixtures.SQLMock. - ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT 1")). - WithArgs(s.secondaryTestCreds.Name). - WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(s.secondaryTestCreds.ID)) + ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_credentials` WHERE name = ? AND `github_credentials`.`deleted_at` IS NULL ORDER BY `github_credentials`.`id` LIMIT ?")). + WithArgs(s.secondaryTestCreds.Name, 1). + WillReturnRows(sqlmock.NewRows([]string{"id", "endpoint_name"}). + AddRow(s.secondaryTestCreds.ID, s.secondaryTestCreds.Endpoint)) + s.Fixtures.SQLMock.ExpectQuery(regexp.QuoteMeta("SELECT * FROM `github_endpoints` WHERE `github_endpoints`.`name` = ? AND `github_endpoints`.`deleted_at` IS NULL")). + WithArgs(s.testCreds.Endpoint). + WillReturnRows(sqlmock.NewRows([]string{"name"}). + AddRow(s.secondaryTestCreds.Endpoint)) s.Fixtures.SQLMock.ExpectRollback() _, err := s.StoreSQLMocked.UpdateRepository(s.adminCtx, s.Fixtures.Repos[0].ID, s.Fixtures.UpdateRepoParams) diff --git a/database/sql/util.go b/database/sql/util.go index bc09142d..c291e5d9 100644 --- a/database/sql/util.go +++ b/database/sql/util.go @@ -114,6 +114,10 @@ func (s *sqlDatabase) sqlToCommonOrganization(org Organization, detailed bool) ( return params.Organization{}, errors.Wrap(err, "decrypting secret") } + endpoint, err := s.sqlToCommonGithubEndpoint(org.Endpoint) + if err != nil { + return params.Organization{}, errors.Wrap(err, "converting endpoint") + } ret := params.Organization{ ID: org.ID.String(), Name: org.Name, @@ -121,6 +125,7 @@ func (s *sqlDatabase) sqlToCommonOrganization(org Organization, detailed bool) ( Pools: make([]params.Pool, len(org.Pools)), WebhookSecret: string(secret), PoolBalancerType: org.PoolBalancerType, + Endpoint: endpoint, } if detailed { creds, err := s.sqlToCommonGithubCredentials(org.Credentials) @@ -153,6 +158,10 @@ func (s *sqlDatabase) sqlToCommonEnterprise(enterprise Enterprise, detailed bool return params.Enterprise{}, errors.Wrap(err, "decrypting secret") } + endpoint, err := s.sqlToCommonGithubEndpoint(enterprise.Endpoint) + if err != nil { + return params.Enterprise{}, errors.Wrap(err, "converting endpoint") + } ret := params.Enterprise{ ID: enterprise.ID.String(), Name: enterprise.Name, @@ -160,6 +169,7 @@ func (s *sqlDatabase) sqlToCommonEnterprise(enterprise Enterprise, detailed bool Pools: make([]params.Pool, len(enterprise.Pools)), WebhookSecret: string(secret), PoolBalancerType: enterprise.PoolBalancerType, + Endpoint: endpoint, } if detailed { @@ -253,7 +263,10 @@ func (s *sqlDatabase) sqlToCommonRepository(repo Repository, detailed bool) (par if err != nil { return params.Repository{}, errors.Wrap(err, "decrypting secret") } - + endpoint, err := s.sqlToCommonGithubEndpoint(repo.Endpoint) + if err != nil { + return params.Repository{}, errors.Wrap(err, "converting endpoint") + } ret := params.Repository{ ID: repo.ID.String(), Name: repo.Name, @@ -262,6 +275,7 @@ func (s *sqlDatabase) sqlToCommonRepository(repo Repository, detailed bool) (par Pools: make([]params.Pool, len(repo.Pools)), WebhookSecret: string(secret), PoolBalancerType: repo.PoolBalancerType, + Endpoint: endpoint, } if detailed { diff --git a/params/params.go b/params/params.go index 8409855d..fcaf3113 100644 --- a/params/params.go +++ b/params/params.go @@ -405,6 +405,7 @@ type Repository struct { Credentials GithubCredentials `json:"credentials"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type"` + Endpoint GithubEndpoint `json:"endpoint"` // Do not serialize sensitive info. WebhookSecret string `json:"-"` } @@ -447,6 +448,7 @@ type Organization struct { Credentials GithubCredentials `json:"credentials"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type"` + Endpoint GithubEndpoint `json:"endpoint"` // Do not serialize sensitive info. WebhookSecret string `json:"-"` } @@ -489,6 +491,7 @@ type Enterprise struct { Credentials GithubCredentials `json:"credentials"` PoolManagerStatus PoolManagerStatus `json:"pool_manager_status,omitempty"` PoolBalancerType PoolBalancerType `json:"pool_balancing_type"` + Endpoint GithubEndpoint `json:"endpoint"` // Do not serialize sensitive info. WebhookSecret string `json:"-"` } diff --git a/util/util.go b/util/util.go index 56f2150a..eb390743 100644 --- a/util/util.go +++ b/util/util.go @@ -59,7 +59,7 @@ func (g *githubClient) ListEntityHooks(ctx context.Context, opts *github.ListOpt case params.GithubEntityTypeOrganization: ret, response, err = g.org.ListHooks(ctx, g.entity.Owner, opts) default: - return nil, nil, errors.New("invalid entity type") + return nil, nil, fmt.Errorf("invalid entity type: %s", g.entity.EntityType) } return ret, response, err }