initial work on tokens

This commit is contained in:
Brad Rydzewski 2015-04-12 22:32:32 -07:00
parent 0f60073adc
commit 66990a95b4
7 changed files with 52 additions and 50 deletions

View file

@ -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"`
}

View file

@ -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)
})
}

View file

@ -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.

View file

@ -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 {

View file

@ -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()
}
}

View file

@ -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

View file

@ -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)
}
}