The k8s operator seems to want to define its own endpoint. This change allows the removal of the default gh endpoint if no credentials are tied to it. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
973 lines
32 KiB
Go
973 lines
32 KiB
Go
// Copyright 2024 Cloudbase Solutions SRL
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package sql
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
|
"github.com/cloudbase/garm/auth"
|
|
"github.com/cloudbase/garm/config"
|
|
"github.com/cloudbase/garm/database/common"
|
|
garmTesting "github.com/cloudbase/garm/internal/testing"
|
|
"github.com/cloudbase/garm/params"
|
|
)
|
|
|
|
const (
|
|
testUploadBaseURL string = "https://uploads.example.com"
|
|
testBaseURL string = "https://example.com"
|
|
testAPIBaseURL string = "https://api.example.com"
|
|
testEndpointName string = "test-endpoint"
|
|
testEndpointDescription string = "test description"
|
|
testCredsName string = "test-creds"
|
|
testCredsDescription string = "test creds"
|
|
defaultGithubEndpoint string = "github.com"
|
|
)
|
|
|
|
type GithubTestSuite struct {
|
|
suite.Suite
|
|
|
|
db common.Store
|
|
}
|
|
|
|
func (s *GithubTestSuite) SetupTest() {
|
|
db, err := NewSQLDatabase(context.Background(), garmTesting.GetTestSqliteDBConfig(s.T()))
|
|
if err != nil {
|
|
s.FailNow(fmt.Sprintf("failed to create db connection: %s", err))
|
|
}
|
|
s.db = db
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDefaultEndpointGetsCreatedAutomaticallyIfNoOtherEndpointExists() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
endpoint, err := s.db.GetGithubEndpoint(ctx, defaultGithubEndpoint)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreatingEndpoint() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
endpoint, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
s.Require().Equal(testEndpointName, endpoint.Name)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreatingDuplicateEndpointFails() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
_, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrDuplicateEntity)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestGetEndpoint() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
endpoint, err := s.db.GetGithubEndpoint(ctx, defaultGithubEndpoint)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
s.Require().Equal(defaultGithubEndpoint, endpoint.Name)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestGetNonExistingEndpointFailsWithNotFoundError() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
_, err := s.db.GetGithubEndpoint(ctx, "non-existing")
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDeletingNonExistingEndpointIsANoop() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
err := s.db.DeleteGithubEndpoint(ctx, "non-existing")
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDeletingEndpoint() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
endpoint, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
|
|
err = s.db.DeleteGithubEndpoint(ctx, testEndpointName)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.db.GetGithubEndpoint(ctx, testEndpointName)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDeleteGithubEndpointFailsWhenCredentialsExist() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
endpoint, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: testEndpointName,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
_, err = s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
|
|
err = s.db.DeleteGithubEndpoint(ctx, testEndpointName)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdateEndpoint() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
endpoint, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
|
|
newDescription := "the new description"
|
|
newAPIBaseURL := "https://new-api.example.com"
|
|
newUploadBaseURL := "https://new-uploads.example.com"
|
|
newBaseURL := "https://new.example.com"
|
|
caCertBundle, err := os.ReadFile("../../testdata/certs/srv-pub.pem")
|
|
s.Require().NoError(err)
|
|
updateEpParams := params.UpdateGithubEndpointParams{
|
|
Description: &newDescription,
|
|
APIBaseURL: &newAPIBaseURL,
|
|
UploadBaseURL: &newUploadBaseURL,
|
|
BaseURL: &newBaseURL,
|
|
CACertBundle: caCertBundle,
|
|
}
|
|
|
|
updatedEndpoint, err := s.db.UpdateGithubEndpoint(ctx, testEndpointName, updateEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(updatedEndpoint)
|
|
s.Require().Equal(newDescription, updatedEndpoint.Description)
|
|
s.Require().Equal(newAPIBaseURL, updatedEndpoint.APIBaseURL)
|
|
s.Require().Equal(newUploadBaseURL, updatedEndpoint.UploadBaseURL)
|
|
s.Require().Equal(newBaseURL, updatedEndpoint.BaseURL)
|
|
s.Require().Equal(caCertBundle, updatedEndpoint.CACertBundle)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdateEndpointUDLsFailsIfCredentialsAreAssociated() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
endpoint, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(endpoint)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: testEndpointName,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
_, err = s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
|
|
newDescription := "new description"
|
|
newBaseURL := "https://new.example.com"
|
|
newAPIBaseURL := "https://new-api.example.com"
|
|
newUploadBaseURL := "https://new-uploads.example.com"
|
|
updateEpParams := params.UpdateGithubEndpointParams{
|
|
BaseURL: &newBaseURL,
|
|
}
|
|
|
|
_, err = s.db.UpdateGithubEndpoint(ctx, testEndpointName, updateEpParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().EqualError(err, "updating github endpoint: cannot update endpoint URLs with existing credentials: invalid request")
|
|
|
|
updateEpParams = params.UpdateGithubEndpointParams{
|
|
UploadBaseURL: &newUploadBaseURL,
|
|
}
|
|
|
|
_, err = s.db.UpdateGithubEndpoint(ctx, testEndpointName, updateEpParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().EqualError(err, "updating github endpoint: cannot update endpoint URLs with existing credentials: invalid request")
|
|
|
|
updateEpParams = params.UpdateGithubEndpointParams{
|
|
APIBaseURL: &newAPIBaseURL,
|
|
}
|
|
_, err = s.db.UpdateGithubEndpoint(ctx, testEndpointName, updateEpParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().EqualError(err, "updating github endpoint: cannot update endpoint URLs with existing credentials: invalid request")
|
|
|
|
updateEpParams = params.UpdateGithubEndpointParams{
|
|
Description: &newDescription,
|
|
}
|
|
ret, err := s.db.UpdateGithubEndpoint(ctx, testEndpointName, updateEpParams)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(newDescription, ret.Description)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdatingNonExistingEndpointReturnsNotFoundError() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
newDescription := "test"
|
|
updateEpParams := params.UpdateGithubEndpointParams{
|
|
Description: &newDescription,
|
|
}
|
|
|
|
_, err := s.db.UpdateGithubEndpoint(ctx, "non-existing", updateEpParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestListEndpoints() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
createEpParams := params.CreateGithubEndpointParams{
|
|
Name: testEndpointName,
|
|
Description: testEndpointDescription,
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
}
|
|
|
|
_, err := s.db.CreateGithubEndpoint(ctx, createEpParams)
|
|
s.Require().NoError(err)
|
|
|
|
endpoints, err := s.db.ListGithubEndpoints(ctx)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(endpoints, 2)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreateCredentialsFailsWithUnauthorizedForAnonUser() {
|
|
ctx := context.Background()
|
|
|
|
_, err := s.db.CreateGithubCredentials(ctx, params.CreateGithubCredentialsParams{})
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrUnauthorized)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreateCredentialsFailsWhenEndpointNameIsEmpty() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
_, err := s.db.CreateGithubCredentials(ctx, params.CreateGithubCredentialsParams{})
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().Regexp("endpoint name is required", err.Error())
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreateCredentialsFailsWhenEndpointDoesNotExist() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
_, err := s.db.CreateGithubCredentials(ctx, params.CreateGithubCredentialsParams{Endpoint: "non-existing"})
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
s.Require().Regexp("endpoint not found", err.Error())
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreateCredentialsFailsWhenAuthTypeIsInvalid() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
_, err := s.db.CreateGithubCredentials(ctx, params.CreateGithubCredentialsParams{Endpoint: defaultGithubEndpoint, AuthType: "invalid"})
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().Regexp("invalid auth type", err.Error())
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreateCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
s.Require().Equal(credParams.Name, creds.Name)
|
|
s.Require().Equal(credParams.Description, creds.Description)
|
|
s.Require().Equal(credParams.Endpoint, creds.Endpoint.Name)
|
|
s.Require().Equal(credParams.AuthType, creds.AuthType)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestCreateCredentialsFailsOnDuplicateCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
testUser := garmTesting.CreateGARMTestUser(ctx, "testuser", s.db, s.T())
|
|
testUserCtx := auth.PopulateContext(context.Background(), testUser, nil)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
_, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
|
|
// Creating creds with the same parameters should fail for the same user.
|
|
_, err = s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrDuplicateEntity)
|
|
|
|
// Creating creds with the same parameters should work for different users.
|
|
_, err = s.db.CreateGithubCredentials(testUserCtx, credParams)
|
|
s.Require().NoError(err)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestNormalUsersCanOnlySeeTheirOwnCredentialsAdminCanSeeAll() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
testUser := garmTesting.CreateGARMTestUser(ctx, "testuser1", s.db, s.T())
|
|
testUser2 := garmTesting.CreateGARMTestUser(ctx, "testuser2", s.db, s.T())
|
|
testUserCtx := auth.PopulateContext(context.Background(), testUser, nil)
|
|
testUser2Ctx := auth.PopulateContext(context.Background(), testUser2, nil)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
credParams.Name = "test-creds2"
|
|
creds2, err := s.db.CreateGithubCredentials(testUserCtx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
|
|
credParams.Name = "test-creds3"
|
|
creds3, err := s.db.CreateGithubCredentials(testUser2Ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds3)
|
|
|
|
credsList, err := s.db.ListGithubCredentials(ctx)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(credsList, 3)
|
|
|
|
credsList, err = s.db.ListGithubCredentials(testUserCtx)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(credsList, 1)
|
|
s.Require().Equal("test-creds2", credsList[0].Name)
|
|
|
|
credsList, err = s.db.ListGithubCredentials(testUser2Ctx)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(credsList, 1)
|
|
s.Require().Equal("test-creds3", credsList[0].Name)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestGetGithubCredentialsFailsWhenCredentialsDontExist() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
_, err := s.db.GetGithubCredentials(ctx, 1, true)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
|
|
_, err = s.db.GetGithubCredentialsByName(ctx, "non-existing", true)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestGetGithubCredentialsByNameReturnsOnlyCurrentUserCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
testUser := garmTesting.CreateGARMTestUser(ctx, "test-user1", s.db, s.T())
|
|
testUserCtx := auth.PopulateContext(context.Background(), testUser, nil)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
creds2, err := s.db.CreateGithubCredentials(testUserCtx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
|
|
creds2Get, err := s.db.GetGithubCredentialsByName(testUserCtx, testCredsName, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
s.Require().Equal(testCredsName, creds2Get.Name)
|
|
s.Require().Equal(creds2.ID, creds2Get.ID)
|
|
|
|
credsGet, err := s.db.GetGithubCredentialsByName(ctx, testCredsName, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
s.Require().Equal(testCredsName, credsGet.Name)
|
|
s.Require().Equal(creds.ID, credsGet.ID)
|
|
|
|
// Admin can get any creds by ID
|
|
credsGet, err = s.db.GetGithubCredentials(ctx, creds2.ID, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
s.Require().Equal(creds2.ID, credsGet.ID)
|
|
|
|
// Normal user cannot get other user creds by ID
|
|
_, err = s.db.GetGithubCredentials(testUserCtx, creds.ID, true)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestGetGithubCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
creds2, err := s.db.GetGithubCredentialsByName(ctx, testCredsName, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
s.Require().Equal(creds.Name, creds2.Name)
|
|
s.Require().Equal(creds.ID, creds2.ID)
|
|
|
|
creds2, err = s.db.GetGithubCredentials(ctx, creds.ID, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
s.Require().Equal(creds.Name, creds2.Name)
|
|
s.Require().Equal(creds.ID, creds2.ID)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDeleteGithubCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
err = s.db.DeleteGithubCredentials(ctx, creds.ID)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.db.GetGithubCredentials(ctx, creds.ID, true)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDeleteGithubCredentialsByNonAdminUser() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
testUser := garmTesting.CreateGARMTestUser(ctx, "test-user4", s.db, s.T())
|
|
testUserCtx := auth.PopulateContext(context.Background(), testUser, nil)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test-creds4",
|
|
},
|
|
}
|
|
|
|
// Create creds as admin
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
// Deleting non existent creds will return a nil error. For the test user
|
|
// the creds created by the admin should not be visible, which leads to not found
|
|
// which in turn returns no error.
|
|
err = s.db.DeleteGithubCredentials(testUserCtx, creds.ID)
|
|
s.Require().NoError(err)
|
|
|
|
// Check that the creds created by the admin are still there.
|
|
credsGet, err := s.db.GetGithubCredentials(ctx, creds.ID, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(credsGet)
|
|
s.Require().Equal(creds.ID, credsGet.ID)
|
|
|
|
// Create the same creds with the test user.
|
|
creds2, err := s.db.CreateGithubCredentials(testUserCtx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds2)
|
|
|
|
// Remove creds created by test user.
|
|
err = s.db.DeleteGithubCredentials(testUserCtx, creds2.ID)
|
|
s.Require().NoError(err)
|
|
|
|
// The creds created by the test user should be gone.
|
|
_, err = s.db.GetGithubCredentials(testUserCtx, creds2.ID, true)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestDeleteCredentialsFailsIfReposOrgsOrEntitiesUseIt() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
repo, err := s.db.CreateRepository(ctx, "test-owner", "test-repo", creds.Name, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(repo)
|
|
|
|
err = s.db.DeleteGithubCredentials(ctx, creds.ID)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
|
|
err = s.db.DeleteRepository(ctx, repo.ID)
|
|
s.Require().NoError(err)
|
|
|
|
org, err := s.db.CreateOrganization(ctx, "test-org", creds.Name, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(org)
|
|
|
|
err = s.db.DeleteGithubCredentials(ctx, creds.ID)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
|
|
err = s.db.DeleteOrganization(ctx, org.ID)
|
|
s.Require().NoError(err)
|
|
|
|
enterprise, err := s.db.CreateEnterprise(ctx, "test-enterprise", creds.Name, "superSecret@123BlaBla", params.PoolBalancerTypeRoundRobin)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(enterprise)
|
|
|
|
err = s.db.DeleteGithubCredentials(ctx, creds.ID)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
|
|
err = s.db.DeleteEnterprise(ctx, enterprise.ID)
|
|
s.Require().NoError(err)
|
|
|
|
err = s.db.DeleteGithubCredentials(ctx, creds.ID)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.db.GetGithubCredentials(ctx, creds.ID, true)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdateCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
newDescription := "new description"
|
|
newName := "new-name"
|
|
newToken := "new-token"
|
|
updateCredParams := params.UpdateGithubCredentialsParams{
|
|
Description: &newDescription,
|
|
Name: &newName,
|
|
PAT: ¶ms.GithubPAT{
|
|
OAuth2Token: newToken,
|
|
},
|
|
}
|
|
|
|
updatedCreds, err := s.db.UpdateGithubCredentials(ctx, creds.ID, updateCredParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(updatedCreds)
|
|
s.Require().Equal(newDescription, updatedCreds.Description)
|
|
s.Require().Equal(newName, updatedCreds.Name)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdateGithubCredentialsFailIfWrongCredentialTypeIsPassed() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
updateCredParams := params.UpdateGithubCredentialsParams{
|
|
App: ¶ms.GithubApp{
|
|
AppID: 1,
|
|
InstallationID: 2,
|
|
PrivateKeyBytes: []byte("test"),
|
|
},
|
|
}
|
|
|
|
_, err = s.db.UpdateGithubCredentials(ctx, creds.ID, updateCredParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().EqualError(err, "updating github credentials: cannot update app credentials for PAT: invalid request")
|
|
|
|
credParamsWithApp := params.CreateGithubCredentialsParams{
|
|
Name: "test-credsApp",
|
|
Description: "test credsApp",
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypeApp,
|
|
App: params.GithubApp{
|
|
AppID: 1,
|
|
InstallationID: 2,
|
|
PrivateKeyBytes: []byte("test"),
|
|
},
|
|
}
|
|
|
|
credsApp, err := s.db.CreateGithubCredentials(ctx, credParamsWithApp)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(credsApp)
|
|
|
|
updateCredParams = params.UpdateGithubCredentialsParams{
|
|
PAT: ¶ms.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
}
|
|
|
|
_, err = s.db.UpdateGithubCredentials(ctx, credsApp.ID, updateCredParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrBadRequest)
|
|
s.Require().EqualError(err, "updating github credentials: cannot update PAT credentials for app: invalid request")
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdateCredentialsFailsForNonExistingCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
|
|
updateCredParams := params.UpdateGithubCredentialsParams{
|
|
Description: nil,
|
|
}
|
|
|
|
_, err := s.db.UpdateGithubCredentials(ctx, 1, updateCredParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestUpdateCredentialsFailsIfCredentialsAreOwnedByNonAdminUser() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
testUser := garmTesting.CreateGARMTestUser(ctx, "test-user5", s.db, s.T())
|
|
testUserCtx := auth.PopulateContext(context.Background(), testUser, nil)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test-creds5",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(ctx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
newDescription := "new description2"
|
|
updateCredParams := params.UpdateGithubCredentialsParams{
|
|
Description: &newDescription,
|
|
}
|
|
|
|
_, err = s.db.UpdateGithubCredentials(testUserCtx, creds.ID, updateCredParams)
|
|
s.Require().Error(err)
|
|
s.Require().ErrorIs(err, runnerErrors.ErrNotFound)
|
|
}
|
|
|
|
func (s *GithubTestSuite) TestAdminUserCanUpdateAnyGithubCredentials() {
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), s.db, s.T())
|
|
testUser := garmTesting.CreateGARMTestUser(ctx, "test-user5", s.db, s.T())
|
|
testUserCtx := auth.PopulateContext(context.Background(), testUser, nil)
|
|
|
|
credParams := params.CreateGithubCredentialsParams{
|
|
Name: testCredsName,
|
|
Description: testCredsDescription,
|
|
Endpoint: defaultGithubEndpoint,
|
|
AuthType: params.GithubAuthTypePAT,
|
|
PAT: params.GithubPAT{
|
|
OAuth2Token: "test-creds5",
|
|
},
|
|
}
|
|
|
|
creds, err := s.db.CreateGithubCredentials(testUserCtx, credParams)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(creds)
|
|
|
|
newDescription := "new description2"
|
|
updateCredParams := params.UpdateGithubCredentialsParams{
|
|
Description: &newDescription,
|
|
}
|
|
|
|
newCreds, err := s.db.UpdateGithubCredentials(ctx, creds.ID, updateCredParams)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(newDescription, newCreds.Description)
|
|
}
|
|
|
|
func TestGithubTestSuite(t *testing.T) {
|
|
t.Parallel()
|
|
suite.Run(t, new(GithubTestSuite))
|
|
}
|
|
|
|
func TestCredentialsAndEndpointMigration(t *testing.T) {
|
|
cfg := garmTesting.GetTestSqliteDBConfig(t)
|
|
|
|
// Copy the sample DB
|
|
data, err := os.ReadFile("../../testdata/db/v0.1.4/garm.db")
|
|
if err != nil {
|
|
t.Fatalf("failed to read test data: %s", err)
|
|
}
|
|
|
|
if cfg.SQLite.DBFile == "" {
|
|
t.Fatalf("DB file not set")
|
|
}
|
|
if err := os.WriteFile(cfg.SQLite.DBFile, data, 0o600); err != nil {
|
|
t.Fatalf("failed to write test data: %s", err)
|
|
}
|
|
|
|
// define some credentials
|
|
credentials := []config.Github{
|
|
{
|
|
Name: "test-creds",
|
|
Description: "test creds",
|
|
AuthType: config.GithubAuthTypePAT,
|
|
PAT: config.GithubPAT{
|
|
OAuth2Token: "test",
|
|
},
|
|
},
|
|
{
|
|
Name: "ghes-test",
|
|
Description: "ghes creds",
|
|
APIBaseURL: testAPIBaseURL,
|
|
UploadBaseURL: testUploadBaseURL,
|
|
BaseURL: testBaseURL,
|
|
AuthType: config.GithubAuthTypeApp,
|
|
App: config.GithubApp{
|
|
AppID: 1,
|
|
InstallationID: 99,
|
|
PrivateKeyPath: "../../testdata/certs/srv-key.pem",
|
|
},
|
|
},
|
|
}
|
|
// Set the config credentials in the cfg. This is what happens in the main function.
|
|
// of GARM as well.
|
|
cfg.MigrateCredentials = credentials
|
|
|
|
db, err := NewSQLDatabase(context.Background(), cfg)
|
|
if err != nil {
|
|
t.Fatalf("failed to create db connection: %s", err)
|
|
}
|
|
|
|
// We expect that 2 endpoints will exist in the migrated DB and 2 credentials.
|
|
ctx := garmTesting.ImpersonateAdminContext(context.Background(), db, t)
|
|
|
|
endpoints, err := db.ListGithubEndpoints(ctx)
|
|
if err != nil {
|
|
t.Fatalf("failed to list endpoints: %s", err)
|
|
}
|
|
if len(endpoints) != 2 {
|
|
t.Fatalf("expected 2 endpoints, got %d", len(endpoints))
|
|
}
|
|
if endpoints[0].Name != defaultGithubEndpoint {
|
|
t.Fatalf("expected default endpoint to exist, got %s", endpoints[0].Name)
|
|
}
|
|
if endpoints[1].Name != "example.com" {
|
|
t.Fatalf("expected example.com endpoint to exist, got %s", endpoints[1].Name)
|
|
}
|
|
if endpoints[1].UploadBaseURL != testUploadBaseURL {
|
|
t.Fatalf("expected upload base URL to be %s, got %s", testUploadBaseURL, endpoints[1].UploadBaseURL)
|
|
}
|
|
if endpoints[1].BaseURL != testBaseURL {
|
|
t.Fatalf("expected base URL to be %s, got %s", testBaseURL, endpoints[1].BaseURL)
|
|
}
|
|
if endpoints[1].APIBaseURL != testAPIBaseURL {
|
|
t.Fatalf("expected API base URL to be %s, got %s", testAPIBaseURL, endpoints[1].APIBaseURL)
|
|
}
|
|
|
|
creds, err := db.ListGithubCredentials(ctx)
|
|
if err != nil {
|
|
t.Fatalf("failed to list credentials: %s", err)
|
|
}
|
|
if len(creds) != 2 {
|
|
t.Fatalf("expected 2 credentials, got %d", len(creds))
|
|
}
|
|
if creds[0].Name != "test-creds" {
|
|
t.Fatalf("expected test-creds to exist, got %s", creds[0].Name)
|
|
}
|
|
if creds[1].Name != "ghes-test" {
|
|
t.Fatalf("expected ghes-test to exist, got %s", creds[1].Name)
|
|
}
|
|
if creds[0].Endpoint.Name != defaultGithubEndpoint {
|
|
t.Fatalf("expected test-creds to be associated with default endpoint, got %s", creds[0].Endpoint.Name)
|
|
}
|
|
if creds[1].Endpoint.Name != "example.com" {
|
|
t.Fatalf("expected ghes-test to be associated with example.com endpoint, got %s", creds[1].Endpoint.Name)
|
|
}
|
|
|
|
if creds[0].AuthType != params.GithubAuthTypePAT {
|
|
t.Fatalf("expected test-creds to have PAT auth type, got %s", creds[0].AuthType)
|
|
}
|
|
if creds[1].AuthType != params.GithubAuthTypeApp {
|
|
t.Fatalf("expected ghes-test to have App auth type, got %s", creds[1].AuthType)
|
|
}
|
|
if len(creds[0].CredentialsPayload) == 0 {
|
|
t.Fatalf("expected test-creds to have credentials payload, got empty")
|
|
}
|
|
|
|
var pat params.GithubPAT
|
|
if err := json.Unmarshal(creds[0].CredentialsPayload, &pat); err != nil {
|
|
t.Fatalf("failed to unmarshal test-creds credentials payload: %s", err)
|
|
}
|
|
if pat.OAuth2Token != "test" {
|
|
t.Fatalf("expected test-creds to have PAT token test, got %s", pat.OAuth2Token)
|
|
}
|
|
|
|
var app params.GithubApp
|
|
if err := json.Unmarshal(creds[1].CredentialsPayload, &app); err != nil {
|
|
t.Fatalf("failed to unmarshal ghes-test credentials payload: %s", err)
|
|
}
|
|
if app.AppID != 1 {
|
|
t.Fatalf("expected ghes-test to have app ID 1, got %d", app.AppID)
|
|
}
|
|
if app.InstallationID != 99 {
|
|
t.Fatalf("expected ghes-test to have installation ID 99, got %d", app.InstallationID)
|
|
}
|
|
if app.PrivateKeyBytes == nil {
|
|
t.Fatalf("expected ghes-test to have private key bytes, got nil")
|
|
}
|
|
|
|
certBundle, err := credentials[1].App.PrivateKeyBytes()
|
|
if err != nil {
|
|
t.Fatalf("failed to read CA cert bundle: %s", err)
|
|
}
|
|
|
|
if !bytes.Equal(app.PrivateKeyBytes, certBundle) {
|
|
t.Fatalf("expected ghes-test private key to be equal to the CA cert bundle")
|
|
}
|
|
}
|