Added clone mode for gitlab, use token mode as default

This commit is contained in:
Kirilll Zaitsev 2015-09-02 06:42:18 +03:00
parent cc86f525df
commit e2cdde02bc
9 changed files with 75 additions and 50 deletions

View file

@ -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. * `open=false` allows users to self-register. Defaults to false for security reasons.
* `orgs=drone,docker` restricts access to these GitLab organizations. **Optional** * `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. * `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 ## Gitlab registration

12
pkg/hash/hash.go Normal file
View file

@ -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))
}

View file

@ -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 // Netrc returns a .netrc file that can be used to clone
// private repositories from a remote system. // 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) url_, err := url.Parse(g.URL)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -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/Bugagazavr/go-gitlab-client"
"github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "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/oauth2"
"github.com/drone/drone/pkg/remote" "github.com/drone/drone/pkg/remote"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
@ -26,6 +27,7 @@ type Gitlab struct {
Client string Client string
Secret string Secret string
AllowedOrgs []string AllowedOrgs []string
CloneMode string
Open bool Open bool
PrivateMode bool PrivateMode bool
SkipVerify bool SkipVerify bool
@ -54,6 +56,13 @@ func NewDriver(config string) (remote.Remote, error) {
gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify")) gitlab.SkipVerify, _ = strconv.ParseBool(params.Get("skip_verify"))
gitlab.Open, _ = strconv.ParseBool(params.Get("open")) 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 // this is a temp workaround
gitlab.Search, _ = strconv.ParseBool(params.Get("search")) gitlab.Search, _ = strconv.ParseBool(params.Get("search"))
@ -64,8 +73,8 @@ func NewDriver(config string) (remote.Remote, error) {
return &gitlab, err return &gitlab, err
} }
func (r *Gitlab) Login(token, secret string) (*common.User, error) { func (g *Gitlab) Login(token, secret string) (*common.User, error) {
client := NewClient(r.URL, token, r.SkipVerify) client := NewClient(g.URL, token, g.SkipVerify)
var login, err = client.CurrentUser() var login, err = client.CurrentUser()
if err != nil { if err != nil {
return nil, err return nil, err
@ -79,20 +88,20 @@ func (r *Gitlab) Login(token, secret string) (*common.User, error) {
if strings.HasPrefix(login.AvatarUrl, "http") { if strings.HasPrefix(login.AvatarUrl, "http") {
user.Avatar = login.AvatarUrl user.Avatar = login.AvatarUrl
} else { } else {
user.Avatar = r.URL + "/" + login.AvatarUrl user.Avatar = g.URL + "/" + login.AvatarUrl
} }
return &user, nil return &user, nil
} }
// Orgs fetches the organizations for the given user. // 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 return nil, nil
} }
// Repo fetches the named repository from the remote system. // Repo fetches the named repository from the remote system.
func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) { func (g *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error) {
client := NewClient(r.URL, u.Token, r.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(r, client, owner, name) id, err := GetProjectId(g, client, owner, name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -113,7 +122,7 @@ func (r *Gitlab) Repo(u *common.User, owner, name string) (*common.Repo, error)
repo.Branch = repo_.DefaultBranch repo.Branch = repo_.DefaultBranch
} }
if r.PrivateMode { if g.PrivateMode {
repo.Private = true repo.Private = true
} else { } else {
repo.Private = !repo_.Public 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. // 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) key := fmt.Sprintf("%s/%s/%s", u.Login, owner, name)
val, ok := r.cache.Get(key) val, ok := g.cache.Get(key)
if ok { if ok {
return val.(*common.Perm), nil return val.(*common.Perm), nil
} }
client := NewClient(r.URL, u.Token, r.SkipVerify) client := NewClient(g.URL, u.Token, g.SkipVerify)
id, err := GetProjectId(r, client, owner, name) id, err := GetProjectId(g, client, owner, name)
if err != nil { if err != nil {
return nil, err 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.Admin = IsAdmin(repo)
m.Pull = IsRead(repo) m.Pull = IsRead(repo)
m.Push = IsWrite(repo) m.Push = IsWrite(repo)
r.cache.Add(key, m) g.cache.Add(key, m)
return m, nil return m, nil
} }
// GetScript fetches the build script (.drone.yml) from the remote // GetScript fetches the build script (.drone.yml) from the remote
// repository and returns in string format. // repository and returns in string format.
func (r *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, error) { func (g *Gitlab) Script(user *common.User, repo *common.Repo, build *common.Build) ([]byte, error) {
var client = NewClient(r.URL, user.Token, r.SkipVerify) var client = NewClient(g.URL, user.Token, g.SkipVerify)
id, err := GetProjectId(r, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, repo.Owner, repo.Name)
if err != nil { if err != nil {
return nil, err 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, // 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, // 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. // 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 return nil
} }
// 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 (r *Gitlab) Netrc(u *common.User) (*common.Netrc, error) { func (g *Gitlab) Netrc(u *common.User, r *common.Repo) (*common.Netrc, error) {
url_, err := url.Parse(r.URL) url_, err := url.Parse(g.URL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
netrc := &common.Netrc{} 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 netrc.Machine = url_.Host
return netrc, nil return netrc, nil
} }
// Activate activates a repository by adding a Post-commit hook and // Activate activates a repository by adding a Post-commit hook and
// a Public Deploy key, if applicable. // a Public Deploy key, if applicable.
func (r *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypair, link string) error { func (g *Gitlab) Activate(user *common.User, repo *common.Repo, k *common.Keypair, link string) error {
var client = NewClient(r.URL, user.Token, r.SkipVerify) var client = NewClient(g.URL, user.Token, g.SkipVerify)
id, err := GetProjectId(r, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, repo.Owner, repo.Name)
if err != nil { if err != nil {
return err 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) droneUrl := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host)
droneToken := uri.Query().Get("access_token") 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{ return client.AddDroneService(id, map[string]string{
"token": droneToken, "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 // Deactivate removes a repository by removing all the post-commit hooks
// which are equal to link and removing the SSH deploy key. // which are equal to link and removing the SSH deploy key.
func (r *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) error { func (g *Gitlab) Deactivate(user *common.User, repo *common.Repo, link string) error {
var client = NewClient(r.URL, user.Token, r.SkipVerify) var client = NewClient(g.URL, user.Token, g.SkipVerify)
id, err := GetProjectId(r, client, repo.Owner, repo.Name) id, err := GetProjectId(g, client, repo.Owner, repo.Name)
if err != nil { if err != nil {
return err 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 // ParseHook parses the post-commit hook from the Request body
// and returns the required data in a standard format. // 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() defer req.Body.Close()
var payload, _ = ioutil.ReadAll(req.Body) var payload, _ = ioutil.ReadAll(req.Body)
var parsed, err = gogitlab.ParseHook(payload) 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. // Accessor method, to allowed remote organizations field.
func (r *Gitlab) GetOrgs() []string { func (g *Gitlab) GetOrgs() []string {
return r.AllowedOrgs return g.AllowedOrgs
} }
// Accessor method, to open field. // Accessor method, to open field.
func (r *Gitlab) GetOpen() bool { func (g *Gitlab) GetOpen() bool {
return r.Open return g.Open
} }
// return default scope for GitHub // return default scope for GitHub
func (r *Gitlab) Scope() string { func (g *Gitlab) Scope() string {
return DefaultScope return DefaultScope
} }

View file

@ -64,7 +64,7 @@ type Remote interface {
// 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.
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 // Activate activates a repository by creating the post-commit hook and
// adding the SSH deploy key, if applicable. // adding the SSH deploy key, if applicable.

View file

@ -166,7 +166,7 @@ func RunBuild(c *gin.Context) {
return return
} }
netrc, err := remote.Netrc(user) netrc, err := remote.Netrc(user, repo)
if err != nil { if err != nil {
c.Fail(500, err) c.Fail(500, err)
return return

View file

@ -5,6 +5,8 @@ import (
"strconv" "strconv"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "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 // RedirectSha accepts a request to retvie a redirect
@ -78,7 +80,7 @@ func GetPullRequest(c *gin.Context) {
repo := ToRepo(c) repo := ToRepo(c)
// get the token and verify the hook is authorized // 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) c.AbortWithStatus(403)
return return
} }
@ -117,7 +119,7 @@ func GetCommit(c *gin.Context) {
sha := c.Params.ByName("sha") sha := c.Params.ByName("sha")
// get the token and verify the hook is authorized // 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) c.AbortWithStatus(403)
return return
} }

View file

@ -5,6 +5,7 @@ import (
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" 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/Godeps/_workspace/src/github.com/gin-gonic/gin"
"github.com/drone/drone/pkg/hash"
"github.com/drone/drone/pkg/queue" "github.com/drone/drone/pkg/queue"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/pkg/yaml" "github.com/drone/drone/pkg/yaml"
@ -56,7 +57,7 @@ func PostHook(c *gin.Context) {
} }
// get the token and verify the hook is authorized // 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.") log.Errorf("invalid token sent with hook.")
c.AbortWithStatus(403) c.AbortWithStatus(403)
return return
@ -128,7 +129,7 @@ func PostHook(c *gin.Context) {
}) })
} }
netrc, err := remote.Netrc(user) netrc, err := remote.Netrc(user, repo)
if err != nil { if err != nil {
log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err) log.Errorf("failure to generate netrc for %s. %s", repo.FullName, err)
c.Fail(500, err) c.Fail(500, err)

View file

@ -1,14 +1,13 @@
package server package server
import ( import (
"crypto/sha256"
"encoding/hex"
"encoding/json" "encoding/json"
"fmt" "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"
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding" "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" "github.com/drone/drone/pkg/remote"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/pkg/utils/httputil" "github.com/drone/drone/pkg/utils/httputil"
@ -209,7 +208,7 @@ func PostRepo(c *gin.Context) {
link := fmt.Sprintf( link := fmt.Sprintf(
"%s/api/hook?access_token=%s", "%s/api/hook?access_token=%s",
httputil.GetURL(c.Request), httputil.GetURL(c.Request),
hash(r.FullName, r.Hash), hash.New(r.FullName, r.Hash),
) )
// generate an RSA key and add to the repo // 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 return p
} }
func hash(text, salt string) string {
hasher := sha256.New()
hasher.Write([]byte(text + salt))
return hex.EncodeToString(hasher.Sum(nil))
}