git-lfs/lfshttp/ssh.go
Chris Darroch dd8e306e31 all: update go.mod module path with explicit v2
When our go.mod file was introduced in commit
114e85c2002091eb415040923d872f8e4a4bc636 in PR #3208, the module
path chosen did not include a trailing /v2 component.  However,
the Go modules specification now advises that module paths must
have a "major version suffix" which matches the release version.

We therefore add a /v2 suffix to our module path and all its
instances in import paths.

See also https://golang.org/ref/mod#major-version-suffixes for
details regarding the Go module system's major version suffix rule.
2021-08-09 23:18:38 -07:00

115 lines
2.8 KiB
Go

package lfshttp
import (
"bytes"
"encoding/json"
"strings"
"time"
"github.com/git-lfs/git-lfs/v2/config"
"github.com/git-lfs/git-lfs/v2/ssh"
"github.com/git-lfs/git-lfs/v2/subprocess"
"github.com/git-lfs/git-lfs/v2/tools"
"github.com/rubyist/tracerx"
)
type SSHResolver interface {
Resolve(Endpoint, string) (sshAuthResponse, error)
}
func withSSHCache(ssh SSHResolver) SSHResolver {
return &sshCache{
endpoints: make(map[string]*sshAuthResponse),
ssh: ssh,
}
}
type sshCache struct {
endpoints map[string]*sshAuthResponse
ssh SSHResolver
}
func (c *sshCache) Resolve(e Endpoint, method string) (sshAuthResponse, error) {
if len(e.SSHMetadata.UserAndHost) == 0 {
return sshAuthResponse{}, nil
}
key := strings.Join([]string{e.SSHMetadata.UserAndHost, e.SSHMetadata.Port, e.SSHMetadata.Path, method}, "//")
if res, ok := c.endpoints[key]; ok {
if _, expired := res.IsExpiredWithin(5 * time.Second); !expired {
tracerx.Printf("ssh cache: %s git-lfs-authenticate %s %s",
e.SSHMetadata.UserAndHost, e.SSHMetadata.Path, endpointOperation(e, method))
return *res, nil
} else {
tracerx.Printf("ssh cache expired: %s git-lfs-authenticate %s %s",
e.SSHMetadata.UserAndHost, e.SSHMetadata.Path, endpointOperation(e, method))
}
}
res, err := c.ssh.Resolve(e, method)
if err == nil {
c.endpoints[key] = &res
}
return res, err
}
type sshAuthResponse struct {
Message string `json:"-"`
Href string `json:"href"`
Header map[string]string `json:"header"`
ExpiresAt time.Time `json:"expires_at"`
ExpiresIn int `json:"expires_in"`
createdAt time.Time
}
func (r *sshAuthResponse) IsExpiredWithin(d time.Duration) (time.Time, bool) {
return tools.IsExpiredAtOrIn(r.createdAt, d, r.ExpiresAt,
time.Duration(r.ExpiresIn)*time.Second)
}
type sshAuthClient struct {
os config.Environment
git config.Environment
}
func (c *sshAuthClient) Resolve(e Endpoint, method string) (sshAuthResponse, error) {
res := sshAuthResponse{}
if len(e.SSHMetadata.UserAndHost) == 0 {
return res, nil
}
exe, args := ssh.GetLFSExeAndArgs(c.os, c.git, &e.SSHMetadata, "git-lfs-authenticate", endpointOperation(e, method), false)
cmd := subprocess.ExecCommand(exe, args...)
// Save stdout and stderr in separate buffers
var outbuf, errbuf bytes.Buffer
cmd.Stdout = &outbuf
cmd.Stderr = &errbuf
now := time.Now()
// Execute command
err := cmd.Start()
if err == nil {
err = cmd.Wait()
}
// Processing result
if err != nil {
res.Message = strings.TrimSpace(errbuf.String())
} else {
err = json.Unmarshal(outbuf.Bytes(), &res)
if res.ExpiresIn == 0 && res.ExpiresAt.IsZero() {
ttl := c.git.Int("lfs.defaulttokenttl", 0)
if ttl < 0 {
ttl = 0
}
res.ExpiresIn = ttl
}
res.createdAt = now
}
return res, err
}