diff --git a/common/token.go b/common/token.go index 7370598b0..e183806f8 100644 --- a/common/token.go +++ b/common/token.go @@ -1,9 +1,16 @@ package common +const ( + TokenUser = "u" + TokenSess = "s" +) + type Token struct { - Sha string `json:"-"` + Kind string `json:"-"` Login string `json:"-"` + Label string `json:"label"` Repos []string `json:"repos,omitempty"` Scopes []string `json:"scopes,omitempty"` Expiry int64 `json:"expiry,omitempty"` + Issued int64 `json:"issued_at,omitempty"` } diff --git a/datastore/bolt/token.go b/datastore/bolt/token.go index 48390c23f..ca5dec049 100644 --- a/datastore/bolt/token.go +++ b/datastore/bolt/token.go @@ -1,16 +1,16 @@ package bolt import ( - "github.com/drone/drone/common" "github.com/boltdb/bolt" + "github.com/drone/drone/common" ) // GetToken gets a token by sha value. -func (db *DB) GetToken(sha string) (*common.Token, error) { +func (db *DB) GetToken(user, label string) (*common.Token, error) { token := &common.Token{} - key := []byte(sha) + key := []byte(user + "/" + label) - err := db.View(func (t *bolt.Tx) error { + err := db.View(func(t *bolt.Tx) error { return get(t, bucketTokens, key, token) }) @@ -20,8 +20,8 @@ func (db *DB) GetToken(sha string) (*common.Token, error) { // InsertToken inserts a new user token in the datastore. // If the token already exists and error is returned. func (db *DB) InsertToken(token *common.Token) error { - key := []byte(token.Sha) - return db.Update(func (t *bolt.Tx) error { + key := []byte(token.Login + "/" + token.Label) + return db.Update(func(t *bolt.Tx) error { return insert(t, bucketTokens, key, token) }) // TODO(bradrydzewski) add token to users_token index @@ -29,8 +29,8 @@ func (db *DB) InsertToken(token *common.Token) error { // DeleteUser deletes the token. func (db *DB) DeleteToken(token *common.Token) error { - key := []byte(token.Sha) - return db.Update(func (t *bolt.Tx) error { + key := []byte(token.Login + "/" + token.Label) + return db.Update(func(t *bolt.Tx) error { return delete(t, bucketUser, key) }) } diff --git a/datastore/datastore.go b/datastore/datastore.go index 753913487..5371101ff 100644 --- a/datastore/datastore.go +++ b/datastore/datastore.go @@ -33,7 +33,7 @@ type Datastore interface { DeleteUser(*common.User) error // GetToken gets a token by sha value. - GetToken(string) (*common.Token, error) + GetToken(string, string) (*common.Token, error) // InsertToken inserts a new user token in the datastore. // If the token already exists and error is returned. diff --git a/server/login.go b/server/login.go index 3d83c7e43..162090c29 100644 --- a/server/login.go +++ b/server/login.go @@ -3,6 +3,7 @@ package server import ( "fmt" "strings" + "time" "github.com/gin-gonic/gin" @@ -106,8 +107,9 @@ func GetLogin(c *gin.Context) { } token := &common.Token{ - // Expiry: settings.Session.Expires, // TODO add this - Login: u.Login, + Kind: common.TokenSess, + Login: u.Login, + Issued: time.Now().UTC().Unix(), } tokenstr, err := session.GenerateToken(c.Request, token) if err != nil { diff --git a/server/server.go b/server/server.go index 897e7eb72..da69da0ff 100644 --- a/server/server.go +++ b/server/server.go @@ -118,6 +118,24 @@ func SetUser(s session.Session) gin.HandlerFunc { if err == nil { c.Set("user", u) } + + // if session token we can proceed, otherwise + // we should validate the token hasn't been revoked + if token.Kind == common.TokenSess { + c.Next() + return + } + + // to verify the token we fetch from the datastore + // and check to see if the token issued date matches + // what we found in the jwt (in case the label is re-used) + t, err := ds.GetToken(token.Login, token.Label) + if err != nil || t.Issued != token.Issued { + c.AbortWithStatus(403) + return + } + + c.Next() } } diff --git a/server/session/session.go b/server/session/session.go index 1542c5d62..5b178db1f 100644 --- a/server/session/session.go +++ b/server/session/session.go @@ -39,19 +39,10 @@ func New(s *settings.Session) Session { // facilitate client-based OAuth2. func (s *session) GenerateToken(r *http.Request, t *common.Token) (string, error) { token := jwt.New(jwt.GetSigningMethod("HS256")) - token.Claims["login"] = t.Login - token.Claims["expiry"] = t.Expiry - - // add optional repos that can be - // access from this session. - if len(t.Repos) != 0 { - token.Claims["repos"] = t.Repos - } - // add optional scopes that can be - // applied to this session. - if len(t.Scopes) != 0 { - token.Claims["scope"] = t.Scopes - } + token.Claims["user"] = t.Login + token.Claims["kind"] = t.Kind + token.Claims["date"] = t.Issued + token.Claims["label"] = t.Label return token.SignedString(s.secret) } @@ -65,21 +56,16 @@ func (s *session) GetLogin(r *http.Request) *common.Token { } claims := getClaims(t, s.secret) - if claims == nil || claims["login"] == nil { + if claims == nil || claims["user"] == nil || claims["date"] == nil || claims["label"] == nil || claims["kind"] == nil { return nil } - loginv, ok := claims["login"] - if !ok { - return nil + return &common.Token{ + Kind: claims["kind"].(string), + Login: claims["user"].(string), + Label: claims["label"].(string), + Issued: int64(claims["date"].(float64)), } - - loginstr, ok := loginv.(string) - if !ok { - return nil - } - - return &common.Token{Login: loginstr} } // getToken is a helper function that extracts the token diff --git a/server/token.go b/server/token.go index 60672b86c..882c6b01f 100644 --- a/server/token.go +++ b/server/token.go @@ -13,12 +13,12 @@ func PostToken(c *gin.Context) { // 3. return the random password to the UI and instruct the user to copy it } -// DELETE /api/user/tokens/:sha +// DELETE /api/user/tokens/:label func DelToken(c *gin.Context) { store := ToDatastore(c) user := ToUser(c) - hash := c.Params.ByName("hash") - token, err := store.GetToken(hash) + label := c.Params.ByName("label") + token, err := store.GetToken(user.Login, label) if err != nil { c.Fail(404, err) } @@ -26,15 +26,4 @@ func DelToken(c *gin.Context) { if err != nil { c.Fail(400, err) } - - // TODO(bradrydzewski) this should be encapsulated - // in our database code, since this feels like a - // database-specific implementation. - delete(user.Tokens, token.Sha) - err = store.UpdateUser(user) - if err != nil { - c.Fail(400, err) - } else { - c.Writer.WriteHeader(200) - } }