Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added --ssh-auth option #215

Merged
merged 3 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions cmd/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ func configurePlatform(cmd *cobra.Command) {
flags.StringSliceP("repo", "R", nil, "The name, including owner of a GitHub repository in the format \"ownerName/repoName\".")
flags.StringSliceP("project", "P", nil, "The name, including owner of a GitLab project in the format \"ownerName/repoName\".")
flags.BoolP("include-subgroups", "", false, "Include GitLab subgroups when using the --group flag.")
flags.BoolP("ssh-auth", "", false, `Use SSH cloning URL instead of HTTPS + token. This requires that a setup with ssh keys that have access to all repos and that the server is already in known_hosts.`)

flags.StringP("platform", "p", "github", "The platform that is used. Available values: github, gitlab, gitea, bitbucket_server.")
_ = cmd.RegisterFlagCompletionFunc("platform", func(cmd *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
Expand Down Expand Up @@ -120,6 +121,7 @@ func createGithubClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi
repos, _ := flag.GetStringSlice("repo")
forkMode, _ := flag.GetBool("fork")
forkOwner, _ := flag.GetString("fork-owner")
sshAuth, _ := flag.GetBool("ssh-auth")

if verifyFlags && len(orgs) == 0 && len(users) == 0 && len(repos) == 0 {
return nil, errors.New("no organization, user or repo set")
Expand Down Expand Up @@ -147,7 +149,7 @@ func createGithubClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi
Organizations: orgs,
Users: users,
Repositories: repoRefs,
}, mergeTypes, forkMode, forkOwner)
}, mergeTypes, forkMode, forkOwner, sshAuth)
if err != nil {
return nil, err
}
Expand All @@ -161,6 +163,7 @@ func createGitlabClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi
users, _ := flag.GetStringSlice("user")
projects, _ := flag.GetStringSlice("project")
includeSubgroups, _ := flag.GetBool("include-subgroups")
sshAuth, _ := flag.GetBool("ssh-auth")

