Start working on specs implementation

This commit is contained in:
Kirill Zaitsev 2015-07-23 20:36:23 +03:00
parent 2939e1356d
commit 2966d294fe
6 changed files with 125 additions and 63 deletions

View file

@ -9,7 +9,7 @@ import (
"github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs" "github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs"
"github.com/drone/drone/pkg/config" "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"
"github.com/drone/drone/pkg/server/session" "github.com/drone/drone/pkg/server/session"
@ -49,7 +49,10 @@ func main() {
panic(err) panic(err)
} }
remote := github.New(settings) remote, err := remote.New(settings)
if err != nil {
panic(err)
}
session := session.New(settings) session := session.New(settings)
eventbus_ := eventbus.New() eventbus_ := eventbus.New()
queue_ := queue.New() queue_ := queue.New()

View file

@ -1,7 +1,11 @@
package config package config
import ( import (
"fmt"
"io/ioutil" "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/naoina/toml"
"github.com/drone/drone/Godeps/_workspace/src/github.com/vrischmann/envconfig" "github.com/drone/drone/Godeps/_workspace/src/github.com/vrischmann/envconfig"
@ -9,12 +13,7 @@ import (
type Config struct { type Config struct {
Remote struct { Remote struct {
Kind string `envconfig:"optional"` Driver string `envconfig:"optional"`
Base string `envconfig:"optional"`
Orgs []string `envconfig:"optional"`
Open bool `envconfig:"optional"`
Private bool `envconfig:"optional"`
SkipVerify bool `envconfig:"optional"`
} }
Auth struct { Auth struct {
@ -67,20 +66,14 @@ type Config struct {
Plugins []string `envconfig:"optional"` Plugins []string `envconfig:"optional"`
Github struct { Github struct {
Client string `envconfig:"optional"` API string `envconfig:"optional"`
Secret string `envconfig:"optional"` Host string `envconfig:"optional"`
Orgs []string `envconfig:"optional"` Client string `envconfig:"optional"`
Open bool `envconfig:"optional"` Secret string `envconfig:"optional"`
} PrivateMode bool `envconfig:"optional"`
SkipVerify bool `envconfig:"optional"`
GithubEnterprise struct { Open bool `envconfig:"optional"`
URL string `envconfig:"optional"` Orgs []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"`
} }
Bitbucket struct { Bitbucket struct {
@ -132,5 +125,29 @@ func applyDefaults(c *Config) *Config {
if len(c.Session.Secret) == 0 { if len(c.Session.Secret) == 0 {
c.Session.Secret = c.Auth.Secret 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 return c
} }

View file

@ -1,6 +1,7 @@
package github package github
import ( import (
"crypto/tls"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
@ -10,14 +11,14 @@ import (
"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/config" "github.com/drone/drone/pkg/config"
"github.com/drone/drone/pkg/oauth2"
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/Godeps/_workspace/src/github.com/google/go-github/github" "github.com/drone/drone/Godeps/_workspace/src/github.com/google/go-github/github"
) )
const ( const (
DefaultAPI = "https://api.github.com/"
DefaultURL = "https://github.com"
DefaultScope = "repo,repo:status,user:email" DefaultScope = "repo,repo:status,user:email"
) )
@ -26,6 +27,8 @@ type GitHub struct {
API string API string
Client string Client string
Secret string Secret string
AllowedOrgs []string
Open bool
PrivateMode bool PrivateMode bool
SkipVerify bool SkipVerify bool
@ -34,12 +37,14 @@ type GitHub struct {
func New(conf *config.Config) *GitHub { func New(conf *config.Config) *GitHub {
var github = GitHub{ var github = GitHub{
API: DefaultAPI, API: conf.Github.API,
URL: DefaultURL, URL: conf.Github.Host,
Client: conf.Auth.Client, Client: conf.Github.Client,
Secret: conf.Auth.Secret, Secret: conf.Github.Secret,
PrivateMode: conf.Remote.Private, AllowedOrgs: conf.Github.Orgs,
SkipVerify: conf.Remote.SkipVerify, Open: conf.Github.Open,
PrivateMode: conf.Github.PrivateMode,
SkipVerify: conf.Github.SkipVerify,
} }
var err error var err error
github.cache, err = lru.New(1028) github.cache, err = lru.New(1028)
@ -47,12 +52,6 @@ func New(conf *config.Config) *GitHub {
panic(err) 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 // the API must have a trailing slash
if !strings.HasSuffix(github.API, "/") { if !strings.HasSuffix(github.API, "/") {
github.API += "/" github.API += "/"
@ -66,7 +65,7 @@ func New(conf *config.Config) *GitHub {
func (g *GitHub) Login(token, secret string) (*common.User, error) { func (g *GitHub) Login(token, secret string) (*common.User, error) {
client := NewClient(g.API, token, g.SkipVerify) client := NewClient(g.API, token, g.SkipVerify)
login, err := GetUserEmail(client) login, err := GetUserEmail(client, g.URL)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -92,6 +91,16 @@ func (g *GitHub) Orgs(u *common.User) ([]string, error) {
return orgs_, nil 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. // Repo fetches the named repository from the remote system.
func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error) { func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error) {
client := NewClient(g.API, u.Token, g.SkipVerify) 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 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` // pullRequest parses a hook with event type `pullRequest`
// and returns the commit data. // and returns the commit data.
func (g *GitHub) pullRequest(r *http.Request) (*common.Hook, error) { func (g *GitHub) pullRequest(r *http.Request) (*common.Hook, error) {

View file

@ -37,7 +37,7 @@ func NewClient(uri, token string, skipVerify bool) *github.Client {
// GetUserEmail is a heper function that retrieves the currently // GetUserEmail is a heper function that retrieves the currently
// authenticated user from GitHub + Email address. // 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("") user, _, err := client.Users.Get("")
if err != nil { if err != nil {
return nil, err return nil, err
@ -58,7 +58,7 @@ func GetUserEmail(client *github.Client) (*github.User, error) {
// WARNING, HACK // WARNING, HACK
// for out-of-date github enterprise editions the primary // for out-of-date github enterprise editions the primary
// and verified fields won't exist. // 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 user.Email = emails[0].Email
return user, nil return user, nil
} }

View file

@ -1,8 +1,14 @@
package remote package remote
import ( import (
"errors"
"fmt"
"net/http" "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" common "github.com/drone/drone/pkg/types"
) )
@ -45,6 +51,27 @@ type Remote interface {
// and returns the required data in a standard format. // and returns the required data in a standard format.
Hook(r *http.Request) (*common.Hook, error) 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 // Default scope for remote
Scope() string 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))
}
}

View file

@ -1,17 +1,13 @@
package server package server
import ( import (
"fmt"
"strings"
"time" "time"
"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/ungerik/go-gravatar" "github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar"
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/pkg/oauth2"
common "github.com/drone/drone/pkg/types" common "github.com/drone/drone/pkg/types"
"github.com/drone/drone/pkg/utils/httputil"
) )
// GetLogin accepts a request to authorize the user and to // GetLogin accepts a request to authorize the user and to
@ -52,9 +48,9 @@ func GetLogin(c *gin.Context) {
login := ToUser(c) login := ToUser(c)
// check organization membership, if applicable // check organization membership, if applicable
if len(settings.Remote.Orgs) != 0 { if len(remote.GetOrgs()) != 0 {
orgs, _ := remote.Orgs(login) orgs, _ := remote.Orgs(login)
if !checkMembership(orgs, settings.Remote.Orgs) { if !checkMembership(orgs, remote.GetOrgs()) {
c.Redirect(303, "/login#error=access_denied_org") c.Redirect(303, "/login#error=access_denied_org")
return return
} }
@ -73,7 +69,7 @@ func GetLogin(c *gin.Context) {
// if self-registration is disabled we should // if self-registration is disabled we should
// return a notAuthorized error. the only exception // return a notAuthorized error. the only exception
// is if no users exist yet in the system we'll proceed. // 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) log.Errorf("cannot register %s. registration closed", login.Login)
c.Redirect(303, "/login#error=access_denied") c.Redirect(303, "/login#error=access_denied")
return return
@ -131,35 +127,26 @@ func GetLogin(c *gin.Context) {
// getLoginOauth2 is the default authorization implementation // getLoginOauth2 is the default authorization implementation
// using the oauth2 protocol. // using the oauth2 protocol.
func getLoginOauth2(c *gin.Context) { func getLoginOauth2(c *gin.Context) {
var settings = ToSettings(c)
var remote = ToRemote(c) var remote = ToRemote(c)
var scope = strings.Join(settings.Auth.Scope, ",") // Bugagazavr: I think this must be moved to remote config
if scope == "" { //var scope = strings.Join(settings.Auth.Scope, ",")
scope = remote.Scope() //if scope == "" {
} // scope = remote.Scope()
var config = &oauth2.Config{ //}
ClientId: settings.Auth.Client, var transport = remote.Oauth2Transport(c.Request)
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),
}
// get the OAuth code // get the OAuth code
var code = c.Request.FormValue("code") var code = c.Request.FormValue("code")
//var state = c.Request.FormValue("state") //var state = c.Request.FormValue("state")
if len(code) == 0 { if len(code) == 0 {
// TODO this should be a random number, verified by a cookie // TODO this should be a random number, verified by a cookie
c.Redirect(303, config.AuthCodeURL("random")) c.Redirect(303, transport.AuthCodeURL("random"))
return return
} }
// exhange for a token // exhange for a token
var trans = &oauth2.Transport{Config: config} var token, err = transport.Exchange(code)
var token, err = trans.Exchange(code)
if err != nil { if err != nil {
log.Errorf("cannot get access_token. %s", err) log.Errorf("cannot get access_token. %s", err)
c.Redirect(303, "/login#error=token_exchange") c.Redirect(303, "/login#error=token_exchange")