diff --git a/cmd/drone-server/drone.go b/cmd/drone-server/drone.go index 9f34453d5..6377e12a3 100644 --- a/cmd/drone-server/drone.go +++ b/cmd/drone-server/drone.go @@ -9,7 +9,7 @@ import ( "github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs" "github.com/drone/drone/pkg/config" - "github.com/drone/drone/pkg/remote/builtin/github" + "github.com/drone/drone/pkg/remote" "github.com/drone/drone/pkg/server" "github.com/drone/drone/pkg/server/session" @@ -49,7 +49,10 @@ func main() { panic(err) } - remote := github.New(settings) + remote, err := remote.New(settings) + if err != nil { + panic(err) + } session := session.New(settings) eventbus_ := eventbus.New() queue_ := queue.New() diff --git a/pkg/config/config.go b/pkg/config/config.go index c0367cc63..f227add10 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,7 +1,11 @@ package config import ( + "fmt" "io/ioutil" + "os" + "path" + "strings" "github.com/drone/drone/Godeps/_workspace/src/github.com/naoina/toml" "github.com/drone/drone/Godeps/_workspace/src/github.com/vrischmann/envconfig" @@ -9,12 +13,7 @@ import ( type Config struct { Remote struct { - Kind string `envconfig:"optional"` - Base string `envconfig:"optional"` - Orgs []string `envconfig:"optional"` - Open bool `envconfig:"optional"` - Private bool `envconfig:"optional"` - SkipVerify bool `envconfig:"optional"` + Driver string `envconfig:"optional"` } Auth struct { @@ -67,20 +66,14 @@ type Config struct { Plugins []string `envconfig:"optional"` Github struct { - Client string `envconfig:"optional"` - Secret string `envconfig:"optional"` - Orgs []string `envconfig:"optional"` - Open bool `envconfig:"optional"` - } - - GithubEnterprise struct { - URL string `envconfig:"optional"` - Client string `envconfig:"optional"` - Secret string `envconfig:"optional"` - Private bool `envconfig:"optional"` - SkipVerify bool `envconfig:"optional"` - Open bool `envconfig:"optional"` - Orgs []string `envconfig:"optional"` + API string `envconfig:"optional"` + Host string `envconfig:"optional"` + Client string `envconfig:"optional"` + Secret string `envconfig:"optional"` + PrivateMode bool `envconfig:"optional"` + SkipVerify bool `envconfig:"optional"` + Open bool `envconfig:"optional"` + Orgs []string `envconfig:"optional"` } Bitbucket struct { @@ -132,5 +125,29 @@ func applyDefaults(c *Config) *Config { if len(c.Session.Secret) == 0 { c.Session.Secret = c.Auth.Secret } + + // Prevent crash on start, use sqlite3 + // driver as default if DRONE_DATABASE_DRIVER and + // DRONE_DATABASE_DATASOURCE not specifed + if len(c.Database.Driver) == 0 && len(c.Database.Datasource) == 0 { + c.Database.Driver = "sqlite3" + + pwd, err := os.Getwd() + if err != nil { + panic(err) + } + + c.Database.Datasource = path.Join(pwd, "drone.sqlite3") + } + + // Set default settings for remotes + switch strings.ToLower(c.Remote.Driver) { + case "github": + if len(c.Github.API) == 0 && len(c.Github.Host) == 0 { + c.Github.API = "https://api.github.com/" + c.Github.Host = "https://github.com" + } + } + return c } diff --git a/pkg/remote/builtin/github/github.go b/pkg/remote/builtin/github/github.go index 1f50bf135..1deec0577 100644 --- a/pkg/remote/builtin/github/github.go +++ b/pkg/remote/builtin/github/github.go @@ -1,6 +1,7 @@ package github import ( + "crypto/tls" "encoding/json" "fmt" "net/http" @@ -10,14 +11,14 @@ import ( "github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" "github.com/drone/drone/pkg/config" + "github.com/drone/drone/pkg/oauth2" common "github.com/drone/drone/pkg/types" + "github.com/drone/drone/pkg/utils/httputil" "github.com/drone/drone/Godeps/_workspace/src/github.com/google/go-github/github" ) const ( - DefaultAPI = "https://api.github.com/" - DefaultURL = "https://github.com" DefaultScope = "repo,repo:status,user:email" ) @@ -26,6 +27,8 @@ type GitHub struct { API string Client string Secret string + AllowedOrgs []string + Open bool PrivateMode bool SkipVerify bool @@ -34,12 +37,14 @@ type GitHub struct { func New(conf *config.Config) *GitHub { var github = GitHub{ - API: DefaultAPI, - URL: DefaultURL, - Client: conf.Auth.Client, - Secret: conf.Auth.Secret, - PrivateMode: conf.Remote.Private, - SkipVerify: conf.Remote.SkipVerify, + API: conf.Github.API, + URL: conf.Github.Host, + Client: conf.Github.Client, + Secret: conf.Github.Secret, + AllowedOrgs: conf.Github.Orgs, + Open: conf.Github.Open, + PrivateMode: conf.Github.PrivateMode, + SkipVerify: conf.Github.SkipVerify, } var err error github.cache, err = lru.New(1028) @@ -47,12 +52,6 @@ func New(conf *config.Config) *GitHub { panic(err) } - // if GitHub enterprise then ensure we're using the - // appropriate URLs - if !strings.HasPrefix(conf.Remote.Base, DefaultURL) && len(conf.Remote.Base) != 0 { - github.URL = conf.Remote.Base - github.API = conf.Remote.Base + "/api/v3/" - } // the API must have a trailing slash if !strings.HasSuffix(github.API, "/") { github.API += "/" @@ -66,7 +65,7 @@ func New(conf *config.Config) *GitHub { func (g *GitHub) Login(token, secret string) (*common.User, error) { client := NewClient(g.API, token, g.SkipVerify) - login, err := GetUserEmail(client) + login, err := GetUserEmail(client, g.URL) if err != nil { return nil, err } @@ -92,6 +91,16 @@ func (g *GitHub) Orgs(u *common.User) ([]string, error) { return orgs_, nil } +// Accessor method, to allowed remote organizations field. +func (g *GitHub) GetOrgs() []string { + return g.AllowedOrgs +} + +// Accessor method, to open field. +func (g *GitHub) GetOpen() bool { + return g.Open +} + // Repo fetches the named repository from the remote system. func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error) { client := NewClient(g.API, u.Token, g.SkipVerify) @@ -281,6 +290,25 @@ func (g *GitHub) push(r *http.Request) (*common.Hook, error) { return &common.Hook{Repo: repo, Commit: commit}, nil } +// ¯\_(ツ)_/¯ +func (g *GitHub) Oauth2Transport(r *http.Request) *oauth2.Transport { + return &oauth2.Transport{ + Config: &oauth2.Config{ + ClientId: g.Client, + ClientSecret: g.Secret, + Scope: DefaultScope, + AuthURL: fmt.Sprintf("%s/login/oauth/authorize", g.URL), + TokenURL: fmt.Sprintf("%s/login/oauth/access_token", g.URL), + RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(r)), + //settings.Server.Scheme, settings.Server.Hostname), + }, + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSClientConfig: &tls.Config{InsecureSkipVerify: g.SkipVerify}, + }, + } +} + // pullRequest parses a hook with event type `pullRequest` // and returns the commit data. func (g *GitHub) pullRequest(r *http.Request) (*common.Hook, error) { diff --git a/pkg/remote/builtin/github/helper.go b/pkg/remote/builtin/github/helper.go index bedd39ceb..f54c19d6b 100644 --- a/pkg/remote/builtin/github/helper.go +++ b/pkg/remote/builtin/github/helper.go @@ -37,7 +37,7 @@ func NewClient(uri, token string, skipVerify bool) *github.Client { // GetUserEmail is a heper function that retrieves the currently // authenticated user from GitHub + Email address. -func GetUserEmail(client *github.Client) (*github.User, error) { +func GetUserEmail(client *github.Client, defaultURL string) (*github.User, error) { user, _, err := client.Users.Get("") if err != nil { return nil, err @@ -58,7 +58,7 @@ func GetUserEmail(client *github.Client) (*github.User, error) { // WARNING, HACK // for out-of-date github enterprise editions the primary // and verified fields won't exist. - if !strings.HasPrefix(*user.HTMLURL, DefaultURL) && len(emails) != 0 { + if !strings.HasPrefix(*user.HTMLURL, defaultURL) && len(emails) != 0 { user.Email = emails[0].Email return user, nil } diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index 8f95572ae..fe4486aeb 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -1,8 +1,14 @@ package remote import ( + "errors" + "fmt" "net/http" + "strings" + "github.com/drone/drone/pkg/config" + "github.com/drone/drone/pkg/oauth2" + "github.com/drone/drone/pkg/remote/builtin/github" common "github.com/drone/drone/pkg/types" ) @@ -45,6 +51,27 @@ type Remote interface { // and returns the required data in a standard format. Hook(r *http.Request) (*common.Hook, error) + // Oauth2Transport + Oauth2Transport(r *http.Request) *oauth2.Transport + + // GetOrgs returns all allowed organizations for remote. + GetOrgs() []string + + // GetOpen returns boolean field with enabled or disabled + // registration. + GetOpen() bool + // Default scope for remote Scope() string } + +func New(conf *config.Config) (Remote, error) { + switch strings.ToLower(conf.Remote.Driver) { + case "github": + return github.New(conf), nil + case "": + return nil, errors.New("Remote not specifed, please set env variable DRONE_REMOTE_DRIVER") + default: + return nil, errors.New(fmt.Sprintf("Remote driver not supported: DRONE_REMOTE_DRIVER=%s", conf.Remote.Driver)) + } +} diff --git a/pkg/server/login.go b/pkg/server/login.go index 5e73129e4..366ed5605 100644 --- a/pkg/server/login.go +++ b/pkg/server/login.go @@ -1,17 +1,13 @@ package server import ( - "fmt" - "strings" "time" "github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" "github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar" log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" - "github.com/drone/drone/pkg/oauth2" common "github.com/drone/drone/pkg/types" - "github.com/drone/drone/pkg/utils/httputil" ) // GetLogin accepts a request to authorize the user and to @@ -52,9 +48,9 @@ func GetLogin(c *gin.Context) { login := ToUser(c) // check organization membership, if applicable - if len(settings.Remote.Orgs) != 0 { + if len(remote.GetOrgs()) != 0 { orgs, _ := remote.Orgs(login) - if !checkMembership(orgs, settings.Remote.Orgs) { + if !checkMembership(orgs, remote.GetOrgs()) { c.Redirect(303, "/login#error=access_denied_org") return } @@ -73,7 +69,7 @@ func GetLogin(c *gin.Context) { // if self-registration is disabled we should // return a notAuthorized error. the only exception // is if no users exist yet in the system we'll proceed. - if !settings.Remote.Open && count != 0 { + if !remote.GetOpen() && count != 0 { log.Errorf("cannot register %s. registration closed", login.Login) c.Redirect(303, "/login#error=access_denied") return @@ -131,35 +127,26 @@ func GetLogin(c *gin.Context) { // getLoginOauth2 is the default authorization implementation // using the oauth2 protocol. func getLoginOauth2(c *gin.Context) { - var settings = ToSettings(c) var remote = ToRemote(c) - var scope = strings.Join(settings.Auth.Scope, ",") - if scope == "" { - scope = remote.Scope() - } - var config = &oauth2.Config{ - ClientId: settings.Auth.Client, - ClientSecret: settings.Auth.Secret, - Scope: scope, - AuthURL: settings.Auth.Authorize, - TokenURL: settings.Auth.AccessToken, - RedirectURL: fmt.Sprintf("%s/authorize", httputil.GetURL(c.Request)), - //settings.Server.Scheme, settings.Server.Hostname), - } + // Bugagazavr: I think this must be moved to remote config + //var scope = strings.Join(settings.Auth.Scope, ",") + //if scope == "" { + // scope = remote.Scope() + //} + var transport = remote.Oauth2Transport(c.Request) // get the OAuth code var code = c.Request.FormValue("code") //var state = c.Request.FormValue("state") if len(code) == 0 { // TODO this should be a random number, verified by a cookie - c.Redirect(303, config.AuthCodeURL("random")) + c.Redirect(303, transport.AuthCodeURL("random")) return } // exhange for a token - var trans = &oauth2.Transport{Config: config} - var token, err = trans.Exchange(code) + var token, err = transport.Exchange(code) if err != nil { log.Errorf("cannot get access_token. %s", err) c.Redirect(303, "/login#error=token_exchange")