diff --git a/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go index 8ca319f16..0aa97e582 100644 --- a/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go +++ b/app/gitspace/orchestrator/container/embedded_docker_container_orchestrator.go @@ -240,7 +240,7 @@ func (e *EmbeddedDockerOrchestrator) startStoppedGitspace( } // Set up git credentials if needed - if resolvedRepoDetails.Credentials != nil { + if resolvedRepoDetails.UserPasswordCredentials != nil { if err = utils.SetupGitCredentials(ctx, exec, resolvedRepoDetails, logStreamInstance); err != nil { return err } @@ -674,7 +674,7 @@ func (e *EmbeddedDockerOrchestrator) buildSetupSteps( exec *devcontainer.Exec, gitspaceLogger gitspaceTypes.GitspaceLogger, ) error { - if resolvedRepoDetails.ResolvedCredentials.Credentials != nil { + if resolvedRepoDetails.ResolvedCredentials.UserPasswordCredentials != nil { return utils.SetupGitCredentials(ctx, exec, resolvedRepoDetails, gitspaceLogger) } return nil diff --git a/app/gitspace/orchestrator/utils/git.go b/app/gitspace/orchestrator/utils/git.go index 9bc0d8b77..5cec92a51 100644 --- a/app/gitspace/orchestrator/utils/git.go +++ b/app/gitspace/orchestrator/utils/git.go @@ -91,9 +91,9 @@ func CloneCode( Branch: resolvedRepoDetails.Branch, RepoName: resolvedRepoDetails.RepoName, } - if resolvedRepoDetails.ResolvedCredentials.Credentials != nil { - data.Email = resolvedRepoDetails.Credentials.Email - data.Name = resolvedRepoDetails.Credentials.Name.Value() + if resolvedRepoDetails.ResolvedCredentials.UserPasswordCredentials != nil { + data.Email = resolvedRepoDetails.UserPasswordCredentials.Email + data.Name = resolvedRepoDetails.UserPasswordCredentials.Name.Value() } script, err := GenerateScriptFromTemplate( templateCloneCode, data) diff --git a/app/gitspace/scm/gitness_scm.go b/app/gitspace/scm/gitness_scm.go index 5bec9b412..c30105ec4 100644 --- a/app/gitspace/scm/gitness_scm.go +++ b/app/gitspace/scm/gitness_scm.go @@ -35,7 +35,8 @@ import ( "github.com/harness/gitness/types/enum" ) -var _ Provider = (*GitnessSCM)(nil) +var _ ListingProvider = (*GitnessSCM)(nil) +var _ AuthAndFileContentProvider = (*GitnessSCM)(nil) var gitspaceJWTLifetime = 720 * 24 * time.Hour @@ -50,7 +51,7 @@ type GitnessSCM struct { urlProvider urlprovider.Provider } -// ListBranches implements Provider. +// ListBranches implements ListingProvider. func (s *GitnessSCM) ListBranches( ctx context.Context, filter *BranchFilter, @@ -66,8 +67,8 @@ func (s *GitnessSCM) ListBranches( Query: filter.Query, Sort: git.BranchSortOptionDate, Order: git.SortOrderDesc, - Page: int32(filter.Page), - PageSize: int32(filter.Size), + Page: filter.Page, + PageSize: filter.Size, }) if err != nil { return nil, err @@ -87,7 +88,7 @@ func mapBranch(b git.Branch) Branch { } } -// ListRepositories implements Provider. +// ListRepositories implements ListingProvider. func (s *GitnessSCM) ListRepositories( ctx context.Context, filter *RepositoryFilter, @@ -202,12 +203,12 @@ func (s *GitnessSCM) ResolveCredentials( userInfo := url.UserPassword("harness", jwtToken) modifiedURL.User = userInfo resolvedCredentails.CloneURL = types.NewMaskSecret(modifiedURL.String()) - credentials := &Credentials{ + credentials := &UserPasswordCredentials{ Email: user.Email, Name: types.NewMaskSecret(user.DisplayName), Password: types.NewMaskSecret(jwtToken), } - resolvedCredentails.Credentials = credentials + resolvedCredentails.UserPasswordCredentials = credentials return resolvedCredentails, nil } diff --git a/app/gitspace/scm/public_scm.go b/app/gitspace/scm/public_scm.go index 8ddac7fd5..2f4de711c 100644 --- a/app/gitspace/scm/public_scm.go +++ b/app/gitspace/scm/public_scm.go @@ -31,7 +31,8 @@ import ( "github.com/rs/zerolog/log" ) -var _ Provider = (*GenericSCM)(nil) +var _ ListingProvider = (*GenericSCM)(nil) +var _ AuthAndFileContentProvider = (*GenericSCM)(nil) type GenericSCM struct { } diff --git a/app/gitspace/scm/scm.go b/app/gitspace/scm/scm.go index 1da08c5e3..6602cf82d 100644 --- a/app/gitspace/scm/scm.go +++ b/app/gitspace/scm/scm.go @@ -62,12 +62,12 @@ func (s *SCM) CheckValidCodeRepo( return codeRepositoryResponse, nil } - scmProvider, err := s.getSCMProvider(codeRepositoryRequest.RepoType) + authAndFileContentProvider, err := s.getSCMAuthAndFileProvider(codeRepositoryRequest.RepoType) if err != nil { return nil, fmt.Errorf("failed to resolve SCM provider: %w", err) } - resolvedCreds, err := s.resolveRepoCredentials(ctx, scmProvider, codeRepositoryRequest) + resolvedCreds, err := s.resolveRepoCredentials(ctx, authAndFileContentProvider, codeRepositoryRequest) if err != nil { return nil, fmt.Errorf("failed to resolve repo credentials and URL: %w", err) } @@ -83,17 +83,21 @@ func (s *SCM) GetSCMRepoDetails( ctx context.Context, gitspaceConfig types.GitspaceConfig, ) (*ResolvedDetails, error) { - scmProvider, err := s.getSCMProvider(gitspaceConfig.CodeRepo.Type) + scmAuthAndFileContentProvider, err := s.getSCMAuthAndFileProvider(gitspaceConfig.CodeRepo.Type) if err != nil { - return nil, fmt.Errorf("failed to resolve SCM provider: %w", err) + return nil, fmt.Errorf("failed to resolve SCM Auth and File COntent provider: %w", err) } - resolvedCredentials, err := scmProvider.ResolveCredentials(ctx, gitspaceConfig) + resolvedCredentials, err := scmAuthAndFileContentProvider.ResolveCredentials(ctx, gitspaceConfig) if err != nil { return nil, fmt.Errorf("failed to resolve repo credentials and url: %w", err) } - devcontainerConfig, err := s.getDevcontainerConfig(ctx, scmProvider, gitspaceConfig, resolvedCredentials) + scmAuthAndFileProvider, err := s.getSCMAuthAndFileProvider(gitspaceConfig.CodeRepo.Type) + if err != nil { + return nil, fmt.Errorf("failed to resolve SCM Auth and File content provider: %w", err) + } + devcontainerConfig, err := s.getDevcontainerConfig(ctx, scmAuthAndFileProvider, gitspaceConfig, resolvedCredentials) if err != nil { return nil, fmt.Errorf("failed to read or parse devcontainer config: %w", err) } @@ -148,16 +152,16 @@ func (s *SCM) detectBranch(ctx context.Context, repoURL string) (string, error) return defaultBranch, nil } -func (s *SCM) getSCMProvider(repoType enum.GitspaceCodeRepoType) (Provider, error) { +func (s *SCM) getSCMAuthAndFileProvider(repoType enum.GitspaceCodeRepoType) (AuthAndFileContentProvider, error) { if repoType == "" { repoType = enum.CodeRepoTypeUnknown } - return s.scmProviderFactory.GetSCMProvider(repoType) + return s.scmProviderFactory.GetSCMAuthAndFileProvider(repoType) } func (s *SCM) resolveRepoCredentials( ctx context.Context, - scmProvider Provider, + authAndFileContentProvider AuthAndFileContentProvider, codeRepositoryRequest CodeRepositoryRequest, ) (*ResolvedCredentials, error) { codeRepo := types.CodeRepo{URL: codeRepositoryRequest.URL} @@ -167,18 +171,19 @@ func (s *SCM) resolveRepoCredentials( SpacePath: codeRepositoryRequest.SpacePath, GitspaceUser: gitspaceUser, } - return scmProvider.ResolveCredentials(ctx, gitspaceConfig) + return authAndFileContentProvider.ResolveCredentials(ctx, gitspaceConfig) } func (s *SCM) getDevcontainerConfig( ctx context.Context, - scmProvider Provider, + scmAuthAndFileContentProvider AuthAndFileContentProvider, gitspaceConfig types.GitspaceConfig, resolvedCredentials *ResolvedCredentials, ) (types.DevcontainerConfig, error) { config := types.DevcontainerConfig{} filePath := devcontainerDefaultPath - catFileOutputBytes, err := scmProvider.GetFileContent(ctx, gitspaceConfig, filePath, resolvedCredentials) + catFileOutputBytes, err := scmAuthAndFileContentProvider.GetFileContent( + ctx, gitspaceConfig, filePath, resolvedCredentials) if err != nil { return config, fmt.Errorf("failed to read devcontainer file: %w", err) } diff --git a/app/gitspace/scm/scm_auth_file_provider.go b/app/gitspace/scm/scm_auth_file_provider.go new file mode 100644 index 000000000..9003af6dd --- /dev/null +++ b/app/gitspace/scm/scm_auth_file_provider.go @@ -0,0 +1,31 @@ +// Copyright 2023 Harness, Inc. +// +// 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 scm + +import ( + "context" + + "github.com/harness/gitness/types" +) + +type AuthAndFileContentProvider interface { + GetFileContent( + ctx context.Context, + gitspaceConfig types.GitspaceConfig, + filePath string, + credentials *ResolvedCredentials, + ) ([]byte, error) + ResolveCredentials(ctx context.Context, gitspaceConfig types.GitspaceConfig) (*ResolvedCredentials, error) +} diff --git a/app/gitspace/scm/scm_factory.go b/app/gitspace/scm/scm_factory.go index eed1a88ce..15a8f4b0d 100644 --- a/app/gitspace/scm/scm_factory.go +++ b/app/gitspace/scm/scm_factory.go @@ -21,22 +21,41 @@ import ( ) type Factory struct { - providers map[enum.GitspaceCodeRepoType]Provider + listingProviders map[enum.GitspaceCodeRepoType]ListingProvider + authAndFileProviders map[enum.GitspaceCodeRepoType]AuthAndFileContentProvider } -func NewFactoryWithProviders(providers map[enum.GitspaceCodeRepoType]Provider) Factory { - return Factory{providers: providers} +func NewFactoryWithProviders( + providers map[enum.GitspaceCodeRepoType]ListingProvider, + authProviders map[enum.GitspaceCodeRepoType]AuthAndFileContentProvider) Factory { + return Factory{listingProviders: providers, + authAndFileProviders: authProviders} } func NewFactory(gitnessProvider *GitnessSCM, genericSCM *GenericSCM) Factory { - providers := make(map[enum.GitspaceCodeRepoType]Provider) - providers[enum.CodeRepoTypeGitness] = gitnessProvider - providers[enum.CodeRepoTypeUnknown] = genericSCM - return Factory{providers: providers} + listingProviders := make(map[enum.GitspaceCodeRepoType]ListingProvider) + listingProviders[enum.CodeRepoTypeGitness] = gitnessProvider + listingProviders[enum.CodeRepoTypeUnknown] = genericSCM + authAndFileContentProviders := make(map[enum.GitspaceCodeRepoType]AuthAndFileContentProvider) + authAndFileContentProviders[enum.CodeRepoTypeGitness] = gitnessProvider + authAndFileContentProviders[enum.CodeRepoTypeUnknown] = genericSCM + return Factory{ + listingProviders: listingProviders, + authAndFileProviders: authAndFileContentProviders, + } } -func (f *Factory) GetSCMProvider(providerType enum.GitspaceCodeRepoType) (Provider, error) { - val := f.providers[providerType] +func (f *Factory) GetSCMProvider(providerType enum.GitspaceCodeRepoType) (ListingProvider, error) { + val := f.listingProviders[providerType] + if val == nil { + return nil, fmt.Errorf("unknown scm provider type: %s", providerType) + } + return val, nil +} + +func (f *Factory) GetSCMAuthAndFileProvider(providerType enum.GitspaceCodeRepoType) ( + AuthAndFileContentProvider, error) { + val := f.authAndFileProviders[providerType] if val == nil { return nil, fmt.Errorf("unknown scm provider type: %s", providerType) } diff --git a/app/gitspace/scm/scm_provider.go b/app/gitspace/scm/scm_listing_provider.go similarity index 74% rename from app/gitspace/scm/scm_provider.go rename to app/gitspace/scm/scm_listing_provider.go index fbca405cb..c287b7f8c 100644 --- a/app/gitspace/scm/scm_provider.go +++ b/app/gitspace/scm/scm_listing_provider.go @@ -16,20 +16,9 @@ package scm import ( "context" - - "github.com/harness/gitness/types" ) -type Provider interface { - ResolveCredentials(ctx context.Context, gitspaceConfig types.GitspaceConfig) (*ResolvedCredentials, error) - - GetFileContent( - ctx context.Context, - gitspaceConfig types.GitspaceConfig, - filePath string, - credentials *ResolvedCredentials, - ) ([]byte, error) - +type ListingProvider interface { ListRepositories( ctx context.Context, filter *RepositoryFilter, diff --git a/app/gitspace/scm/types.go b/app/gitspace/scm/types.go index 4c5ce820e..957c0f94f 100644 --- a/app/gitspace/scm/types.go +++ b/app/gitspace/scm/types.go @@ -33,26 +33,50 @@ type CodeRepositoryResponse struct { CodeRepoIsPrivate bool `json:"is_private"` } +// CredentialType represents the type of credential. +type CredentialType string + +const ( + CredentialTypeUserPassword CredentialType = "user_password" + CredentialTypeOAuthTokenRef CredentialType = "oauth_token_ref" // #nosec G101 +) + type ( ResolvedDetails struct { ResolvedCredentials DevcontainerConfig types.DevcontainerConfig } - // Credentials contains login and initialization information used + // UserPasswordCredentials contains login and initialization information used // by an automated login process. - Credentials struct { + UserPasswordCredentials struct { Email string Name types.MaskSecret Password types.MaskSecret } + OAuth2ClientRefCredentials struct { + ClientID string + ClientSecretRef string + GrantType string + RedirectURI string + Code string + } + + OAuth2TokenRefCredentials struct { + UserPasswordCredentials + RefreshTokenRef string + AccessTokenRef string + } + ResolvedCredentials struct { Branch string // CloneURL contains credentials for private repositories in url prefix - CloneURL types.MaskSecret - Credentials *Credentials - RepoName string + CloneURL types.MaskSecret + UserPasswordCredentials *UserPasswordCredentials + OAuth2TokenRefCredentials *OAuth2TokenRefCredentials + RepoName string + CredentialType CredentialType } RepositoryFilter struct { @@ -67,8 +91,8 @@ type ( SpaceID int64 `json:"space_id"` Repository string `json:"repo"` Query string `json:"query"` - Page int `json:"page"` - Size int `json:"size"` + Page int32 `json:"page"` + Size int32 `json:"size"` RepoURL string `json:"repo_url"` } diff --git a/types/enum/gitspace_cope_repo_type.go b/types/enum/gitspace_cope_repo_type.go index c9eee2d43..ece6b1996 100644 --- a/types/enum/gitspace_cope_repo_type.go +++ b/types/enum/gitspace_cope_repo_type.go @@ -25,13 +25,21 @@ var codeRepoTypes = []GitspaceCodeRepoType{ CodeRepoTypeBitbucket, CodeRepoTypeUnknown, CodeRepoTypeGitness, + CodeRepoTypeGitlabOnPrem, + CodeRepoTypeBitbucketServer, } const ( - CodeRepoTypeGithub GitspaceCodeRepoType = "github" - CodeRepoTypeGitlab GitspaceCodeRepoType = "gitlab" - CodeRepoTypeGitness GitspaceCodeRepoType = "gitness" - CodeRepoTypeHarnessCode GitspaceCodeRepoType = "harness_code" - CodeRepoTypeBitbucket GitspaceCodeRepoType = "bitbucket" - CodeRepoTypeUnknown GitspaceCodeRepoType = "unknown" + CodeRepoTypeGithub GitspaceCodeRepoType = "github" + CodeRepoTypeGitlab GitspaceCodeRepoType = "gitlab" + CodeRepoTypeGitness GitspaceCodeRepoType = "gitness" + CodeRepoTypeHarnessCode GitspaceCodeRepoType = "harness_code" + CodeRepoTypeBitbucket GitspaceCodeRepoType = "bitbucket" + CodeRepoTypeUnknown GitspaceCodeRepoType = "unknown" + CodeRepoTypeGitlabOnPrem GitspaceCodeRepoType = "gitlab_on_prem" + CodeRepoTypeBitbucketServer GitspaceCodeRepoType = "bitbucket_server" ) + +func (p GitspaceCodeRepoType) IsOnPrem() bool { + return p == CodeRepoTypeGitlabOnPrem || p == CodeRepoTypeBitbucketServer +}