if verifyFlags && len(groups) == 0 && len(users) == 0 && len(projects) == 0 {
return nil, errors.New("no group user or project set")
Expand All @@ -185,6 +188,7 @@ func createGitlabClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versi
Projects: projRefs,
}, gitlab.Config{
IncludeSubgroups: includeSubgroups,
SSHAuth: sshAuth,
})
if err != nil {
return nil, err
Expand All @@ -198,6 +202,7 @@ func createGiteaClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versio
orgs, _ := flag.GetStringSlice("org")
users, _ := flag.GetStringSlice("user")
repos, _ := flag.GetStringSlice("repo")
sshAuth, _ := flag.GetBool("ssh-auth")

if verifyFlags && len(orgs) == 0 && len(users) == 0 && len(repos) == 0 {
return nil, errors.New("no organization, user or repository set")
Expand Down Expand Up @@ -229,7 +234,7 @@ func createGiteaClient(flag *flag.FlagSet, verifyFlags bool) (multigitter.Versio
Organizations: orgs,
Users: users,
Repositories: repoRefs,
}, mergeTypes)
}, mergeTypes, sshAuth)
if err != nil {
return nil, err
}
Expand All @@ -244,6 +249,7 @@ func createBitbucketServerClient(flag *flag.FlagSet, verifyFlags bool) (multigit
repos, _ := flag.GetStringSlice("repo")
username, _ := flag.GetString("username")
insecure, _ := flag.GetBool("insecure")
sshAuth, _ := flag.GetBool("ssh-auth")

if verifyFlags && len(projects) == 0 && len(users) == 0 && len(repos) == 0 {
return nil, errors.New("no organization, user or repository set")
Expand All @@ -270,7 +276,7 @@ func createBitbucketServerClient(flag *flag.FlagSet, verifyFlags bool) (multigit
}
}

vc, err := bitbucketserver.New(username, token, bitbucketServerBaseURL, insecure, http.NewLoggingRoundTripper, bitbucketserver.RepositoryListing{
vc, err := bitbucketserver.New(username, token, bitbucketServerBaseURL, insecure, sshAuth, http.NewLoggingRoundTripper, bitbucketserver.RepositoryListing{
Projects: projects,
Users: users,
Repositories: repoRefs,
Expand Down
9 changes: 6 additions & 3 deletions internal/scm/bitbucketserver/bitbucket_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ import (
)

const (
cloneType = "http"
cloneHTTPType = "http"
cloneSSHType = "ssh"
stateMerged = "MERGED"
stateDeclined = "DECLINED"
)

// New create a new BitbucketServer client
func New(username, token, baseURL string, insecure bool, transportMiddleware func(http.RoundTripper) http.RoundTripper, repoListing RepositoryListing) (*BitbucketServer, error) {
func New(username, token, baseURL string, insecure bool, sshAuth bool, transportMiddleware func(http.RoundTripper) http.RoundTripper, repoListing RepositoryListing) (*BitbucketServer, error) {
if strings.TrimSpace(token) == "" {
return nil, errors.New("token is empty")
}
Expand All @@ -49,6 +50,7 @@ func New(username, token, baseURL string, insecure bool, transportMiddleware fun
bitbucketServer.baseURL = bitbucketBaseURL
bitbucketServer.username = username
bitbucketServer.token = token
bitbucketServer.sshAuth = sshAuth
bitbucketServer.httpClient = &http.Client{
Transport: transportMiddleware(&http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure}, // nolint: gosec
Expand Down Expand Up @@ -78,6 +80,7 @@ type BitbucketServer struct {
RepositoryListing
baseURL *url.URL
username, token string
sshAuth bool
config *bitbucketv1.Configuration
httpClient *http.Client
}
Expand Down Expand Up @@ -136,7 +139,7 @@ func (b *BitbucketServer) GetRepositories(ctx context.Context) ([]scm.Repository
return nil, err
}

repo, repoErr := convertRepository(bitbucketRepository, defaultBranch, b.username, b.token)
repo, repoErr := b.convertRepository(bitbucketRepository, defaultBranch)
if repoErr != nil {
return nil, repoErr
}
Expand Down
54 changes: 29 additions & 25 deletions internal/scm/bitbucketserver/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,54 +8,58 @@ import (
"github.com/pkg/errors"
)

func convertRepository(bitbucketRepository *bitbucketv1.Repository, defaultBranch bitbucketv1.Branch, username, token string) (*repository, error) {
var cloneURL *url.URL
var err error
for _, clone := range bitbucketRepository.Links.Clone {
if strings.EqualFold(clone.Name, cloneType) {
cloneURL, err = url.Parse(clone.Href)
if err != nil {
return nil, err
}
func (b *BitbucketServer) convertRepository(bitbucketRepository *bitbucketv1.Repository, defaultBranch bitbucketv1.Branch) (*repository, error) {
var cloneURL string

break
if b.sshAuth {
cloneURL = findLinkType(bitbucketRepository.Links.Clone, cloneSSHType)
if cloneURL == "" {
return nil, errors.Errorf("unable to find clone url for repository %s using clone type %s", bitbucketRepository.Name, cloneSSHType)
}
} else {
httpURL := findLinkType(bitbucketRepository.Links.Clone, cloneHTTPType)
if httpURL == "" {
return nil, errors.Errorf("unable to find clone url for repository %s using clone type %s", bitbucketRepository.Name, cloneHTTPType)
}
parsedURL, err := url.Parse(httpURL)
if err != nil {
return nil, err
}
}

if cloneURL == nil {
return nil, errors.Errorf("unable to find clone url for repostory %s using clone type %s", bitbucketRepository.Name, cloneType)
parsedURL.User = url.UserPassword(b.username, b.token)
cloneURL = parsedURL.String()
}

repo := repository{
name: bitbucketRepository.Slug,
project: bitbucketRepository.Project.Key,
defaultBranch: defaultBranch.DisplayID,
username: username,
token: token,
cloneURL: cloneURL,
}

return &repo, nil
}

func findLinkType(links []bitbucketv1.CloneLink, cloneType string) string {
for _, clone := range links {
if strings.EqualFold(clone.Name, cloneType) {
return clone.Href
}
}

return ""
}

// repository contains information about a bitbucket repository
type repository struct {
name string
project string
defaultBranch string
username string
token string
cloneURL *url.URL
cloneURL string
}

func (r repository) CloneURL() string {
cloneURL := r.cloneURL

if r.username != "" && r.token != "" {
cloneURL.User = url.UserPassword(r.username, r.token)
}

return cloneURL.String()
return r.cloneURL
}

func (r repository) DefaultBranch() string {
Expand Down
10 changes: 6 additions & 4 deletions internal/scm/gitea/gitea.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ import (
)

// New create a new Gitea client
func New(token, baseURL string, repoListing RepositoryListing, mergeTypes []scm.MergeType) (*Gitea, error) {
func New(token, baseURL string, repoListing RepositoryListing, mergeTypes []scm.MergeType, sshAuth bool) (*Gitea, error) {
gitea := &Gitea{
RepositoryListing: repoListing,

baseURL: baseURL,
token: token,

MergeTypes: mergeTypes,
SSHAuth: sshAuth,
}

// Initialize the gitea client to ensure no error will occur when running a function
Expand Down Expand Up @@ -58,6 +59,7 @@ type Gitea struct {
currentUser *gitea.User

MergeTypes []scm.MergeType
SSHAuth bool
}

// RepositoryListing contains information about which repositories that should be fetched
Expand Down Expand Up @@ -94,7 +96,7 @@ func (g *Gitea) GetRepositories(ctx context.Context) ([]scm.Repository, error) {

repos := make([]scm.Repository, 0, len(allRepos))
for _, repo := range allRepos {
convertedRepo, err := convertRepository(repo, g.token)
convertedRepo, err := g.convertRepository(repo)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -424,7 +426,7 @@ func (g *Gitea) ForkRepository(ctx context.Context, repo scm.Repository, newOwne

existingRepo, _, err := g.giteaClient(ctx).GetRepo(forkTo, r.name)
if err == nil { // NB!
return convertRepository(existingRepo, g.token)
return g.convertRepository(existingRepo)
}

forkOptions := gitea.CreateForkOption{}
Expand All @@ -437,7 +439,7 @@ func (g *Gitea) ForkRepository(ctx context.Context, repo scm.Repository, newOwne
return nil, err
}

return convertRepository(createdRepo, g.token)
return g.convertRepository(createdRepo)
}

func (g *Gitea) getUser(ctx context.Context) (*gitea.User, error) {
Expand Down
26 changes: 15 additions & 11 deletions internal/scm/gitea/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,37 @@ import (
"code.gitea.io/sdk/gitea"
)

func convertRepository(repo *gitea.Repository, token string) (repository, error) {
u, err := url.Parse(repo.CloneURL)
if err != nil {
return repository{}, err
func (g *Gitea) convertRepository(repo *gitea.Repository) (repository, error) {
var repoURL string
if g.SSHAuth {
repoURL = repo.SSHURL
} else {
u, err := url.Parse(repo.CloneURL)
if err != nil {
return repository{}, err
}
// Set the token as https://oauth2:TOKEN@url
u.User = url.UserPassword("oauth2", g.token)
repoURL = u.String()
}

return repository{
url: *u,
url: repoURL,
name: repo.Name,
ownerName: repo.Owner.UserName,
defaultBranch: repo.DefaultBranch,
token: token,
}, nil
}

type repository struct {
url url.URL
url string
name string
ownerName string
defaultBranch string
token string
}

func (r repository) CloneURL() string {
// Set the token as https://oauth2:TOKEN@url
r.url.User = url.UserPassword("oauth2", r.token)
return r.url.String()
return r.url
}

func (r repository) DefaultBranch() string {
Expand Down
11 changes: 8 additions & 3 deletions internal/scm/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ func New(
mergeTypes []scm.MergeType,
forkMode bool,
forkOwner string,
sshAuth bool,
) (*Github, error) {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
Expand All @@ -50,6 +51,7 @@ func New(
token: token,
Fork: forkMode,
ForkOwner: forkOwner,
SSHAuth: sshAuth,
ghClient: client,
}, nil
}
Expand All @@ -67,6 +69,9 @@ type Github struct {
// If set, the fork will happen to the ForkOwner value, and not the logged in user
ForkOwner string

// If set, use the SSH clone url instead of http(s)
SSHAuth bool

ghClient *github.Client

// Caching of the logged in user
Expand Down Expand Up @@ -127,7 +132,7 @@ func (g *Github) GetRepositories(ctx context.Context) ([]scm.Repository, error)
continue
}

newRepo, err := convertRepo(r, g.token)
newRepo, err := g.convertRepo(r)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -482,13 +487,13 @@ func (g *Github) ForkRepository(ctx context.Context, repo scm.Repository, newOwn
continue
}
// The fork does now exist
return convertRepo(repo, g.token)
return g.convertRepo(repo)
}

return nil, errors.New("time waiting for fork to complete was exceeded")
}

return convertRepo(createdRepo, g.token)
return g.convertRepo(createdRepo)
}

// GetAutocompleteOrganizations gets organizations for autocompletion
Expand Down
8 changes: 4 additions & 4 deletions internal/scm/github/github_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func Test_GetRepositories(t *testing.T) {
{
gh, err := github.New("", "", transport.Wrapper, github.RepositoryListing{
Organizations: []string{"test-org"},
}, []scm.MergeType{scm.MergeTypeMerge}, false, "")
}, []scm.MergeType{scm.MergeTypeMerge}, false, "", false)
require.NoError(t, err)

repos, err := gh.GetRepositories(context.Background())
Expand All @@ -137,7 +137,7 @@ func Test_GetRepositories(t *testing.T) {
Name: "test1",
},
},
}, []scm.MergeType{scm.MergeTypeMerge}, false, "")
}, []scm.MergeType{scm.MergeTypeMerge}, false, "", false)
require.NoError(t, err)

repos, err := gh.GetRepositories(context.Background())
Expand All @@ -152,7 +152,7 @@ func Test_GetRepositories(t *testing.T) {
{
gh, err := github.New("", "", transport.Wrapper, github.RepositoryListing{
Users: []string{"test-user"},
}, []scm.MergeType{scm.MergeTypeMerge}, false, "")
}, []scm.MergeType{scm.MergeTypeMerge}, false, "", false)
require.NoError(t, err)

repos, err := gh.GetRepositories(context.Background())
Expand All @@ -174,7 +174,7 @@ func Test_GetRepositories(t *testing.T) {
Name: "test1",
},
},
}, []scm.MergeType{scm.MergeTypeMerge}, false, "")
}, []scm.MergeType{scm.MergeTypeMerge}, false, "", false)
require.NoError(t, err)

repos, err := gh.GetRepositories(context.Background())
Expand Down
Loading