hooks are protected with signed sha

This commit is contained in:
Brad Rydzewski 2015-04-30 14:23:46 -07:00
parent 64cc684295
commit 6fcae7d80a
9 changed files with 59 additions and 21 deletions

View File

@ -1,8 +1,10 @@
package common package common
const ( const (
TokenUser = "u" TokenUser = "u"
TokenSess = "s" TokenSess = "s"
TokenHook = "h"
TokenAgent = "a"
) )
type Token struct { type Token struct {

View File

@ -64,6 +64,12 @@ func push(t *bolt.Tx, bucket, index, value []byte) error {
if err != nil && err != ErrKeyNotFound { if err != nil && err != ErrKeyNotFound {
return err 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) keys = append(keys, value)
return update(t, bucket, index, &keys) return update(t, bucket, index, &keys)
} }

View File

@ -42,12 +42,12 @@ func main() {
api.Use(server.SetRemote(remote)) api.Use(server.SetRemote(remote))
api.Use(server.SetQueue(queue.New())) api.Use(server.SetQueue(queue.New()))
api.Use(server.SetSettings(settings)) api.Use(server.SetSettings(settings))
api.Use(server.SetSession(session))
api.Use(server.SetUser(session)) api.Use(server.SetUser(session))
user := api.Group("/user") user := api.Group("/user")
{ {
user.Use(server.MustUser()) user.Use(server.MustUser())
user.Use(server.SetSession(session))
user.GET("", server.GetUserCurr) user.GET("", server.GetUserCurr)
user.PATCH("", server.PutUserCurr) user.PATCH("", server.PutUserCurr)

View File

@ -172,7 +172,7 @@ func GetHook(client *github.Client, owner, name, url string) (*github.Hook, erro
return nil, err return nil, err
} }
for _, hook := range hooks { for _, hook := range hooks {
if hook.Config["url"] == url { if strings.HasPrefix(hook.Config["url"].(string), url) {
return &hook, nil return &hook, nil
} }
} }
@ -255,6 +255,9 @@ func DeleteKey(client *github.Client, owner, name, title string) error {
if err != nil { if err != nil {
return err return err
} }
if k == nil {
return nil
}
_, err = client.Repositories.DeleteKey(owner, name, *k.ID) _, err = client.Repositories.DeleteKey(owner, name, *k.ID)
return err return err
} }

View File

@ -21,6 +21,7 @@ func PostHook(c *gin.Context) {
remote := ToRemote(c) remote := ToRemote(c)
store := ToDatastore(c) store := ToDatastore(c)
queue_ := ToQueue(c) queue_ := ToQueue(c)
sess := ToSession(c)
hook, err := remote.Hook(c.Request) hook, err := remote.Hook(c.Request)
if err != nil { if err != nil {
@ -38,6 +39,14 @@ func PostHook(c *gin.Context) {
return 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] // a build may be skipped if the text [CI SKIP]
// is found inside the commit message // is found inside the commit message
if hook.Commit != nil && strings.Contains(hook.Commit.Message, "[CI SKIP]") { if hook.Commit != nil && strings.Contains(hook.Commit.Message, "[CI SKIP]") {

View File

@ -166,15 +166,11 @@ func DeleteRepo(c *gin.Context) {
// //
func PostRepo(c *gin.Context) { func PostRepo(c *gin.Context) {
user := ToUser(c) user := ToUser(c)
sess := ToSession(c)
store := ToDatastore(c) store := ToDatastore(c)
owner := c.Params.ByName("owner") owner := c.Params.ByName("owner")
name := c.Params.ByName("name") name := c.Params.ByName("name")
link := fmt.Sprintf(
"%s/api/hook",
httputil.GetURL(c.Request),
)
// TODO(bradrydzewski) verify repo not exists // TODO(bradrydzewski) verify repo not exists
// get the repository and user permissions // get the repository and user permissions
@ -194,6 +190,21 @@ func PostRepo(c *gin.Context) {
return 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 // set the repository owner to the
// currently authenticated user. // currently authenticated user.
r.User = &common.Owner{Login: user.Login} r.User = &common.Owner{Login: user.Login}

View File

@ -125,7 +125,7 @@ func SetUser(s session.Session) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
ds := ToDatastore(c) ds := ToDatastore(c)
token := s.GetLogin(c.Request) token := s.GetLogin(c.Request)
if token == nil { if token == nil || len(token.Login) == 0 {
c.Next() c.Next()
return return
} }
@ -137,7 +137,8 @@ func SetUser(s session.Session) gin.HandlerFunc {
// if session token we can proceed, otherwise // if session token we can proceed, otherwise
// we should validate the token hasn't been revoked // we should validate the token hasn't been revoked
if token.Kind == common.TokenSess { switch token.Kind {
case common.TokenSess:
c.Next() c.Next()
return return
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/dgrijalva/jwt-go" "github.com/dgrijalva/jwt-go"
"github.com/drone/drone/common" "github.com/drone/drone/common"
"github.com/drone/drone/settings" "github.com/drone/drone/settings"
"github.com/gorilla/securecookie"
) )
type Session interface { type Session interface {
@ -22,12 +21,8 @@ type session struct {
} }
func New(s *settings.Session) Session { func New(s *settings.Session) Session {
// TODO (bradrydzewski) hook up the Session.Expires
secret := []byte(s.Secret) secret := []byte(s.Secret)
expire := time.Hour * 72 expire := time.Hour * 72
if len(secret) == 0 {
securecookie.GenerateRandomKey(32)
}
return &session{ return &session{
secret: secret, secret: secret,
expire: expire, expire: expire,

View File

@ -1,8 +1,6 @@
package settings package settings
import ( import "github.com/BurntSushi/toml"
"github.com/BurntSushi/toml"
)
// Service represents the configuration details required // Service represents the configuration details required
// to connect to the revision control system (ie GitHub, Bitbucket) // to connect to the revision control system (ie GitHub, Bitbucket)
@ -108,7 +106,7 @@ type Settings struct {
func Parse(path string) (*Settings, error) { func Parse(path string) (*Settings, error) {
s := &Settings{} s := &Settings{}
_, err := toml.DecodeFile(path, s) _, err := toml.DecodeFile(path, s)
return s, err return applyDefaults(s), err
} }
// ParseString parses the Drone settings string and unmarshals // ParseString parses the Drone settings string and unmarshals
@ -116,5 +114,18 @@ func Parse(path string) (*Settings, error) {
func ParseString(data string) (*Settings, error) { func ParseString(data string) (*Settings, error) {
s := &Settings{} s := &Settings{}
_, err := toml.Decode(data, s) _, 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
} }