Get Netrc machine from clone url (#800)

We previously got the machine hostname for Netrc from the url of the remote, but in cases where the clone-url does not match the api url this can lead to errors.
This commit is contained in:
Anbraten 2022-02-26 02:54:15 +01:00 committed by GitHub
parent 8ae124d5e6
commit 52d8097290
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 97 additions and 85 deletions

View file

@ -285,7 +285,6 @@ func setupCoding(c *cli.Context) (remote.Remote, error) {
Client: c.String("coding-client"), Client: c.String("coding-client"),
Secret: c.String("coding-secret"), Secret: c.String("coding-secret"),
Scopes: c.StringSlice("coding-scope"), Scopes: c.StringSlice("coding-scope"),
Machine: c.String("coding-git-machine"),
Username: c.String("coding-git-username"), Username: c.String("coding-git-username"),
Password: c.String("coding-git-password"), Password: c.String("coding-git-password"),
SkipVerify: c.Bool("coding-skip-verify"), SkipVerify: c.Bool("coding-skip-verify"),

View file

@ -52,7 +52,7 @@ func Test_bitbucket(t *testing.T) {
g.It("Should return the netrc file", func() { g.It("Should return the netrc file", func() {
remote, _ := New(&Opts{}) remote, _ := New(&Opts{})
netrc, _ := remote.Netrc(fakeUser, nil) netrc, _ := remote.Netrc(fakeUser, fakeRepo)
g.Assert(netrc.Machine).Equal("bitbucket.org") g.Assert(netrc.Machine).Equal("bitbucket.org")
g.Assert(netrc.Login).Equal("x-token-auth") g.Assert(netrc.Login).Equal("x-token-auth")
g.Assert(netrc.Password).Equal(fakeUser.Token) g.Assert(netrc.Password).Equal(fakeUser.Token)

View file

@ -26,8 +26,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strings"
"github.com/mrjones/oauth" "github.com/mrjones/oauth"
@ -201,21 +199,15 @@ func (c *Config) Status(ctx context.Context, user *model.User, repo *model.Repo,
} }
func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) { func (c *Config) Netrc(user *model.User, r *model.Repo) (*model.Netrc, error) {
u, err := url.Parse(c.URL) host, err := common.ExtractHostFromCloneURL(r.Clone)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// remove the port
tmp := strings.Split(u.Host, ":")
host := tmp[0]
if err != nil {
return nil, err
}
return &model.Netrc{ return &model.Netrc{
Machine: host,
Login: c.Username, Login: c.Username,
Password: c.Password, Password: c.Password,
Machine: host,
}, nil }, nil
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/remote" "github.com/woodpecker-ci/woodpecker/server/remote"
"github.com/woodpecker-ci/woodpecker/server/remote/coding/internal" "github.com/woodpecker-ci/woodpecker/server/remote/coding/internal"
"github.com/woodpecker-ci/woodpecker/server/remote/common"
) )
const ( const (
@ -39,7 +40,6 @@ type Opts struct {
Client string // Coding oauth client id. Client string // Coding oauth client id.
Secret string // Coding oauth client secret. Secret string // Coding oauth client secret.
Scopes []string // Coding oauth scopes. Scopes []string // Coding oauth scopes.
Machine string // Optional machine name.
Username string // Optional machine account username. Username string // Optional machine account username.
Password string // Optional machine account password. Password string // Optional machine account password.
SkipVerify bool // Skip ssl verification. SkipVerify bool // Skip ssl verification.
@ -53,7 +53,6 @@ func New(opts Opts) (remote.Remote, error) {
Client: opts.Client, Client: opts.Client,
Secret: opts.Secret, Secret: opts.Secret,
Scopes: opts.Scopes, Scopes: opts.Scopes,
Machine: opts.Machine,
Username: opts.Username, Username: opts.Username,
Password: opts.Password, Password: opts.Password,
SkipVerify: opts.SkipVerify, SkipVerify: opts.SkipVerify,
@ -70,7 +69,6 @@ type Coding struct {
Client string Client string
Secret string Secret string
Scopes []string Scopes []string
Machine string
Username string Username string
Password string Password string
SkipVerify bool SkipVerify bool
@ -251,17 +249,23 @@ func (c *Coding) Status(ctx context.Context, u *model.User, r *model.Repo, b *mo
// Netrc returns a .netrc file that can be used to clone // Netrc returns a .netrc file that can be used to clone
// private repositories from a remote system. // private repositories from a remote system.
func (c *Coding) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { func (c *Coding) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
host, err := common.ExtractHostFromCloneURL(r.Clone)
if err != nil {
return nil, err
}
if c.Password != "" { if c.Password != "" {
return &model.Netrc{ return &model.Netrc{
Login: c.Username, Login: c.Username,
Password: c.Password, Password: c.Password,
Machine: c.Machine, Machine: host,
}, nil }, nil
} }
return &model.Netrc{ return &model.Netrc{
Login: u.Token, Login: u.Token,
Password: "x-oauth-basic", Password: "x-oauth-basic",
Machine: c.Machine, Machine: host,
}, nil }, nil
} }

View file

@ -48,7 +48,6 @@ func Test_coding(t *testing.T) {
Client: "KTNF2ALdm3ofbtxLh6IbV95Ro5AKWJUP", Client: "KTNF2ALdm3ofbtxLh6IbV95Ro5AKWJUP",
Secret: "zVtxJrKhNhBcNyqCz1NggNAAmehAxnRO3Z0fXmCp", Secret: "zVtxJrKhNhBcNyqCz1NggNAAmehAxnRO3Z0fXmCp",
Scopes: []string{"user", "project", "project:depot"}, Scopes: []string{"user", "project", "project:depot"},
Machine: "git.coding.net",
Username: "someuser", Username: "someuser",
Password: "password", Password: "password",
SkipVerify: true, SkipVerify: true,
@ -57,7 +56,6 @@ func Test_coding(t *testing.T) {
g.Assert(remote.(*Coding).Client).Equal("KTNF2ALdm3ofbtxLh6IbV95Ro5AKWJUP") g.Assert(remote.(*Coding).Client).Equal("KTNF2ALdm3ofbtxLh6IbV95Ro5AKWJUP")
g.Assert(remote.(*Coding).Secret).Equal("zVtxJrKhNhBcNyqCz1NggNAAmehAxnRO3Z0fXmCp") g.Assert(remote.(*Coding).Secret).Equal("zVtxJrKhNhBcNyqCz1NggNAAmehAxnRO3Z0fXmCp")
g.Assert(remote.(*Coding).Scopes).Equal([]string{"user", "project", "project:depot"}) g.Assert(remote.(*Coding).Scopes).Equal([]string{"user", "project", "project:depot"})
g.Assert(remote.(*Coding).Machine).Equal("git.coding.net")
g.Assert(remote.(*Coding).Username).Equal("someuser") g.Assert(remote.(*Coding).Username).Equal("someuser")
g.Assert(remote.(*Coding).Password).Equal("password") g.Assert(remote.(*Coding).Password).Equal("password")
g.Assert(remote.(*Coding).SkipVerify).Equal(true) g.Assert(remote.(*Coding).SkipVerify).Equal(true)
@ -172,21 +170,18 @@ func Test_coding(t *testing.T) {
g.Describe("When requesting a netrc config", func() { g.Describe("When requesting a netrc config", func() {
g.It("Should return the netrc file for global credential", func() { g.It("Should return the netrc file for global credential", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{
Machine: "git.coding.net",
Username: "someuser", Username: "someuser",
Password: "password", Password: "password",
}) })
netrc, err := remote.Netrc(fakeUser, nil) netrc, err := remote.Netrc(fakeUser, fakeRepo)
g.Assert(err).IsNil() g.Assert(err).IsNil()
g.Assert(netrc.Login).Equal("someuser") g.Assert(netrc.Login).Equal("someuser")
g.Assert(netrc.Password).Equal("password") g.Assert(netrc.Password).Equal("password")
g.Assert(netrc.Machine).Equal("git.coding.net") g.Assert(netrc.Machine).Equal("git.coding.net")
}) })
g.It("Should return the netrc file for specified user", func() { g.It("Should return the netrc file for specified user", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{})
Machine: "git.coding.net", netrc, err := remote.Netrc(fakeUser, fakeRepo)
})
netrc, err := remote.Netrc(fakeUser, nil)
g.Assert(err).IsNil() g.Assert(err).IsNil()
g.Assert(netrc.Login).Equal(fakeUser.Token) g.Assert(netrc.Login).Equal(fakeUser.Token)
g.Assert(netrc.Password).Equal("x-oauth-basic") g.Assert(netrc.Password).Equal("x-oauth-basic")

View file

@ -0,0 +1,25 @@
package common
import (
"net"
"net/url"
"strings"
)
func ExtractHostFromCloneURL(cloneURL string) (string, error) {
u, err := url.Parse(cloneURL)
if err != nil {
return "", err
}
if !strings.Contains(u.Host, ":") {
return u.Host, nil
}
host, _, err := net.SplitHostPort(u.Host)
if err != nil {
return "", err
}
return host, nil
}

View file

@ -0,0 +1,18 @@
package common_test
import (
"testing"
"github.com/woodpecker-ci/woodpecker/server/remote/common"
)
func Test_Netrc(t *testing.T) {
host, err := common.ExtractHostFromCloneURL("https://git.example.com/foo/bar.git")
if err != nil {
t.Fatal(err)
}
if host != "git.example.com" {
t.Errorf("Expected host to be git.example.com, got %s", host)
}
}

View file

@ -44,7 +44,6 @@ const (
type Gitea struct { type Gitea struct {
URL string URL string
Machine string
ClientID string ClientID string
ClientSecret string ClientSecret string
SkipVerify bool SkipVerify bool
@ -71,7 +70,6 @@ func New(opts Opts) (remote.Remote, error) {
} }
return &Gitea{ return &Gitea{
URL: opts.URL, URL: opts.URL,
Machine: u.Host,
ClientID: opts.Client, ClientID: opts.Client,
ClientSecret: opts.Secret, ClientSecret: opts.Secret,
SkipVerify: opts.SkipVerify, SkipVerify: opts.SkipVerify,
@ -355,10 +353,15 @@ func (c *Gitea) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
token = u.Token token = u.Token
} }
host, err := common.ExtractHostFromCloneURL(r.Clone)
if err != nil {
return nil, err
}
return &model.Netrc{ return &model.Netrc{
Login: login, Login: login,
Password: token, Password: token,
Machine: c.Machine, Machine: host,
}, nil }, nil
} }

View file

@ -49,7 +49,6 @@ func Test_gitea(t *testing.T) {
SkipVerify: true, SkipVerify: true,
}) })
g.Assert(remote.(*Gitea).URL).Equal("http://localhost:8080") g.Assert(remote.(*Gitea).URL).Equal("http://localhost:8080")
g.Assert(remote.(*Gitea).Machine).Equal("localhost")
g.Assert(remote.(*Gitea).SkipVerify).Equal(true) g.Assert(remote.(*Gitea).SkipVerify).Equal(true)
}) })
g.It("Should handle malformed url", func() { g.It("Should handle malformed url", func() {
@ -60,19 +59,15 @@ func Test_gitea(t *testing.T) {
g.Describe("Generating a netrc file", func() { g.Describe("Generating a netrc file", func() {
g.It("Should return a netrc with the user token", func() { g.It("Should return a netrc with the user token", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{})
URL: "http://gitea.com", netrc, _ := remote.Netrc(fakeUser, fakeRepo)
})
netrc, _ := remote.Netrc(fakeUser, nil)
g.Assert(netrc.Machine).Equal("gitea.com") g.Assert(netrc.Machine).Equal("gitea.com")
g.Assert(netrc.Login).Equal(fakeUser.Login) g.Assert(netrc.Login).Equal(fakeUser.Login)
g.Assert(netrc.Password).Equal(fakeUser.Token) g.Assert(netrc.Password).Equal(fakeUser.Token)
}) })
g.It("Should return a netrc with the machine account", func() { g.It("Should return a netrc with the machine account", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{})
URL: "http://gitea.com", netrc, _ := remote.Netrc(nil, fakeRepo)
})
netrc, _ := remote.Netrc(nil, nil)
g.Assert(netrc.Machine).Equal("gitea.com") g.Assert(netrc.Machine).Equal("gitea.com")
g.Assert(netrc.Login).Equal("") g.Assert(netrc.Login).Equal("")
g.Assert(netrc.Password).Equal("") g.Assert(netrc.Password).Equal("")
@ -172,6 +167,7 @@ var (
} }
fakeRepo = &model.Repo{ fakeRepo = &model.Repo{
Clone: "http://gitea.com/test_name/repo_name.git",
Owner: "test_name", Owner: "test_name",
Name: "repo_name", Name: "repo_name",
FullName: "test_name/repo_name", FullName: "test_name/repo_name",

View file

@ -18,7 +18,6 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"net"
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
@ -54,14 +53,6 @@ type Opts struct {
// New returns a Remote implementation that integrates with a GitHub Cloud or // New returns a Remote implementation that integrates with a GitHub Cloud or
// GitHub Enterprise version control hosting provider. // GitHub Enterprise version control hosting provider.
func New(opts Opts) (remote.Remote, error) { func New(opts Opts) (remote.Remote, error) {
u, err := url.Parse(opts.URL)
if err != nil {
return nil, err
}
host, _, err := net.SplitHostPort(u.Host)
if err == nil {
u.Host = host
}
r := &client{ r := &client{
API: defaultAPI, API: defaultAPI,
URL: defaultURL, URL: defaultURL,
@ -69,7 +60,6 @@ func New(opts Opts) (remote.Remote, error) {
Secret: opts.Secret, Secret: opts.Secret,
SkipVerify: opts.SkipVerify, SkipVerify: opts.SkipVerify,
MergeRef: opts.MergeRef, MergeRef: opts.MergeRef,
Machine: u.Host,
} }
if opts.URL != defaultURL { if opts.URL != defaultURL {
r.URL = strings.TrimSuffix(opts.URL, "/") r.URL = strings.TrimSuffix(opts.URL, "/")
@ -84,7 +74,6 @@ type client struct {
API string API string
Client string Client string
Secret string Secret string
Machine string
SkipVerify bool SkipVerify bool
MergeRef bool MergeRef bool
} }
@ -283,10 +272,15 @@ func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
token = "x-oauth-basic" token = "x-oauth-basic"
} }
host, err := common.ExtractHostFromCloneURL(r.Clone)
if err != nil {
return nil, err
}
return &model.Netrc{ return &model.Netrc{
Login: login, Login: login,
Password: token, Password: token,
Machine: c.Machine, Machine: host,
}, nil }, nil
} }

View file

@ -52,32 +52,23 @@ func Test_github(t *testing.T) {
}) })
g.Assert(remote.(*client).URL).Equal("http://localhost:8080") g.Assert(remote.(*client).URL).Equal("http://localhost:8080")
g.Assert(remote.(*client).API).Equal("http://localhost:8080/api/v3/") g.Assert(remote.(*client).API).Equal("http://localhost:8080/api/v3/")
g.Assert(remote.(*client).Machine).Equal("localhost")
g.Assert(remote.(*client).Client).Equal("0ZXh0IjoiI") g.Assert(remote.(*client).Client).Equal("0ZXh0IjoiI")
g.Assert(remote.(*client).Secret).Equal("I1NiIsInR5") g.Assert(remote.(*client).Secret).Equal("I1NiIsInR5")
g.Assert(remote.(*client).SkipVerify).Equal(true) g.Assert(remote.(*client).SkipVerify).Equal(true)
}) })
g.It("Should handle malformed url", func() {
_, err := New(Opts{URL: "%gh&%ij"})
g.Assert(err).IsNotNil()
})
}) })
g.Describe("Generating a netrc file", func() { g.Describe("Generating a netrc file", func() {
g.It("Should return a netrc with the user token", func() { g.It("Should return a netrc with the user token", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{})
URL: "http://github.com:443", netrc, _ := remote.Netrc(fakeUser, fakeRepo)
})
netrc, _ := remote.Netrc(fakeUser, nil)
g.Assert(netrc.Machine).Equal("github.com") g.Assert(netrc.Machine).Equal("github.com")
g.Assert(netrc.Login).Equal(fakeUser.Token) g.Assert(netrc.Login).Equal(fakeUser.Token)
g.Assert(netrc.Password).Equal("x-oauth-basic") g.Assert(netrc.Password).Equal("x-oauth-basic")
}) })
g.It("Should return a netrc with the machine account", func() { g.It("Should return a netrc with the machine account", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{})
URL: "http://github.com:443", netrc, _ := remote.Netrc(nil, fakeRepo)
})
netrc, _ := remote.Netrc(nil, nil)
g.Assert(netrc.Machine).Equal("github.com") g.Assert(netrc.Machine).Equal("github.com")
g.Assert(netrc.Login).Equal("") g.Assert(netrc.Login).Equal("")
g.Assert(netrc.Password).Equal("") g.Assert(netrc.Password).Equal("")

View file

@ -19,7 +19,6 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -54,7 +53,6 @@ type Gitlab struct {
URL string URL string
ClientID string ClientID string
ClientSecret string ClientSecret string
Machine string
SkipVerify bool SkipVerify bool
HideArchives bool HideArchives bool
Search bool Search bool
@ -63,19 +61,10 @@ type Gitlab struct {
// New returns a Remote implementation that integrates with Gitlab, an open // New returns a Remote implementation that integrates with Gitlab, an open
// source Git service. See https://gitlab.com // source Git service. See https://gitlab.com
func New(opts Opts) (remote.Remote, error) { func New(opts Opts) (remote.Remote, error) {
u, err := url.Parse(opts.URL)
if err != nil {
return nil, err
}
host, _, err := net.SplitHostPort(u.Host)
if err == nil {
u.Host = host
}
return &Gitlab{ return &Gitlab{
URL: opts.URL, URL: opts.URL,
ClientID: opts.ClientID, ClientID: opts.ClientID,
ClientSecret: opts.ClientSecret, ClientSecret: opts.ClientSecret,
Machine: u.Host,
SkipVerify: opts.SkipVerify, SkipVerify: opts.SkipVerify,
}, nil }, nil
} }
@ -386,10 +375,15 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
token = u.Token token = u.Token
} }
host, err := common.ExtractHostFromCloneURL(r.Clone)
if err != nil {
return nil, err
}
return &model.Netrc{ return &model.Netrc{
Login: login, Login: login,
Password: token, Password: token,
Machine: g.Machine, Machine: host,
}, nil }, nil
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/server/remote" "github.com/woodpecker-ci/woodpecker/server/remote"
"github.com/woodpecker-ci/woodpecker/server/remote/common"
) )
// Opts defines configuration options. // Opts defines configuration options.
@ -40,7 +41,6 @@ type Opts struct {
type client struct { type client struct {
URL string URL string
Machine string
Username string Username string
Password string Password string
PrivateMode bool PrivateMode bool
@ -60,7 +60,6 @@ func New(opts Opts) (remote.Remote, error) {
} }
return &client{ return &client{
URL: opts.URL, URL: opts.URL,
Machine: u.Host,
Username: opts.Username, Username: opts.Username,
Password: opts.Password, Password: opts.Password,
PrivateMode: opts.PrivateMode, PrivateMode: opts.PrivateMode,
@ -217,17 +216,22 @@ func (c *client) Status(ctx context.Context, u *model.User, r *model.Repo, b *mo
// cloning Gogs repositories. The netrc will use the global machine account // cloning Gogs repositories. The netrc will use the global machine account
// when configured. // when configured.
func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) { func (c *client) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
host, err := common.ExtractHostFromCloneURL(r.Clone)
if err != nil {
return nil, err
}
if c.Password != "" { if c.Password != "" {
return &model.Netrc{ return &model.Netrc{
Login: c.Username, Login: c.Username,
Password: c.Password, Password: c.Password,
Machine: c.Machine, Machine: host,
}, nil }, nil
} }
return &model.Netrc{ return &model.Netrc{
Login: u.Token, Login: u.Token,
Password: "x-oauth-basic", Password: "x-oauth-basic",
Machine: c.Machine, Machine: host,
}, nil }, nil
} }

View file

@ -52,7 +52,6 @@ func Test_gogs(t *testing.T) {
PrivateMode: true, PrivateMode: true,
}) })
g.Assert(remote.(*client).URL).Equal("http://localhost:8080") g.Assert(remote.(*client).URL).Equal("http://localhost:8080")
g.Assert(remote.(*client).Machine).Equal("localhost")
g.Assert(remote.(*client).Username).Equal("someuser") g.Assert(remote.(*client).Username).Equal("someuser")
g.Assert(remote.(*client).Password).Equal("password") g.Assert(remote.(*client).Password).Equal("password")
g.Assert(remote.(*client).SkipVerify).Equal(true) g.Assert(remote.(*client).SkipVerify).Equal(true)
@ -66,21 +65,18 @@ func Test_gogs(t *testing.T) {
g.Describe("Generating a netrc file", func() { g.Describe("Generating a netrc file", func() {
g.It("Should return a netrc with the user token", func() { g.It("Should return a netrc with the user token", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{})
URL: "http://gogs.com", netrc, _ := remote.Netrc(fakeUser, fakeRepo)
})
netrc, _ := remote.Netrc(fakeUser, nil)
g.Assert(netrc.Machine).Equal("gogs.com") g.Assert(netrc.Machine).Equal("gogs.com")
g.Assert(netrc.Login).Equal(fakeUser.Token) g.Assert(netrc.Login).Equal(fakeUser.Token)
g.Assert(netrc.Password).Equal("x-oauth-basic") g.Assert(netrc.Password).Equal("x-oauth-basic")
}) })
g.It("Should return a netrc with the machine account", func() { g.It("Should return a netrc with the machine account", func() {
remote, _ := New(Opts{ remote, _ := New(Opts{
URL: "http://gogs.com",
Username: "someuser", Username: "someuser",
Password: "password", Password: "password",
}) })
netrc, _ := remote.Netrc(nil, nil) netrc, _ := remote.Netrc(nil, fakeRepo)
g.Assert(netrc.Machine).Equal("gogs.com") g.Assert(netrc.Machine).Equal("gogs.com")
g.Assert(netrc.Login).Equal("someuser") g.Assert(netrc.Login).Equal("someuser")
g.Assert(netrc.Password).Equal("password") g.Assert(netrc.Password).Equal("password")
@ -185,6 +181,7 @@ var (
} }
fakeRepo = &model.Repo{ fakeRepo = &model.Repo{
Clone: "http://gogs.com/test_name/repo_name.git",
Owner: "test_name", Owner: "test_name",
Name: "repo_name", Name: "repo_name",
FullName: "test_name/repo_name", FullName: "test_name/repo_name",