Skip to content

Commit

Permalink
fix ssh authenticate
Browse files Browse the repository at this point in the history
  • Loading branch information
wiegelmann committed Apr 15, 2024
1 parent 4d4a4d5 commit 8029108
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 4 deletions.
82 changes: 78 additions & 4 deletions repository/gogit.go
Expand Up @@ -9,6 +9,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
"sync"
Expand All @@ -21,6 +22,7 @@ import (
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"
"golang.org/x/sync/errgroup"
"golang.org/x/sys/execabs"

Expand Down Expand Up @@ -383,14 +385,31 @@ func (repo *GoGitRepo) FetchRefs(remote string, prefixes ...string) (string, err

buf := bytes.NewBuffer(nil)

err := repo.r.Fetch(&gogit.FetchOptions{
fetchOptions := &gogit.FetchOptions{
RemoteName: remote,
RefSpecs: refSpecs,
Progress: buf,
})
}

publicKeys, err := repo.SSHAuth(remote)
if err != nil {
return "", err
}

err = repo.r.Fetch(fetchOptions)
if err == gogit.NoErrAlreadyUpToDate {
return "already up-to-date", nil
}
// ssh-agent is required if ssh or scp-like url is configured in repository config
// we can not fetch if the ssh-agent has invalid keys or ssh-agent is not running
// retry to fetch again if we can retreive public keys from the users home directory
if err != nil && publicKeys != nil {
fetchOptions.Auth = publicKeys
err = repo.r.Fetch(fetchOptions)
if err == gogit.NoErrAlreadyUpToDate {
return "already up-to-date", nil
}
}
if err != nil {
return "", err
}
Expand Down Expand Up @@ -436,21 +455,76 @@ func (repo *GoGitRepo) PushRefs(remote string, prefixes ...string) (string, erro

buf := bytes.NewBuffer(nil)

err = remo.Push(&gogit.PushOptions{
pushOptions := &gogit.PushOptions{
RemoteName: remote,
RefSpecs: refSpecs,
Progress: buf,
})
}

publicKeys, err := repo.SSHAuth(remote)
if err != nil {
return "", err
}

err = repo.r.Push(pushOptions)
if err == gogit.NoErrAlreadyUpToDate {
return "already up-to-date", nil
}
// ssh-agent is required if ssh or scp-like url is configured in repository config
// we can not push if the ssh-agent has invalid keys or ssh-agent is not running
// retry to push again if we can retreive public keys from the users home directory
if err != nil && publicKeys != nil {
pushOptions.Auth = publicKeys
err = repo.r.Push(pushOptions)
if err == gogit.NoErrAlreadyUpToDate {
return "already up-to-date", nil
}
}
if err != nil {
return "", err
}

return buf.String(), nil
}

// SSHAuth will attempt to read public keys for SSH auth
// if the repository remote contains a ssh or scp-like url
func (repo *GoGitRepo) SSHAuth(remote string) (*ssh.PublicKeys, error) {
// get the repository config
config, err := repo.r.Config()
if err != nil {
return nil, err
}

// check if the repository config has at least one remote url
remotes, found := config.Remotes[remote]
if !found || len(remotes.URLs) < 1 {
return nil, fmt.Errorf("remote %s url not found in repository config", remote)
}

schemeRegexp := regexp.MustCompile(`^[^:]+://`)
scpLikeRegexp := regexp.MustCompile(`^(?:(?P<user>[^@]+)@)?(?P<host>)`)

// try to load public keys from the users home directory
// if the repository remote contains a ssh or scp-like url
if strings.HasPrefix(remotes.URLs[0], "ssh://") || (scpLikeRegexp.MatchString(remotes.URLs[0]) && !schemeRegexp.MatchString(remotes.URLs[0])) {
home, err := os.UserHomeDir()
if err != nil {
return nil, err
}

// try to find and load valid public keys from the users home directory
attemptKeys := []string{"id_rsa", "id_ecdsa", "id_ecdsa_sk", "id_ed25519", "id_ed25519_sk", "id_xmss", "id_dsa"}
for _, key := range attemptKeys {
authMethod, err := ssh.NewPublicKeysFromFile("git", filepath.Join(home, ".ssh", key), "")
if err == nil {
return authMethod, nil
}
}
}
return nil, nil
}

// StoreData will store arbitrary data and return the corresponding hash
func (repo *GoGitRepo) StoreData(data []byte) (Hash, error) {
obj := repo.r.Storer.NewEncodedObject()
Expand Down
46 changes: 46 additions & 0 deletions repository/gogit_test.go
Expand Up @@ -2,6 +2,7 @@ package repository

import (
"fmt"
"log"
"os"
"path"
"path/filepath"
Expand Down Expand Up @@ -96,3 +97,48 @@ func TestGoGit_DetectsSubmodules(t *testing.T) {
assert.Empty(t, err)
assert.Equal(t, expected, result)
}

func TestGoGitRepoSSH(t *testing.T) {
repo := CreateGoGitTestRepo(t, false)

err := repo.AddRemote("ssh", "ssh://git@github.com:MichaelMure/git-bug.git")
if err != nil {
log.Fatal(err)
}
keys, err := repo.SSHAuth("ssh")
require.NotNil(t, keys)
require.Empty(t, err)

err = repo.AddRemote("http", "http://github.com/MichaelMure/git-bug.git")
if err != nil {
log.Fatal(err)
}
keys, err = repo.SSHAuth("http")
require.Nil(t, keys)
require.Empty(t, err)

err = repo.AddRemote("https", "https://github.com/MichaelMure/git-bug.git")
if err != nil {
log.Fatal(err)
}
keys, err = repo.SSHAuth("https")
require.Nil(t, keys)
require.Empty(t, err)

err = repo.AddRemote("git", "git://github.com/MichaelMure/git-bug.git")
if err != nil {
log.Fatal(err)
}
keys, err = repo.SSHAuth("git")
require.Nil(t, keys)
require.Empty(t, err)

err = repo.AddRemote("scp-like", "git@github.com:MichaelMure/git-bug.git")
if err != nil {
log.Fatal(err)
}
keys, err = repo.SSHAuth("scp-like")
require.NotNil(t, keys)
require.Empty(t, err)

}
5 changes: 5 additions & 0 deletions repository/mock_repo.go
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/99designs/keyring"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-billy/v5/memfs"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"

"github.com/MichaelMure/git-bug/util/lamport"
)
Expand Down Expand Up @@ -252,6 +253,10 @@ func (r *mockRepoData) PushRefs(remote string, prefixes ...string) (string, erro
panic("implement me")
}

func (r *mockRepoData) SSHAuth(remote string) (*ssh.PublicKeys, error) {
panic("implement me")
}

func (r *mockRepoData) StoreData(data []byte) (Hash, error) {
rawHash := sha1.Sum(data)
hash := Hash(fmt.Sprintf("%x", rawHash))
Expand Down
4 changes: 4 additions & 0 deletions repository/repo.go
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/ProtonMail/go-crypto/openpgp"
"github.com/go-git/go-billy/v5"
"github.com/go-git/go-git/v5/plumbing/transport/ssh"

"github.com/MichaelMure/git-bug/util/lamport"
)
Expand Down Expand Up @@ -142,6 +143,9 @@ type RepoData interface {
// the remote state.
PushRefs(remote string, prefixes ...string) (string, error)

// SSHAuth will attempt to read public keys for SSH auth
SSHAuth(remote string) (*ssh.PublicKeys, error)

// StoreData will store arbitrary data and return the corresponding hash
StoreData(data []byte) (Hash, error)

Expand Down

0 comments on commit 8029108

Please sign in to comment.