From e2cdde02bc16b87eb764bf8507c463efb513b9b5 Mon Sep 17 00:00:00 2001 From: Kirilll Zaitsev Date: Wed, 2 Sep 2015 06:42:18 +0300 Subject: [PATCH] Added clone mode for gitlab, use token mode as default --- doc/setup/gitlab.md | 1 + pkg/hash/hash.go | 12 +++++ pkg/remote/builtin/github/github.go | 2 +- pkg/remote/builtin/gitlab/gitlab.go | 84 +++++++++++++++++------------ pkg/remote/remote.go | 2 +- pkg/server/commits.go | 2 +- pkg/server/gitlab.go | 6 ++- pkg/server/hooks.go | 5 +- pkg/server/repos.go | 11 +--- 9 files changed, 75 insertions(+), 50 deletions(-) create mode 100644 pkg/hash/hash.go diff --git a/doc/setup/gitlab.md b/doc/setup/gitlab.md index cc3dbff47..edcd2afab 100644 --- a/doc/setup/gitlab.md +++ b/doc/setup/gitlab.md @@ -31,6 +31,7 @@ This section lists all connection options used in the connection string format. * `open=false` allows users to self-register. Defaults to false for security reasons. * `orgs=drone,docker` restricts access to these GitLab organizations. **Optional** * `skip_verify=false` skip ca verification if self-signed certificate. Defaults to false for security reasons. +* `clone_mode=token` a strategy for clone authorization, by default use repo token, but can be changed to `oauth` ( This is not secure, because your user token, with full access to your gitlab account will be written to .netrc, and it can be read by all who have access to project builds ) ## Gitlab registration diff --git a/pkg/hash/hash.go b/pkg/hash/hash.go new file mode 100644 index 000000000..3950244cb --- /dev/null +++ b/pkg/hash/hash.go @@ -0,0 +1,12 @@ +package hash + +import ( + "crypto/sha256" + "encoding/hex" +) + +func New(text, salt string) string { + hasher := sha256.New() + hasher.Write([]byte(text + salt)) + return hex.EncodeToString(hasher.Sum(nil)) +} diff --git a/pkg/remote/builtin/github/github.go b/pkg/remote/builtin/github/github.go index 40173ff34..4829565fc 100644 --- a/pkg/remote/builtin/github/github.go +++ b/pkg/remote/builtin/github/github.go @@ -170,7 +170,7 @@ func (g *GitHub) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte // Netrc returns a .netrc file that can be used to clone // private repositories from a remote system. -func (g *GitHub) Netrc(u *common.User) (*common.Netrc, error) { +func (g *GitHub) Netrc(u *common.User, r *common.Repo) (*common.Netrc, error) { url_, err := url.Parse(g.URL) if err != nil { return nil, err diff --git a/pkg/remote/builtin/gitlab/gitlab.go b/pkg/remote/builtin/gitlab/gitlab.go index 74c644851..c100d21c2 100644 --- a/pkg/remote/builtin/gitlab/gitlab.go +++ b/pkg/remote/builtin/gitlab/gitlab.go @@ -11,6 +11,7 @@ import ( "github.com/drone/drone/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client" "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" + "github.com/drone/drone/pkg/hash" "github.com/drone/drone/pkg/oauth2" "github.com/drone/drone/pkg/remote" common "github.com/drone/drone/pkg/types" @@ -26,6 +27,7 @@ type Gitlab struct { Client string Secret string AllowedOrgs []string + CloneMode string Open bool PrivateMode bool SkipVerify bool @@ -54,6 +56,13 @@ func NewDriver(config string) (remote.Remote, error) { gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify")) gitlab.Open, _ = strconv.ParseBool(params.Get("open")) + switch params.Get("clone_mode") { + case "oauth": + gitlab.CloneMode = "oauth" + default: + gitlab.CloneMode = "token" + } + // this is a temp workaround gitlab.Search, _ = strconv.ParseBool(params.Get("search")) @@ -64,8 +73,8 @@ func NewDriver(config string) (remote.Remote, error) { return &gitlab, err } -func (r *Gitlab) Login(token, secret string) (*common.User, error) { - client := NewClient(r.URL, token, r.SkipVerify) +func (g *Gitlab) Login(token, secret string) (*common.User, error) { + client := NewClient(g.URL, token, g.SkipVerify) var login, err = client.CurrentUser() if err != nil { return nil, err @@ -79,20 +88,20 @@ func (r *Gitlab) Login(token, secret string) (*common.User, error) { if strings.HasPrefix(login.AvatarUrl, "http") { user.Avatar = login.AvatarUrl } else { - user.Avatar = r.URL + "/" + login.AvatarUrl + user.Avatar = g.URL + "/" + login.AvatarUrl } return &user, nil } // Orgs fetches the organizations for the given user. -func (r *Gitlab) Orgs(u *common.User) ([]string, error) { +func (g *Gitlab) Orgs(u *common.User) ([]string, error) { return nil, nil } // Repo fetches the named repository from the remote system. -func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) { - client := NewClient(r.URL, u.Token, r.SkipVerify) - id, err := GetProjectId(r, client, owner, name) +func (g *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) { + client := NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, owner, name) if err != nil { return nil, err } @@ -113,7 +122,7 @@ func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) repo.Branch = repo_.DefaultBranch } - if r.PrivateMode { + if g.PrivateMode { repo.Private = true } else { repo.Private = !repo_.Public @@ -123,15 +132,15 @@ func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) } // Perm fetches the named repository from the remote system. -func (r *Gitlab) Perm(u *common.User, owner, name string) (*common.Perm, error) { +func (g *Gitlab) Perm(u *common.User, owner, name string) (*common.Perm, error) { key := fmt.Sprintf("%s/%s/%s", u.Login, owner, name) - val, ok := r.cache.Get(key) + val, ok := g.cache.Get(key) if ok { return val.(*common.Perm), nil } - client := NewClient(r.URL, u.Token, r.SkipVerify) - id, err := GetProjectId(r, client, owner, name) + client := NewClient(g.URL, u.Token, g.SkipVerify) + id, err := GetProjectId(g, client, owner, name) if err != nil { return nil, err } @@ -144,15 +153,15 @@ func (r *Gitlab) Perm(u *common.User, owner, name string) (*common.Perm, error) m.Admin = IsAdmin(repo) m.Pull = IsRead(repo) m.Push = IsWrite(repo) - r.cache.Add(key, m) + g.cache.Add(key, m) return m, nil } // GetScript fetches the build script (.drone.yml) from the remote // repository and returns in string format. -func (r *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, error) { - var client = NewClient(r.URL, user.Token, r.SkipVerify) - id, err := GetProjectId(r, client, repo.Owner, repo.Name) +func (g *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, error) { + var client = NewClient(g.URL, user.Token, g.SkipVerify) + id, err := GetProjectId(g, client, repo.Owner, repo.Name) if err != nil { return nil, err } @@ -163,29 +172,36 @@ func (r *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Buil // NOTE Currently gitlab doesn't support status for commits and events, // also if we want get MR status in gitlab we need implement a special plugin for gitlab, // gitlab uses API to fetch build status on client side. But for now we skip this. -func (r *Gitlab) Status(u *common.User, repo *common.Repo, b *common.Build) error { +func (g *Gitlab) Status(u *common.User, repo *common.Repo, b *common.Build) error { return nil } // Netrc returns a .netrc file that can be used to clone // private repositories from a remote system. -func (r *Gitlab) Netrc(u *common.User) (*common.Netrc, error) { - url_, err := url.Parse(r.URL) +func (g *Gitlab) Netrc(u *common.User, r *common.Repo) (*common.Netrc, error) { + url_, err := url.Parse(g.URL) if err != nil { return nil, err } netrc := &common.Netrc{} - netrc.Login = "oauth2" - netrc.Password = u.Token + + switch g.CloneMode { + case "oauth": + netrc.Login = "oauth2" + netrc.Password = u.Token + case "token": + netrc.Login = "drone-ci-token" + netrc.Password = hash.New(r.FullName, r.Hash) + } netrc.Machine = url_.Host return netrc, nil } // Activate activates a repository by adding a Post-commit hook and // a Public Deploy key, if applicable. -func (r *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypair, link string) error { - var client = NewClient(r.URL, user.Token, r.SkipVerify) - id, err := GetProjectId(r, client, repo.Owner, repo.Name) +func (g *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypair, link string) error { + var client = NewClient(g.URL, user.Token, g.SkipVerify) + id, err := GetProjectId(g, client, repo.Owner, repo.Name) if err != nil { return err } @@ -197,7 +213,7 @@ func (r *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypai droneUrl := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host) droneToken := uri.Query().Get("access_token") - ssl_verify := strconv.FormatBool(!r.SkipVerify) + ssl_verify := strconv.FormatBool(!g.SkipVerify) return client.AddDroneService(id, map[string]string{ "token": droneToken, @@ -208,9 +224,9 @@ func (r *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypai // Deactivate removes a repository by removing all the post-commit hooks // which are equal to link and removing the SSH deploy key. -func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) error { - var client = NewClient(r.URL, user.Token, r.SkipVerify) - id, err := GetProjectId(r, client, repo.Owner, repo.Name) +func (g *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) error { + var client = NewClient(g.URL, user.Token, g.SkipVerify) + id, err := GetProjectId(g, client, repo.Owner, repo.Name) if err != nil { return err } @@ -220,7 +236,7 @@ func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) e // ParseHook parses the post-commit hook from the Request body // and returns the required data in a standard format. -func (r *Gitlab) Hook(req *http.Request) (*common.Hook, error) { +func (g *Gitlab) Hook(req *http.Request) (*common.Hook, error) { defer req.Body.Close() var payload, _ = ioutil.ReadAll(req.Body) var parsed, err = gogitlab.ParseHook(payload) @@ -341,16 +357,16 @@ func (g *Gitlab) Oauth2Transport(r *http.Request) *oauth2.Transport { } // Accessor method, to allowed remote organizations field. -func (r *Gitlab) GetOrgs() []string { - return r.AllowedOrgs +func (g *Gitlab) GetOrgs() []string { + return g.AllowedOrgs } // Accessor method, to open field. -func (r *Gitlab) GetOpen() bool { - return r.Open +func (g *Gitlab) GetOpen() bool { + return g.Open } // return default scope for GitHub -func (r *Gitlab) Scope() string { +func (g *Gitlab) Scope() string { return DefaultScope } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 7453a7856..9d6367414 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -64,7 +64,7 @@ type Remote interface { // Netrc returns a .netrc file that can be used to clone // private repositories from a remote system. - Netrc(u *types.User) (*types.Netrc, error) + Netrc(u *types.User, r *types.Repo) (*types.Netrc, error) // Activate activates a repository by creating the post-commit hook and // adding the SSH deploy key, if applicable. diff --git a/pkg/server/commits.go b/pkg/server/commits.go index 3c75faad5..c2a97eb0c 100644 --- a/pkg/server/commits.go +++ b/pkg/server/commits.go @@ -166,7 +166,7 @@ func RunBuild(c *gin.Context) { return } - netrc, err := remote.Netrc(user) + netrc, err := remote.Netrc(user, repo) if err != nil { c.Fail(500, err) return diff --git a/pkg/server/gitlab.go b/pkg/server/gitlab.go index 69a4caf1f..adbf53b98 100644 --- a/pkg/server/gitlab.go +++ b/pkg/server/gitlab.go @@ -5,6 +5,8 @@ import ( "strconv" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" + + "github.com/drone/drone/pkg/hash" ) // RedirectSha accepts a request to retvie a redirect @@ -78,7 +80,7 @@ func GetPullRequest(c *gin.Context) { repo := ToRepo(c) // get the token and verify the hook is authorized - if c.Request.FormValue("access_token") != hash(repo.FullName, repo.Hash) { + if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) { c.AbortWithStatus(403) return } @@ -117,7 +119,7 @@ func GetCommit(c *gin.Context) { sha := c.Params.ByName("sha") // get the token and verify the hook is authorized - if c.Request.FormValue("access_token") != hash(repo.FullName, repo.Hash) { + if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) { c.AbortWithStatus(403) return } diff --git a/pkg/server/hooks.go b/pkg/server/hooks.go index 134621226..d54caa1db 100644 --- a/pkg/server/hooks.go +++ b/pkg/server/hooks.go @@ -5,6 +5,7 @@ import ( log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" + "github.com/drone/drone/pkg/hash" "github.com/drone/drone/pkg/queue" common "github.com/drone/drone/pkg/types" "github.com/drone/drone/pkg/yaml" @@ -56,7 +57,7 @@ func PostHook(c *gin.Context) { } // get the token and verify the hook is authorized - if c.Request.FormValue("access_token") != hash(repo.FullName, repo.Hash) { + if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) { log.Errorf("invalid token sent with hook.") c.AbortWithStatus(403) return @@ -128,7 +129,7 @@ func PostHook(c *gin.Context) { }) } - netrc, err := remote.Netrc(user) + netrc, err := remote.Netrc(user, repo) if err != nil { log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err) c.Fail(500, err) diff --git a/pkg/server/repos.go b/pkg/server/repos.go index 8e7809160..09933faaa 100644 --- a/pkg/server/repos.go +++ b/pkg/server/repos.go @@ -1,14 +1,13 @@ package server import ( - "crypto/sha256" - "encoding/hex" "encoding/json" "fmt" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding" + "github.com/drone/drone/pkg/hash" "github.com/drone/drone/pkg/remote" common "github.com/drone/drone/pkg/types" "github.com/drone/drone/pkg/utils/httputil" @@ -209,7 +208,7 @@ func PostRepo(c *gin.Context) { link := fmt.Sprintf( "%s/api/hook?access_token=%s", httputil.GetURL(c.Request), - hash(r.FullName, r.Hash), + hash.New(r.FullName, r.Hash), ) // generate an RSA key and add to the repo @@ -316,9 +315,3 @@ func perms(remote remote.Remote, u *common.User, r *common.Repo) *common.Perm { } return p } - -func hash(text, salt string) string { - hasher := sha256.New() - hasher.Write([]byte(text + salt)) - return hex.EncodeToString(hasher.Sum(nil)) -}