diff --git a/common/token.go b/common/token.go index 85b0967be..482f01ad1 100644 --- a/common/token.go +++ b/common/token.go @@ -1,8 +1,10 @@ package common const ( - TokenUser = "u" - TokenSess = "s" + TokenUser = "u" + TokenSess = "s" + TokenHook = "h" + TokenAgent = "a" ) type Token struct { diff --git a/datastore/bolt/util.go b/datastore/bolt/util.go index 7affb98e5..c69d24877 100644 --- a/datastore/bolt/util.go +++ b/datastore/bolt/util.go @@ -64,6 +64,12 @@ func push(t *bolt.Tx, bucket, index, value []byte) error { if err != nil && err != ErrKeyNotFound { return err } + // we shouldn't add a key that already exists + for _, key := range keys { + if bytes.Equal(key, value) { + return nil + } + } keys = append(keys, value) return update(t, bucket, index, &keys) } diff --git a/drone.go b/drone.go index 04c72519c..f7e15baaf 100644 --- a/drone.go +++ b/drone.go @@ -42,12 +42,12 @@ func main() { api.Use(server.SetRemote(remote)) api.Use(server.SetQueue(queue.New())) api.Use(server.SetSettings(settings)) + api.Use(server.SetSession(session)) api.Use(server.SetUser(session)) user := api.Group("/user") { user.Use(server.MustUser()) - user.Use(server.SetSession(session)) user.GET("", server.GetUserCurr) user.PATCH("", server.PutUserCurr) diff --git a/remote/github/helper.go b/remote/github/helper.go index 906340c1d..8714e4b2a 100644 --- a/remote/github/helper.go +++ b/remote/github/helper.go @@ -172,7 +172,7 @@ func GetHook(client *github.Client, owner, name, url string) (*github.Hook, erro return nil, err } for _, hook := range hooks { - if hook.Config["url"] == url { + if strings.HasPrefix(hook.Config["url"].(string), url) { return &hook, nil } } @@ -255,6 +255,9 @@ func DeleteKey(client *github.Client, owner, name, title string) error { if err != nil { return err } + if k == nil { + return nil + } _, err = client.Repositories.DeleteKey(owner, name, *k.ID) return err } diff --git a/server/hooks.go b/server/hooks.go index 4cabcc4b9..d7af6de93 100644 --- a/server/hooks.go +++ b/server/hooks.go @@ -21,6 +21,7 @@ func PostHook(c *gin.Context) { remote := ToRemote(c) store := ToDatastore(c) queue_ := ToQueue(c) + sess := ToSession(c) hook, err := remote.Hook(c.Request) if err != nil { @@ -38,6 +39,14 @@ func PostHook(c *gin.Context) { return } + // get the token and verify the hook is authorized + token := sess.GetLogin(c.Request) + if token == nil || token.Label != hook.Repo.FullName { + log.Errorf("invalid token sent with hook.") + c.AbortWithStatus(403) + return + } + // a build may be skipped if the text [CI SKIP] // is found inside the commit message if hook.Commit != nil && strings.Contains(hook.Commit.Message, "[CI SKIP]") { diff --git a/server/repos.go b/server/repos.go index bd7d19994..95e76b119 100644 --- a/server/repos.go +++ b/server/repos.go @@ -166,15 +166,11 @@ func DeleteRepo(c *gin.Context) { // func PostRepo(c *gin.Context) { user := ToUser(c) + sess := ToSession(c) store := ToDatastore(c) owner := c.Params.ByName("owner") name := c.Params.ByName("name") - link := fmt.Sprintf( - "%s/api/hook", - httputil.GetURL(c.Request), - ) - // TODO(bradrydzewski) verify repo not exists // get the repository and user permissions @@ -194,6 +190,21 @@ func PostRepo(c *gin.Context) { return } + token := &common.Token{} + token.Kind = common.TokenHook + token.Label = r.FullName + tokenstr, err := sess.GenerateToken(token) + if err != nil { + c.Fail(500, err) + return + } + + link := fmt.Sprintf( + "%s/api/hook?access_token=%s", + httputil.GetURL(c.Request), + tokenstr, + ) + // set the repository owner to the // currently authenticated user. r.User = &common.Owner{Login: user.Login} diff --git a/server/server.go b/server/server.go index ce96d4c43..bb0cb0cdc 100644 --- a/server/server.go +++ b/server/server.go @@ -125,7 +125,7 @@ func SetUser(s session.Session) gin.HandlerFunc { return func(c *gin.Context) { ds := ToDatastore(c) token := s.GetLogin(c.Request) - if token == nil { + if token == nil || len(token.Login) == 0 { c.Next() return } @@ -137,7 +137,8 @@ func SetUser(s session.Session) gin.HandlerFunc { // if session token we can proceed, otherwise // we should validate the token hasn't been revoked - if token.Kind == common.TokenSess { + switch token.Kind { + case common.TokenSess: c.Next() return } diff --git a/server/session/session.go b/server/session/session.go index bc2e3f48c..608b65f9a 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -8,7 +8,6 @@ import ( "github.com/dgrijalva/jwt-go" "github.com/drone/drone/common" "github.com/drone/drone/settings" - "github.com/gorilla/securecookie" ) type Session interface { @@ -22,12 +21,8 @@ type session struct { } func New(s *settings.Session) Session { - // TODO (bradrydzewski) hook up the Session.Expires secret := []byte(s.Secret) expire := time.Hour * 72 - if len(secret) == 0 { - securecookie.GenerateRandomKey(32) - } return &session{ secret: secret, expire: expire, diff --git a/settings/settings.go b/settings/settings.go index 3361f1791..235693a54 100644 --- a/settings/settings.go +++ b/settings/settings.go @@ -1,8 +1,6 @@ package settings -import ( - "github.com/BurntSushi/toml" -) +import "github.com/BurntSushi/toml" // Service represents the configuration details required // to connect to the revision control system (ie GitHub, Bitbucket) @@ -108,7 +106,7 @@ type Settings struct { func Parse(path string) (*Settings, error) { s := &Settings{} _, err := toml.DecodeFile(path, s) - return s, err + return applyDefaults(s), err } // ParseString parses the Drone settings string and unmarshals @@ -116,5 +114,18 @@ func Parse(path string) (*Settings, error) { func ParseString(data string) (*Settings, error) { s := &Settings{} _, err := toml.Decode(data, s) - return s, err + return applyDefaults(s), err +} + +func applyDefaults(s *Settings) *Settings { + if s.Session == nil { + s.Session = &Session{} + } + // if no session token is provided we can + // instead use the client secret to sign + // our sessions and tokens. + if len(s.Session.Secret) == 0 { + s.Session.Secret = s.Service.OAuth.Secret + } + return s }