You've already forked woodpecker
							
							
				mirror of
				https://github.com/woodpecker-ci/woodpecker.git
				synced 2025-10-30 23:27:39 +02:00 
			
		
		
		
	cleaned up the token implementation for #1175
This commit is contained in:
		
							
								
								
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Godeps/Godeps.json
									
									
									
										generated
									
									
									
								
							| @@ -25,8 +25,8 @@ | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/dgrijalva/jwt-go", | ||||
| 			"Comment": "v2.2.0-16-gc48cfd5", | ||||
| 			"Rev": "c48cfd5d9711c75acb6036d2698ef3aef7bb655a" | ||||
| 			"Comment": "v2.3.0-4-gc1da563", | ||||
| 			"Rev": "c1da56349675b292d3200463e2c88b9aa5e02391" | ||||
| 		}, | ||||
| 		{ | ||||
| 			"ImportPath": "github.com/elazarl/go-bindata-assetfs", | ||||
|   | ||||
							
								
								
									
										7
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| language: go | ||||
|  | ||||
| go: | ||||
|   - 1.3.3 | ||||
|   - 1.4.2 | ||||
|   - 1.5 | ||||
|   - tip | ||||
							
								
								
									
										14
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,5 +1,7 @@ | ||||
| A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-jones-json-web-token.html) | ||||
|  | ||||
| [](https://travis-ci.org/dgrijalva/jwt-go) | ||||
|  | ||||
| **NOTICE:** A vulnerability in JWT was [recently published](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/).  As this library doesn't force users to validate the `alg` is what they expected, it's possible your usage is effected.  There will be an update soon to remedy this, and it will likey require backwards-incompatible changes to the API.  In the short term, please make sure your implementation verifies the `alg` is what you expect. | ||||
|  | ||||
| ## What the heck is a JWT? | ||||
| @@ -21,8 +23,8 @@ Parsing and verifying tokens is pretty straight forward.  You pass in the token | ||||
| ```go | ||||
| 	token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) { | ||||
| 		// Don't forget to validate the alg is what you expect: | ||||
| 		if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok { | ||||
| 			return nil, fmt.Errorf("Unexpected signing method: %v", t.Header["alg"]) | ||||
| 		if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { | ||||
| 			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) | ||||
| 		} | ||||
| 		return myLookupKey(token.Header["kid"]) | ||||
| 	}) | ||||
| @@ -46,12 +48,20 @@ Parsing and verifying tokens is pretty straight forward.  You pass in the token | ||||
| 	tokenString, err := token.SignedString(mySigningKey) | ||||
| ```	 | ||||
|  | ||||
| ## Extensions | ||||
|  | ||||
| This library publishes all the necessary components for adding your own signing methods.  Simply implement the `SigningMethod` interface and register a factory method using `RegisterSigningMethod`.   | ||||
|  | ||||
| Here's an example of an extension that integrates with the Google App Engine signing tools: https://github.com/someone1/gcp-jwt-go | ||||
|  | ||||
| ## Project Status & Versioning | ||||
|  | ||||
| This library is considered production ready.  Feedback and feature requests are appreciated.  The API should be considered stable.  There should be very few backwards-incompatible changes outside of major version updates (and only with good reason). | ||||
|  | ||||
| This project uses [Semantic Versioning 2.0.0](http://semver.org).  Accepted pull requests will land on `master`.  Periodically, versions will be tagged from `master`.  You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases). | ||||
|  | ||||
| While we try to make it obvious when we make breaking changes, there isn't a great mechanism for pushing announcements out to users.  You may want to use this alternative package include: `gopkg.in/dgrijalva/jwt-go.v2`.  It will do the right thing WRT semantic versioning. | ||||
|  | ||||
| ## More | ||||
|  | ||||
| Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go). | ||||
|   | ||||
							
								
								
									
										5
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,5 +1,10 @@ | ||||
| ## `jwt-go` Version History | ||||
|  | ||||
| #### 2.3.0 | ||||
|  | ||||
| * Added support for ECDSA signing methods | ||||
| * Added support for RSA PSS signing methods (requires go v1.4) | ||||
|  | ||||
| #### 2.2.0 | ||||
|  | ||||
| * Gracefully handle a `nil` `Keyfunc` being passed to `Parse`.  Result will now be the parsed token and an error, instead of a panic. | ||||
|   | ||||
							
								
								
									
										2
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -15,7 +15,7 @@ import ( | ||||
| 	"os" | ||||
| 	"regexp" | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
|   | ||||
							
								
								
									
										136
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/ecdsa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/ecdsa.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| package jwt | ||||
|  | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/rand" | ||||
| 	"encoding/asn1" | ||||
| 	"errors" | ||||
| 	"math/big" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// Sadly this is missing from crypto/ecdsa compared to crypto/rsa | ||||
| 	ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") | ||||
| ) | ||||
|  | ||||
| // Implements the ECDSA family of signing methods signing methods | ||||
| type SigningMethodECDSA struct { | ||||
| 	Name string | ||||
| 	Hash crypto.Hash | ||||
| } | ||||
|  | ||||
| // Marshalling structure for r, s EC point | ||||
| type ECPoint struct { | ||||
| 	R *big.Int | ||||
| 	S *big.Int | ||||
| } | ||||
|  | ||||
| // Specific instances for EC256 and company | ||||
| var ( | ||||
| 	SigningMethodES256 *SigningMethodECDSA | ||||
| 	SigningMethodES384 *SigningMethodECDSA | ||||
| 	SigningMethodES512 *SigningMethodECDSA | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	// ES256 | ||||
| 	SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256} | ||||
| 	RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { | ||||
| 		return SigningMethodES256 | ||||
| 	}) | ||||
|  | ||||
| 	// ES384 | ||||
| 	SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384} | ||||
| 	RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { | ||||
| 		return SigningMethodES384 | ||||
| 	}) | ||||
|  | ||||
| 	// ES512 | ||||
| 	SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512} | ||||
| 	RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { | ||||
| 		return SigningMethodES512 | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (m *SigningMethodECDSA) Alg() string { | ||||
| 	return m.Name | ||||
| } | ||||
|  | ||||
| // Implements the Verify method from SigningMethod | ||||
| // For this verify method, key must be an ecdsa.PublicKey struct | ||||
| func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { | ||||
| 	var err error | ||||
|  | ||||
| 	// Decode the signature | ||||
| 	var sig []byte | ||||
| 	if sig, err = DecodeSegment(signature); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Get the key | ||||
| 	var ecdsaKey *ecdsa.PublicKey | ||||
| 	switch k := key.(type) { | ||||
| 	case *ecdsa.PublicKey: | ||||
| 		ecdsaKey = k | ||||
| 	default: | ||||
| 		return ErrInvalidKey | ||||
| 	} | ||||
|  | ||||
| 	// Unmarshal asn1 ECPoint | ||||
| 	var ecpoint = new(ECPoint) | ||||
| 	if _, err := asn1.Unmarshal(sig, ecpoint); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Create hasher | ||||
| 	if !m.Hash.Available() { | ||||
| 		return ErrHashUnavailable | ||||
| 	} | ||||
| 	hasher := m.Hash.New() | ||||
| 	hasher.Write([]byte(signingString)) | ||||
|  | ||||
| 	// Verify the signature | ||||
| 	if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), ecpoint.R, ecpoint.S); verifystatus == true { | ||||
| 		return nil | ||||
| 	} else { | ||||
| 		return ErrECDSAVerification | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Implements the Sign method from SigningMethod | ||||
| // For this signing method, key must be an ecdsa.PrivateKey struct | ||||
| func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { | ||||
| 	// Get the key | ||||
| 	var ecdsaKey *ecdsa.PrivateKey | ||||
| 	switch k := key.(type) { | ||||
| 	case *ecdsa.PrivateKey: | ||||
| 		ecdsaKey = k | ||||
| 	default: | ||||
| 		return "", ErrInvalidKey | ||||
| 	} | ||||
|  | ||||
| 	// Create the hasher | ||||
| 	if !m.Hash.Available() { | ||||
| 		return "", ErrHashUnavailable | ||||
| 	} | ||||
|  | ||||
| 	hasher := m.Hash.New() | ||||
| 	hasher.Write([]byte(signingString)) | ||||
|  | ||||
| 	// Sign the string and return r, s | ||||
| 	if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { | ||||
| 		// asn1 marhsal r, s using ecPoint as the structure | ||||
| 		var ecpoint = new(ECPoint) | ||||
| 		ecpoint.R = r | ||||
| 		ecpoint.S = s | ||||
|  | ||||
| 		if signature, err := asn1.Marshal(*ecpoint); err != nil { | ||||
| 			return "", err | ||||
| 		} else { | ||||
| 			return EncodeSegment(signature), nil | ||||
| 		} | ||||
| 	} else { | ||||
| 		return "", err | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										100
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/ecdsa_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/ecdsa_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| package jwt_test | ||||
|  | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| ) | ||||
|  | ||||
| var ecdsaTestData = []struct { | ||||
| 	name        string | ||||
| 	keys        map[string]string | ||||
| 	tokenString string | ||||
| 	alg         string | ||||
| 	claims      map[string]interface{} | ||||
| 	valid       bool | ||||
| }{ | ||||
| 	{ | ||||
| 		"Basic ES256", | ||||
| 		map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"}, | ||||
| 		"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8w", | ||||
| 		"ES256", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"Basic ES384", | ||||
| 		map[string]string{"private": "test/ec384-private.pem", "public": "test/ec384-public.pem"}, | ||||
| 		"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MGUCMQCHBr61FXDuFY9xUhyp8iWQAuBIaSgaf1z2j_8XrKcCfzTPzoSa3SZKq-m3L492xe8CMG3kafRMeuaN5Aw8ZJxmOLhkTo4D3-LaGzcaUWINvWvkwFMl7dMC863s0gov6xvXuA", | ||||
| 		"ES384", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"Basic ES512", | ||||
| 		map[string]string{"private": "test/ec512-private.pem", "public": "test/ec512-public.pem"}, | ||||
| 		"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MIGIAkIAmVKjdJE5lG1byOFgZZVTeNDRp6E7SNvUj0UrvpzoBH6nrleWVTcwfHzbwWuooNpPADDSFR_Ql3ze-Vwwi8hBqQsCQgHn-ZooL8zegkOVeEEsqd7WHWdhb8UekFCYw3X8JnNP-D3wvZQ1-tkkHakt5gZ2-xO29TxfSPun4ViGkMYa7Q4N-Q", | ||||
| 		"ES512", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"basic ES256 invalid: foo => bar", | ||||
| 		map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"}, | ||||
| 		"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8W", | ||||
| 		"ES256", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		false, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func TestECDSAVerify(t *testing.T) { | ||||
| 	for _, data := range ecdsaTestData { | ||||
| 		var err error | ||||
|  | ||||
| 		key, _ := ioutil.ReadFile(data.keys["public"]) | ||||
|  | ||||
| 		var ecdsaKey *ecdsa.PublicKey | ||||
| 		if ecdsaKey, err = jwt.ParseECPublicKeyFromPEM(key); err != nil { | ||||
| 			t.Errorf("Unable to parse ECDSA public key: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		parts := strings.Split(data.tokenString, ".") | ||||
|  | ||||
| 		method := jwt.GetSigningMethod(data.alg) | ||||
| 		err = method.Verify(strings.Join(parts[0:2], "."), parts[2], ecdsaKey) | ||||
| 		if data.valid && err != nil { | ||||
| 			t.Errorf("[%v] Error while verifying key: %v", data.name, err) | ||||
| 		} | ||||
| 		if !data.valid && err == nil { | ||||
| 			t.Errorf("[%v] Invalid key passed validation", data.name) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestECDSASign(t *testing.T) { | ||||
| 	for _, data := range ecdsaTestData { | ||||
| 		var err error | ||||
| 		key, _ := ioutil.ReadFile(data.keys["private"]) | ||||
|  | ||||
| 		var ecdsaKey *ecdsa.PrivateKey | ||||
| 		if ecdsaKey, err = jwt.ParseECPrivateKeyFromPEM(key); err != nil { | ||||
| 			t.Errorf("Unable to parse ECDSA private key: %v", err) | ||||
| 		} | ||||
|  | ||||
| 		if data.valid { | ||||
| 			parts := strings.Split(data.tokenString, ".") | ||||
| 			method := jwt.GetSigningMethod(data.alg) | ||||
| 			sig, err := method.Sign(strings.Join(parts[0:2], "."), ecdsaKey) | ||||
| 			if err != nil { | ||||
| 				t.Errorf("[%v] Error signing token: %v", data.name, err) | ||||
| 			} | ||||
| 			if sig == parts[2] { | ||||
| 				t.Errorf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										67
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/ecdsa_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/ecdsa_utils.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| package jwt | ||||
|  | ||||
| import ( | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/pem" | ||||
| 	"errors" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrNotECPublicKey  = errors.New("Key is not a valid ECDSA public key") | ||||
| 	ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key") | ||||
| ) | ||||
|  | ||||
| // Parse PEM encoded Elliptic Curve Private Key Structure | ||||
| func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) { | ||||
| 	var err error | ||||
|  | ||||
| 	// Parse PEM block | ||||
| 	var block *pem.Block | ||||
| 	if block, _ = pem.Decode(key); block == nil { | ||||
| 		return nil, ErrKeyMustBePEMEncoded | ||||
| 	} | ||||
|  | ||||
| 	// Parse the key | ||||
| 	var parsedKey interface{} | ||||
| 	if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var pkey *ecdsa.PrivateKey | ||||
| 	var ok bool | ||||
| 	if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok { | ||||
| 		return nil, ErrNotECPrivateKey | ||||
| 	} | ||||
|  | ||||
| 	return pkey, nil | ||||
| } | ||||
|  | ||||
| // Parse PEM encoded PKCS1 or PKCS8 public key | ||||
| func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) { | ||||
| 	var err error | ||||
|  | ||||
| 	// Parse PEM block | ||||
| 	var block *pem.Block | ||||
| 	if block, _ = pem.Decode(key); block == nil { | ||||
| 		return nil, ErrKeyMustBePEMEncoded | ||||
| 	} | ||||
|  | ||||
| 	// Parse the key | ||||
| 	var parsedKey interface{} | ||||
| 	if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil { | ||||
| 		if cert, err := x509.ParseCertificate(block.Bytes); err == nil { | ||||
| 			parsedKey = cert.PublicKey | ||||
| 		} else { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var pkey *ecdsa.PublicKey | ||||
| 	var ok bool | ||||
| 	if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok { | ||||
| 		return nil, ErrNotECPublicKey | ||||
| 	} | ||||
|  | ||||
| 	return pkey, nil | ||||
| } | ||||
							
								
								
									
										43
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| package jwt | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| ) | ||||
|  | ||||
| // Error constants | ||||
| var ( | ||||
| 	ErrInvalidKey       = errors.New("key is invalid or of invalid type") | ||||
| 	ErrHashUnavailable  = errors.New("the requested hash function is unavailable") | ||||
| 	ErrNoTokenInRequest = errors.New("no token present in request") | ||||
| ) | ||||
|  | ||||
| // The errors that might occur when parsing and validating a token | ||||
| const ( | ||||
| 	ValidationErrorMalformed        uint32 = 1 << iota // Token is malformed | ||||
| 	ValidationErrorUnverifiable                        // Token could not be verified because of signing problems | ||||
| 	ValidationErrorSignatureInvalid                    // Signature validation failed | ||||
| 	ValidationErrorExpired                             // Exp validation failed | ||||
| 	ValidationErrorNotValidYet                         // NBF validation failed | ||||
| ) | ||||
|  | ||||
| // The error from Parse if token is not valid | ||||
| type ValidationError struct { | ||||
| 	err    string | ||||
| 	Errors uint32 // bitfield.  see ValidationError... constants | ||||
| } | ||||
|  | ||||
| // Validation error is an error type | ||||
| func (e ValidationError) Error() string { | ||||
| 	if e.err == "" { | ||||
| 		return "token is invalid" | ||||
| 	} | ||||
| 	return e.err | ||||
| } | ||||
|  | ||||
| // No errors | ||||
| func (e *ValidationError) valid() bool { | ||||
| 	if e.Errors > 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										2
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/example_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/example_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ package jwt_test | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
|   | ||||
							
								
								
									
										14
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/hmac_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/hmac_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| package jwt_test | ||||
|  | ||||
| import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| @@ -77,3 +77,15 @@ func TestHMACSign(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BenchmarkHS256Signing(b *testing.B) { | ||||
| 	benchmarkSigning(b, jwt.SigningMethodHS256, hmacTestKey) | ||||
| } | ||||
|  | ||||
| func BenchmarkHS384Signing(b *testing.B) { | ||||
| 	benchmarkSigning(b, jwt.SigningMethodHS384, hmacTestKey) | ||||
| } | ||||
|  | ||||
| func BenchmarkHS512Signing(b *testing.B) { | ||||
| 	benchmarkSigning(b, jwt.SigningMethodHS512, hmacTestKey) | ||||
| } | ||||
|   | ||||
							
								
								
									
										39
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,7 +3,6 @@ package jwt | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| @@ -20,13 +19,6 @@ var TimeFunc = time.Now | ||||
| // Header of the token (such as `kid`) to identify which key to use. | ||||
| type Keyfunc func(*Token) (interface{}, error) | ||||
|  | ||||
| // Error constants | ||||
| var ( | ||||
| 	ErrInvalidKey       = errors.New("key is invalid or of invalid type") | ||||
| 	ErrHashUnavailable  = errors.New("the requested hash function is unavailable") | ||||
| 	ErrNoTokenInRequest = errors.New("no token present in request") | ||||
| ) | ||||
|  | ||||
| // A JWT Token.  Different fields will be used depending on whether you're | ||||
| // creating or parsing/verifying a token. | ||||
| type Token struct { | ||||
| @@ -167,37 +159,6 @@ func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { | ||||
| 	return token, vErr | ||||
| } | ||||
|  | ||||
| // The errors that might occur when parsing and validating a token | ||||
| const ( | ||||
| 	ValidationErrorMalformed        uint32 = 1 << iota // Token is malformed | ||||
| 	ValidationErrorUnverifiable                        // Token could not be verified because of signing problems | ||||
| 	ValidationErrorSignatureInvalid                    // Signature validation failed | ||||
| 	ValidationErrorExpired                             // Exp validation failed | ||||
| 	ValidationErrorNotValidYet                         // NBF validation failed | ||||
| ) | ||||
|  | ||||
| // The error from Parse if token is not valid | ||||
| type ValidationError struct { | ||||
| 	err    string | ||||
| 	Errors uint32 // bitfield.  see ValidationError... constants | ||||
| } | ||||
|  | ||||
| // Validation error is an error type | ||||
| func (e ValidationError) Error() string { | ||||
| 	if e.err == "" { | ||||
| 		return "token is invalid" | ||||
| 	} | ||||
| 	return e.err | ||||
| } | ||||
|  | ||||
| // No errors | ||||
| func (e *ValidationError) valid() bool { | ||||
| 	if e.Errors > 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // Try to find the token in an http.Request. | ||||
| // This method will call ParseMultipartForm if there's no token in the header. | ||||
| // Currently, it looks in the Authorization header as well as | ||||
|   | ||||
							
								
								
									
										15
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ package jwt_test | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"reflect" | ||||
| @@ -172,3 +172,16 @@ func TestParseRequest(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Helper method for benchmarking various methods | ||||
| func benchmarkSigning(b *testing.B, method jwt.SigningMethod, key interface{}) { | ||||
| 	t := jwt.New(method) | ||||
| 	b.RunParallel(func(pb *testing.PB) { | ||||
| 		for pb.Next() { | ||||
| 			if _, err := t.SignedString(key); err != nil { | ||||
| 				b.Fatal(err) | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										126
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_pss.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_pss.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,126 @@ | ||||
| // +build go1.4 | ||||
|  | ||||
| package jwt | ||||
|  | ||||
| import ( | ||||
| 	"crypto" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| ) | ||||
|  | ||||
| // Implements the RSAPSS family of signing methods signing methods | ||||
| type SigningMethodRSAPSS struct { | ||||
| 	*SigningMethodRSA | ||||
| 	Options *rsa.PSSOptions | ||||
| } | ||||
|  | ||||
| // Specific instances for RS/PS and company | ||||
| var ( | ||||
| 	SigningMethodPS256 *SigningMethodRSAPSS | ||||
| 	SigningMethodPS384 *SigningMethodRSAPSS | ||||
| 	SigningMethodPS512 *SigningMethodRSAPSS | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	// PS256 | ||||
| 	SigningMethodPS256 = &SigningMethodRSAPSS{ | ||||
| 		&SigningMethodRSA{ | ||||
| 			Name: "PS256", | ||||
| 			Hash: crypto.SHA256, | ||||
| 		}, | ||||
| 		&rsa.PSSOptions{ | ||||
| 			SaltLength: rsa.PSSSaltLengthAuto, | ||||
| 			Hash:       crypto.SHA256, | ||||
| 		}, | ||||
| 	} | ||||
| 	RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod { | ||||
| 		return SigningMethodPS256 | ||||
| 	}) | ||||
|  | ||||
| 	// PS384 | ||||
| 	SigningMethodPS384 = &SigningMethodRSAPSS{ | ||||
| 		&SigningMethodRSA{ | ||||
| 			Name: "PS384", | ||||
| 			Hash: crypto.SHA384, | ||||
| 		}, | ||||
| 		&rsa.PSSOptions{ | ||||
| 			SaltLength: rsa.PSSSaltLengthAuto, | ||||
| 			Hash:       crypto.SHA384, | ||||
| 		}, | ||||
| 	} | ||||
| 	RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod { | ||||
| 		return SigningMethodPS384 | ||||
| 	}) | ||||
|  | ||||
| 	// PS512 | ||||
| 	SigningMethodPS512 = &SigningMethodRSAPSS{ | ||||
| 		&SigningMethodRSA{ | ||||
| 			Name: "PS512", | ||||
| 			Hash: crypto.SHA512, | ||||
| 		}, | ||||
| 		&rsa.PSSOptions{ | ||||
| 			SaltLength: rsa.PSSSaltLengthAuto, | ||||
| 			Hash:       crypto.SHA512, | ||||
| 		}, | ||||
| 	} | ||||
| 	RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod { | ||||
| 		return SigningMethodPS512 | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Implements the Verify method from SigningMethod | ||||
| // For this verify method, key must be an rsa.PrivateKey struct | ||||
| func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error { | ||||
| 	var err error | ||||
|  | ||||
| 	// Decode the signature | ||||
| 	var sig []byte | ||||
| 	if sig, err = DecodeSegment(signature); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	var rsaKey *rsa.PublicKey | ||||
| 	switch k := key.(type) { | ||||
| 	case *rsa.PublicKey: | ||||
| 		rsaKey = k | ||||
| 	default: | ||||
| 		return ErrInvalidKey | ||||
| 	} | ||||
|  | ||||
| 	// Create hasher | ||||
| 	if !m.Hash.Available() { | ||||
| 		return ErrHashUnavailable | ||||
| 	} | ||||
| 	hasher := m.Hash.New() | ||||
| 	hasher.Write([]byte(signingString)) | ||||
|  | ||||
| 	return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options) | ||||
| } | ||||
|  | ||||
| // Implements the Sign method from SigningMethod | ||||
| // For this signing method, key must be an rsa.PublicKey struct | ||||
| func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) { | ||||
| 	var rsaKey *rsa.PrivateKey | ||||
|  | ||||
| 	switch k := key.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		rsaKey = k | ||||
| 	default: | ||||
| 		return "", ErrInvalidKey | ||||
| 	} | ||||
|  | ||||
| 	// Create the hasher | ||||
| 	if !m.Hash.Available() { | ||||
| 		return "", ErrHashUnavailable | ||||
| 	} | ||||
|  | ||||
| 	hasher := m.Hash.New() | ||||
| 	hasher.Write([]byte(signingString)) | ||||
|  | ||||
| 	// Sign the string and return the encoded bytes | ||||
| 	if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil { | ||||
| 		return EncodeSegment(sigBytes), nil | ||||
| 	} else { | ||||
| 		return "", err | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										96
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_pss_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_pss_test.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| // +build go1.4 | ||||
|  | ||||
| package jwt_test | ||||
|  | ||||
| import ( | ||||
| 	"crypto/rsa" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| ) | ||||
|  | ||||
| var rsaPSSTestData = []struct { | ||||
| 	name        string | ||||
| 	tokenString string | ||||
| 	alg         string | ||||
| 	claims      map[string]interface{} | ||||
| 	valid       bool | ||||
| }{ | ||||
| 	{ | ||||
| 		"Basic PS256", | ||||
| 		"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9w", | ||||
| 		"PS256", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"Basic PS384", | ||||
| 		"eyJhbGciOiJQUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.w7-qqgj97gK4fJsq_DCqdYQiylJjzWONvD0qWWWhqEOFk2P1eDULPnqHRnjgTXoO4HAw4YIWCsZPet7nR3Xxq4ZhMqvKW8b7KlfRTb9cH8zqFvzMmybQ4jv2hKc3bXYqVow3AoR7hN_CWXI3Dv6Kd2X5xhtxRHI6IL39oTVDUQ74LACe-9t4c3QRPuj6Pq1H4FAT2E2kW_0KOc6EQhCLWEhm2Z2__OZskDC8AiPpP8Kv4k2vB7l0IKQu8Pr4RcNBlqJdq8dA5D3hk5TLxP8V5nG1Ib80MOMMqoS3FQvSLyolFX-R_jZ3-zfq6Ebsqr0yEb0AH2CfsECF7935Pa0FKQ", | ||||
| 		"PS384", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"Basic PS512", | ||||
| 		"eyJhbGciOiJQUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.GX1HWGzFaJevuSLavqqFYaW8_TpvcjQ8KfC5fXiSDzSiT9UD9nB_ikSmDNyDILNdtjZLSvVKfXxZJqCfefxAtiozEDDdJthZ-F0uO4SPFHlGiXszvKeodh7BuTWRI2wL9-ZO4mFa8nq3GMeQAfo9cx11i7nfN8n2YNQ9SHGovG7_T_AvaMZB_jT6jkDHpwGR9mz7x1sycckEo6teLdHRnH_ZdlHlxqknmyTu8Odr5Xh0sJFOL8BepWbbvIIn-P161rRHHiDWFv6nhlHwZnVzjx7HQrWSGb6-s2cdLie9QL_8XaMcUpjLkfOMKkDOfHo6AvpL7Jbwi83Z2ZTHjJWB-A", | ||||
| 		"PS512", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		true, | ||||
| 	}, | ||||
| 	{ | ||||
| 		"basic PS256 invalid: foo => bar", | ||||
| 		"eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.PPG4xyDVY8ffp4CcxofNmsTDXsrVG2npdQuibLhJbv4ClyPTUtR5giNSvuxo03kB6I8VXVr0Y9X7UxhJVEoJOmULAwRWaUsDnIewQa101cVhMa6iR8X37kfFoiZ6NkS-c7henVkkQWu2HtotkEtQvN5hFlk8IevXXPmvZlhQhwzB1sGzGYnoi1zOfuL98d3BIjUjtlwii5w6gYG2AEEzp7HnHCsb3jIwUPdq86Oe6hIFjtBwduIK90ca4UqzARpcfwxHwVLMpatKask00AgGVI0ysdk0BLMjmLutquD03XbThHScC2C2_Pp4cHWgMzvbgLU2RYYZcZRKr46QeNgz9W", | ||||
| 		"PS256", | ||||
| 		map[string]interface{}{"foo": "bar"}, | ||||
| 		false, | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| func TestRSAPSSVerify(t *testing.T) { | ||||
| 	var err error | ||||
|  | ||||
| 	key, _ := ioutil.ReadFile("test/sample_key.pub") | ||||
| 	var rsaPSSKey *rsa.PublicKey | ||||
| 	if rsaPSSKey, err = jwt.ParseRSAPublicKeyFromPEM(key); err != nil { | ||||
| 		t.Errorf("Unable to parse RSA public key: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	for _, data := range rsaPSSTestData { | ||||
| 		parts := strings.Split(data.tokenString, ".") | ||||
|  | ||||
| 		method := jwt.GetSigningMethod(data.alg) | ||||
| 		err := method.Verify(strings.Join(parts[0:2], "."), parts[2], rsaPSSKey) | ||||
| 		if data.valid && err != nil { | ||||
| 			t.Errorf("[%v] Error while verifying key: %v", data.name, err) | ||||
| 		} | ||||
| 		if !data.valid && err == nil { | ||||
| 			t.Errorf("[%v] Invalid key passed validation", data.name) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRSAPSSSign(t *testing.T) { | ||||
| 	var err error | ||||
|  | ||||
| 	key, _ := ioutil.ReadFile("test/sample_key") | ||||
| 	var rsaPSSKey *rsa.PrivateKey | ||||
| 	if rsaPSSKey, err = jwt.ParseRSAPrivateKeyFromPEM(key); err != nil { | ||||
| 		t.Errorf("Unable to parse RSA private key: %v", err) | ||||
| 	} | ||||
|  | ||||
| 	for _, data := range rsaPSSTestData { | ||||
| 		if data.valid { | ||||
| 			parts := strings.Split(data.tokenString, ".") | ||||
| 			method := jwt.GetSigningMethod(data.alg) | ||||
| 			sig, err := method.Sign(strings.Join(parts[0:2], "."), rsaPSSKey) | ||||
| 			if err != nil { | ||||
| 				t.Errorf("[%v] Error signing token: %v", data.name, err) | ||||
| 			} | ||||
| 			if sig == parts[2] { | ||||
| 				t.Errorf("[%v] Signatures shouldn't match\nnew:\n%v\noriginal:\n%v", data.name, sig, parts[2]) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										32
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_test.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| package jwt_test | ||||
|  | ||||
| import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	"io/ioutil" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| @@ -142,3 +142,33 @@ func TestRSAKeyParsing(t *testing.T) { | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| func BenchmarkRS256Signing(b *testing.B) { | ||||
| 	key, _ := ioutil.ReadFile("test/sample_key") | ||||
| 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key) | ||||
| 	if err != nil { | ||||
| 		b.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	benchmarkSigning(b, jwt.SigningMethodRS256, parsedKey) | ||||
| } | ||||
|  | ||||
| func BenchmarkRS384Signing(b *testing.B) { | ||||
| 	key, _ := ioutil.ReadFile("test/sample_key") | ||||
| 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key) | ||||
| 	if err != nil { | ||||
| 		b.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	benchmarkSigning(b, jwt.SigningMethodRS384, parsedKey) | ||||
| } | ||||
|  | ||||
| func BenchmarkRS512Signing(b *testing.B) { | ||||
| 	key, _ := ioutil.ReadFile("test/sample_key") | ||||
| 	parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key) | ||||
| 	if err != nil { | ||||
| 		b.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	benchmarkSigning(b, jwt.SigningMethodRS512, parsedKey) | ||||
| } | ||||
|   | ||||
							
								
								
									
										5
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec256-private.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec256-private.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| -----BEGIN EC PRIVATE KEY----- | ||||
| MHcCAQEEIAh5qA3rmqQQuu0vbKV/+zouz/y/Iy2pLpIcWUSyImSwoAoGCCqGSM49 | ||||
| AwEHoUQDQgAEYD54V/vp+54P9DXarYqx4MPcm+HKRIQzNasYSoRQHQ/6S6Ps8tpM | ||||
| cT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg== | ||||
| -----END EC PRIVATE KEY----- | ||||
							
								
								
									
										4
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec256-public.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec256-public.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| -----BEGIN PUBLIC KEY----- | ||||
| MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYD54V/vp+54P9DXarYqx4MPcm+HK | ||||
| RIQzNasYSoRQHQ/6S6Ps8tpMcT+KvIIC8W/e9k0W7Cm72M1P9jU7SLf/vg== | ||||
| -----END PUBLIC KEY----- | ||||
							
								
								
									
										6
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec384-private.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec384-private.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| -----BEGIN EC PRIVATE KEY----- | ||||
| MIGkAgEBBDCaCvMHKhcG/qT7xsNLYnDT7sE/D+TtWIol1ROdaK1a564vx5pHbsRy | ||||
| SEKcIxISi1igBwYFK4EEACKhZANiAATYa7rJaU7feLMqrAx6adZFNQOpaUH/Uylb | ||||
| ZLriOLON5YFVwtVUpO1FfEXZUIQpptRPtc5ixIPY658yhBSb6irfIJUSP9aYTflJ | ||||
| GKk/mDkK4t8mWBzhiD5B6jg9cEGhGgA= | ||||
| -----END EC PRIVATE KEY----- | ||||
							
								
								
									
										5
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec384-public.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec384-public.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| -----BEGIN PUBLIC KEY----- | ||||
| MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2Gu6yWlO33izKqwMemnWRTUDqWlB/1Mp | ||||
| W2S64jizjeWBVcLVVKTtRXxF2VCEKabUT7XOYsSD2OufMoQUm+oq3yCVEj/WmE35 | ||||
| SRipP5g5CuLfJlgc4Yg+Qeo4PXBBoRoA | ||||
| -----END PUBLIC KEY----- | ||||
							
								
								
									
										7
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec512-private.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec512-private.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| -----BEGIN EC PRIVATE KEY----- | ||||
| MIHcAgEBBEIB0pE4uFaWRx7t03BsYlYvF1YvKaBGyvoakxnodm9ou0R9wC+sJAjH | ||||
| QZZJikOg4SwNqgQ/hyrOuDK2oAVHhgVGcYmgBwYFK4EEACOhgYkDgYYABAAJXIuw | ||||
| 12MUzpHggia9POBFYXSxaOGKGbMjIyDI+6q7wi7LMw3HgbaOmgIqFG72o8JBQwYN | ||||
| 4IbXHf+f86CRY1AA2wHzbHvt6IhkCXTNxBEffa1yMUgu8n9cKKF2iLgyQKcKqW33 | ||||
| 8fGOw/n3Rm2Yd/EB56u2rnD29qS+nOM9eGS+gy39OQ== | ||||
| -----END EC PRIVATE KEY----- | ||||
							
								
								
									
										6
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec512-public.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/ec512-public.pem
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| -----BEGIN PUBLIC KEY----- | ||||
| MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQACVyLsNdjFM6R4IImvTzgRWF0sWjh | ||||
| ihmzIyMgyPuqu8IuyzMNx4G2jpoCKhRu9qPCQUMGDeCG1x3/n/OgkWNQANsB82x7 | ||||
| 7eiIZAl0zcQRH32tcjFILvJ/XCihdoi4MkCnCqlt9/HxjsP590ZtmHfxAeertq5w | ||||
| 9vakvpzjPXhkvoMt/Tk= | ||||
| -----END PUBLIC KEY----- | ||||
| @@ -11,7 +11,6 @@ import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs" | ||||
| 	"github.com/drone/drone/pkg/remote" | ||||
| 	"github.com/drone/drone/pkg/server" | ||||
| 	"github.com/drone/drone/pkg/server/session" | ||||
|  | ||||
| 	log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" | ||||
| 	eventbus "github.com/drone/drone/pkg/bus/builtin" | ||||
| @@ -40,11 +39,6 @@ var conf = struct { | ||||
| 		key  string | ||||
| 	} | ||||
|  | ||||
| 	session struct { | ||||
| 		expiry string | ||||
| 		secret string | ||||
| 	} | ||||
|  | ||||
| 	docker struct { | ||||
| 		host string | ||||
| 		cert string | ||||
| @@ -76,8 +70,6 @@ func main() { | ||||
| 	flag.StringVar(&conf.server.addr, "server-addr", ":8080", "") | ||||
| 	flag.StringVar(&conf.server.cert, "server-cert", "", "") | ||||
| 	flag.StringVar(&conf.server.key, "server-key", "", "") | ||||
| 	flag.StringVar(&conf.session.expiry, "session-expiry", "", "") | ||||
| 	flag.StringVar(&conf.session.secret, "session-secret", "", "") | ||||
| 	flag.StringVar(&conf.remote.driver, "remote-driver", "github", "") | ||||
| 	flag.StringVar(&conf.remote.config, "remote-config", "https://github.com", "") | ||||
| 	flag.StringVar(&conf.database.driver, "database-driver", "sqlite3", "") | ||||
| @@ -98,7 +90,6 @@ func main() { | ||||
| 		panic(err) | ||||
| 	} | ||||
|  | ||||
| 	session := session.New(conf.remote.config) | ||||
| 	eventbus_ := eventbus.New() | ||||
| 	queue_ := queue.New() | ||||
| 	updater := runner.NewUpdater(eventbus_, store, remote) | ||||
| @@ -116,8 +107,7 @@ func main() { | ||||
| 	api.Use(server.SetDatastore(store)) | ||||
| 	api.Use(server.SetRemote(remote)) | ||||
| 	api.Use(server.SetQueue(queue_)) | ||||
| 	api.Use(server.SetSession(session)) | ||||
| 	api.Use(server.SetUser(session)) | ||||
| 	api.Use(server.SetUser()) | ||||
| 	api.Use(server.SetRunner(&runner_)) | ||||
| 	api.OPTIONS("/*path", func(c *gin.Context) {}) | ||||
|  | ||||
| @@ -129,9 +119,7 @@ func main() { | ||||
| 		user.PATCH("", server.PutUserCurr) | ||||
| 		user.GET("/feed", server.GetUserFeed) | ||||
| 		user.GET("/repos", server.GetUserRepos) | ||||
| 		user.GET("/tokens", server.GetUserTokens) | ||||
| 		user.POST("/tokens", server.PostToken) | ||||
| 		user.DELETE("/tokens/:label", server.DelToken) | ||||
| 		user.POST("/token", server.PostUserToken) | ||||
| 	} | ||||
|  | ||||
| 	users := api.Group("/users") | ||||
| @@ -199,7 +187,6 @@ func main() { | ||||
| 		auth.Use(server.SetHeaders()) | ||||
| 		auth.Use(server.SetDatastore(store)) | ||||
| 		auth.Use(server.SetRemote(remote)) | ||||
| 		auth.Use(server.SetSession(session)) | ||||
| 		auth.GET("", server.GetLogin) | ||||
| 		auth.POST("", server.GetLogin) | ||||
| 	} | ||||
|   | ||||
| @@ -1,12 +0,0 @@ | ||||
| package hash | ||||
|  | ||||
| import ( | ||||
| 	"crypto/sha256" | ||||
| 	"encoding/hex" | ||||
| ) | ||||
|  | ||||
| func New(text, salt string) string { | ||||
| 	hasher := sha256.New() | ||||
| 	hasher.Write([]byte(text + salt)) | ||||
| 	return hex.EncodeToString(hasher.Sum(nil)) | ||||
| } | ||||
| @@ -11,9 +11,9 @@ import ( | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/Bugagazavr/go-gitlab-client" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru" | ||||
| 	"github.com/drone/drone/pkg/hash" | ||||
| 	"github.com/drone/drone/pkg/oauth2" | ||||
| 	"github.com/drone/drone/pkg/remote" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| 	"github.com/drone/drone/pkg/utils/httputil" | ||||
| ) | ||||
| @@ -186,17 +186,18 @@ func (g *Gitlab) Netrc(u *common.User, r *common.Repo) (*common.Netrc, error) { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	netrc := &common.Netrc{} | ||||
| 	netrc.Machine = url_.Host | ||||
|  | ||||
| 	switch g.CloneMode { | ||||
| 	case "oauth": | ||||
| 		netrc.Login = "oauth2" | ||||
| 		netrc.Password = u.Token | ||||
| 	case "token": | ||||
| 		t := token.New(token.HookToken, r.FullName) | ||||
| 		netrc.Login = "drone-ci-token" | ||||
| 		netrc.Password = hash.New(r.FullName, r.Hash) | ||||
| 		netrc.Password, err = t.Sign(r.Hash) | ||||
| 	} | ||||
| 	netrc.Machine = url_.Host | ||||
| 	return netrc, nil | ||||
| 	return netrc, err | ||||
| } | ||||
|  | ||||
| // Activate activates a repository by adding a Post-commit hook and | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" | ||||
|  | ||||
| 	"github.com/drone/drone/pkg/hash" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| ) | ||||
|  | ||||
| // RedirectSha accepts a request to retvie a redirect | ||||
| @@ -79,8 +79,14 @@ func GetPullRequest(c *gin.Context) { | ||||
| 	store := ToDatastore(c) | ||||
| 	repo := ToRepo(c) | ||||
|  | ||||
| 	// get the token and verify the hook is authorized | ||||
| 	if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) { | ||||
| 	parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { | ||||
| 		return repo.Hash, nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		c.Fail(400, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if parsed.Text != repo.FullName { | ||||
| 		c.AbortWithStatus(403) | ||||
| 		return | ||||
| 	} | ||||
| @@ -118,8 +124,14 @@ func GetCommit(c *gin.Context) { | ||||
| 	repo := ToRepo(c) | ||||
| 	sha := c.Params.ByName("sha") | ||||
|  | ||||
| 	// get the token and verify the hook is authorized | ||||
| 	if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) { | ||||
| 	parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { | ||||
| 		return repo.Hash, nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		c.Fail(400, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if parsed.Text != repo.FullName { | ||||
| 		c.AbortWithStatus(403) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -6,8 +6,8 @@ import ( | ||||
|  | ||||
| 	log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" | ||||
| 	"github.com/drone/drone/pkg/hash" | ||||
| 	"github.com/drone/drone/pkg/queue" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| 	"github.com/drone/drone/pkg/utils/httputil" | ||||
| 	"github.com/drone/drone/pkg/yaml" | ||||
| @@ -56,8 +56,16 @@ func PostHook(c *gin.Context) { | ||||
| 	} | ||||
|  | ||||
| 	// get the token and verify the hook is authorized | ||||
| 	if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) { | ||||
| 		log.Errorf("invalid token sent with hook.") | ||||
| 	parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { | ||||
| 		return repo.Hash, nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		log.Errorf("failure to parse token from hook for %s. %s", repo.FullName, err) | ||||
| 		c.Fail(400, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if parsed.Text != repo.FullName { | ||||
| 		log.Errorf("failure to verify token from hook. Expected %s, got %s", repo.FullName, parsed.Text) | ||||
| 		c.AbortWithStatus(403) | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -7,7 +7,8 @@ import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar" | ||||
|  | ||||
| 	log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus" | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| 	"github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| // GetLogin accepts a request to authorize the user and to | ||||
| @@ -17,7 +18,6 @@ import ( | ||||
| //     GET /authorize | ||||
| // | ||||
| func GetLogin(c *gin.Context) { | ||||
| 	session := ToSession(c) | ||||
| 	remote := ToRemote(c) | ||||
| 	store := ToDatastore(c) | ||||
|  | ||||
| @@ -65,13 +65,13 @@ func GetLogin(c *gin.Context) { | ||||
| 		} | ||||
|  | ||||
| 		// create the user account | ||||
| 		u = &common.User{} | ||||
| 		u = &types.User{} | ||||
| 		u.Login = login.Login | ||||
| 		u.Token = login.Token | ||||
| 		u.Secret = login.Secret | ||||
| 		u.Email = login.Email | ||||
| 		u.Avatar = login.Avatar | ||||
| 		u.Hash = common.GenerateToken() | ||||
| 		u.Hash = types.GenerateToken() | ||||
|  | ||||
| 		// insert the user into the database | ||||
| 		if err := store.AddUser(u); err != nil { | ||||
| @@ -106,12 +106,9 @@ func GetLogin(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	token := &common.Token{ | ||||
| 		Kind:   common.TokenSess, | ||||
| 		Login:  u.Login, | ||||
| 		Issued: time.Now().UTC().Unix(), | ||||
| 	} | ||||
| 	tokenstr, err := session.GenerateToken(token) | ||||
| 	exp := time.Now().Add(time.Hour * 72).Unix() | ||||
| 	token := token.New(token.SessToken, u.Login) | ||||
| 	tokenstr, err := token.SignExpires(u.Hash, exp) | ||||
| 	if err != nil { | ||||
| 		log.Errorf("cannot create token for %s. %s", u.Login, err) | ||||
| 		c.Redirect(303, "/login#error=internal_error") | ||||
|   | ||||
| @@ -10,8 +10,8 @@ import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2" | ||||
|  | ||||
| 	"github.com/drone/drone/pkg/hash" | ||||
| 	"github.com/drone/drone/pkg/remote" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| 	"github.com/drone/drone/pkg/utils/httputil" | ||||
| 	"github.com/drone/drone/pkg/utils/sshutil" | ||||
| @@ -208,10 +208,18 @@ func PostRepo(c *gin.Context) { | ||||
| 		r.FullName, | ||||
| 	) | ||||
|  | ||||
| 	// crates the jwt token used to verify the repository | ||||
| 	t := token.New(token.HookToken, r.FullName) | ||||
| 	sig, err := t.Sign(r.Hash) | ||||
| 	if err != nil { | ||||
| 		c.Fail(500, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	link := fmt.Sprintf( | ||||
| 		"%s/api/hook?access_token=%s", | ||||
| 		httputil.GetURL(c.Request), | ||||
| 		hash.New(r.FullName, r.Hash), | ||||
| 		sig, | ||||
| 	) | ||||
|  | ||||
| 	// generate an RSA key and add to the repo | ||||
|   | ||||
| @@ -10,8 +10,8 @@ import ( | ||||
| 	"github.com/drone/drone/pkg/queue" | ||||
| 	"github.com/drone/drone/pkg/remote" | ||||
| 	"github.com/drone/drone/pkg/runner" | ||||
| 	"github.com/drone/drone/pkg/server/session" | ||||
| 	"github.com/drone/drone/pkg/store" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| @@ -103,10 +103,6 @@ func ToDatastore(c *gin.Context) store.Store { | ||||
| 	return c.MustGet("datastore").(store.Store) | ||||
| } | ||||
|  | ||||
| func ToSession(c *gin.Context) session.Session { | ||||
| 	return c.MustGet("session").(session.Session) | ||||
| } | ||||
|  | ||||
| func SetDatastore(ds store.Store) gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		c.Set("datastore", ds) | ||||
| @@ -114,44 +110,24 @@ func SetDatastore(ds store.Store) gin.HandlerFunc { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func SetSession(s session.Session) gin.HandlerFunc { | ||||
| func SetUser() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		c.Set("session", s) | ||||
| 		c.Next() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func SetUser(s session.Session) gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		ds := ToDatastore(c) | ||||
| 		token := s.GetLogin(c.Request) | ||||
| 		if token == nil || len(token.Login) == 0 { | ||||
| 			c.Next() | ||||
| 			return | ||||
| 		} | ||||
| 		var store = ToDatastore(c) | ||||
| 		var user *common.User | ||||
|  | ||||
| 		user, err := ds.UserLogin(token.Login) | ||||
| 		if err == nil { | ||||
| 		_, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) { | ||||
| 			var err error | ||||
| 			user, err = store.UserLogin(t.Text) | ||||
| 			if err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 			return user.Hash, nil | ||||
| 		}) | ||||
|  | ||||
| 		if err == nil && user != nil && user.ID != 0 { | ||||
| 			c.Set("user", user) | ||||
| 		} | ||||
|  | ||||
| 		// if session token we can proceed, otherwise | ||||
| 		// we should validate the token hasn't been revoked | ||||
| 		switch token.Kind { | ||||
| 		case 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.TokenLabel(user, token.Label) | ||||
| 		if err != nil || t.Issued != token.Issued { | ||||
| 			c.AbortWithStatus(403) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		c.Next() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,107 +0,0 @@ | ||||
| package session | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| type Session interface { | ||||
| 	GenerateToken(*common.Token) (string, error) | ||||
| 	GetLogin(*http.Request) *common.Token | ||||
| } | ||||
|  | ||||
| type session struct { | ||||
| 	secret []byte | ||||
| 	expire time.Duration | ||||
| } | ||||
|  | ||||
| func New(rand string) Session { | ||||
| 	secret := []byte(rand) | ||||
| 	expire := time.Hour * 72 | ||||
| 	return &session{ | ||||
| 		secret: secret, | ||||
| 		expire: expire, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GenerateToken generates a JWT token for the user session | ||||
| // that can be appended to the #access_token segment to | ||||
| // facilitate client-based OAuth2. | ||||
| func (s *session) GenerateToken(t *common.Token) (string, error) { | ||||
| 	token := jwt.New(jwt.GetSigningMethod("HS256")) | ||||
| 	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) | ||||
| } | ||||
|  | ||||
| // GetLogin gets the currently authenticated user for the | ||||
| // http.Request. The user details will be stored as either | ||||
| // a simple API token or JWT bearer token. | ||||
| func (s *session) GetLogin(r *http.Request) *common.Token { | ||||
| 	t := getToken(r) | ||||
| 	if len(t) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	claims := getClaims(t, s.secret) | ||||
| 	if claims == nil || claims["user"] == nil || claims["date"] == nil || claims["label"] == nil || claims["kind"] == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	token := &common.Token{ | ||||
| 		Kind:   claims["kind"].(string), | ||||
| 		Login:  claims["user"].(string), | ||||
| 		Label:  claims["label"].(string), | ||||
| 		Issued: int64(claims["date"].(float64)), | ||||
| 	} | ||||
| 	if token.Kind != common.TokenSess { | ||||
| 		return token | ||||
| 	} | ||||
|  | ||||
| 	if time.Unix(token.Issued, 0).Add(s.expire).Before(time.Now()) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return token | ||||
| } | ||||
|  | ||||
| // getToken is a helper function that extracts the token | ||||
| // from the http.Request. | ||||
| func getToken(r *http.Request) string { | ||||
| 	token := getTokenHeader(r) | ||||
| 	if len(token) == 0 { | ||||
| 		token = getTokenParam(r) | ||||
| 	} | ||||
| 	return token | ||||
| } | ||||
|  | ||||
| // getTokenHeader parses the JWT token value from | ||||
| // the http Authorization header. | ||||
| func getTokenHeader(r *http.Request) string { | ||||
| 	var tokenstr = r.Header.Get("Authorization") | ||||
| 	fmt.Sscanf(tokenstr, "Bearer %s", &tokenstr) | ||||
| 	return tokenstr | ||||
| } | ||||
|  | ||||
| // getTokenParam parses the JWT token value from | ||||
| // the http Request's query parameter. | ||||
| func getTokenParam(r *http.Request) string { | ||||
| 	return r.FormValue("access_token") | ||||
| } | ||||
|  | ||||
| // getClaims is a helper function that extracts the token | ||||
| // claims from the JWT token string. | ||||
| func getClaims(token string, secret []byte) map[string]interface{} { | ||||
| 	t, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) { | ||||
| 		return secret, nil | ||||
| 	}) | ||||
| 	if err != nil || !t.Valid { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return t.Claims | ||||
| } | ||||
| @@ -1,68 +0,0 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"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/binding" | ||||
|  | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| // POST /api/user/tokens | ||||
| func PostToken(c *gin.Context) { | ||||
| 	sess := ToSession(c) | ||||
| 	store := ToDatastore(c) | ||||
| 	user := ToUser(c) | ||||
|  | ||||
| 	in := &common.Token{} | ||||
| 	if !c.BindWith(in, binding.JSON) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	token := &common.Token{} | ||||
| 	token.Label = in.Label | ||||
| 	token.UserID = user.ID | ||||
| 	// token.Repos = in.Repos | ||||
| 	// token.Scopes = in.Scopes | ||||
| 	token.Login = user.Login | ||||
| 	token.Kind = common.TokenUser | ||||
| 	token.Issued = time.Now().UTC().Unix() | ||||
|  | ||||
| 	err := store.AddToken(token) | ||||
| 	if err != nil { | ||||
| 		c.Fail(500, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	jwt, err := sess.GenerateToken(token) | ||||
| 	if err != nil { | ||||
| 		c.Fail(400, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.JSON(200, struct { | ||||
| 		*common.Token | ||||
| 		Hash string `json:"hash"` | ||||
| 	}{token, jwt}) | ||||
| } | ||||
|  | ||||
| // DELETE /api/user/tokens/:label | ||||
| func DelToken(c *gin.Context) { | ||||
| 	store := ToDatastore(c) | ||||
| 	user := ToUser(c) | ||||
| 	label := c.Params.ByName("label") | ||||
|  | ||||
| 	token, err := store.TokenLabel(user, label) | ||||
| 	if err != nil { | ||||
| 		c.Fail(404, err) | ||||
| 		return | ||||
| 	} | ||||
| 	err = store.DelToken(token) | ||||
| 	if err != nil { | ||||
| 		c.Fail(400, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	c.Writer.WriteHeader(200) | ||||
| } | ||||
| @@ -1,120 +0,0 @@ | ||||
| package server | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"database/sql" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| 	. "github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/stretchr/testify/mock" | ||||
| 	"github.com/drone/drone/pkg/server/recorder" | ||||
| 	"github.com/drone/drone/pkg/server/session" | ||||
| 	"github.com/drone/drone/pkg/store/mock" | ||||
| 	"github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| var createTests = []struct { | ||||
| 	inLabel  string | ||||
| 	inBody   string | ||||
| 	storeErr error | ||||
| 	outCode  int | ||||
| 	outKind  string | ||||
| }{ | ||||
| 	{"", `{}`, sql.ErrNoRows, 500, ""}, | ||||
| 	{"app1", `{"label": "app1"}`, nil, 200, types.TokenUser}, | ||||
| 	{"app2", `{"label": "app2"}`, nil, 200, types.TokenUser}, | ||||
| } | ||||
|  | ||||
| var deleteTests = []struct { | ||||
| 	inLabel       string | ||||
| 	errTokenLabel error | ||||
| 	errDelToken   error | ||||
| 	outCode       int | ||||
| 	outToken      *types.Token | ||||
| }{ | ||||
| 	{"app1", sql.ErrNoRows, nil, 404, &types.Token{}}, | ||||
| 	{"app2", nil, sql.ErrNoRows, 400, &types.Token{Label: "app2"}}, | ||||
| 	{"app3", nil, nil, 200, &types.Token{Label: "app2"}}, | ||||
| } | ||||
|  | ||||
| func TestToken(t *testing.T) { | ||||
| 	store := new(mocks.Store) | ||||
|  | ||||
| 	g := Goblin(t) | ||||
| 	g.Describe("Token", func() { | ||||
|  | ||||
| 		// POST /api/user/tokens | ||||
| 		g.It("should create tokens", func() { | ||||
| 			for _, test := range createTests { | ||||
| 				rw := recorder.New() | ||||
| 				ctx := gin.Context{Engine: gin.Default(), Writer: rw} | ||||
| 				body := bytes.NewBufferString(test.inBody) | ||||
| 				ctx.Request, _ = http.NewRequest("POST", "/api/user/tokens", body) | ||||
|  | ||||
| 				ctx.Set("datastore", store) | ||||
| 				ctx.Set("user", &types.User{Login: "Freya"}) | ||||
|  | ||||
| 				ctx.Set("session", session.New("Otto")) | ||||
|  | ||||
| 				// prepare the mock | ||||
| 				store.On("AddToken", mock.AnythingOfType("*types.Token")).Return(test.storeErr).Once() | ||||
| 				PostToken(&ctx) | ||||
|  | ||||
| 				g.Assert(rw.Code).Equal(test.outCode) | ||||
| 				if test.outCode != 200 { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				var respjson map[string]interface{} | ||||
| 				json.Unmarshal(rw.Body.Bytes(), &respjson) | ||||
| 				g.Assert(respjson["kind"]).Equal(types.TokenUser) | ||||
| 				g.Assert(respjson["label"]).Equal(test.inLabel) | ||||
|  | ||||
| 				// this is probably going too far... maybe just validate hash is not empty? | ||||
| 				jwt.Parse(respjson["hash"].(string), func(token *jwt.Token) (interface{}, error) { | ||||
| 					_, ok := token.Method.(*jwt.SigningMethodHMAC) | ||||
| 					g.Assert(ok).IsTrue() | ||||
| 					g.Assert(token.Claims["label"]).Equal(test.inLabel) | ||||
| 					return nil, nil | ||||
| 				}) | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		// DELETE /api/user/tokens/:label | ||||
| 		g.It("should delete tokens", func() { | ||||
| 			for _, test := range deleteTests { | ||||
| 				rw := recorder.New() | ||||
| 				ctx := gin.Context{Engine: gin.Default(), Writer: rw} | ||||
| 				ctx.Params = append(ctx.Params, gin.Param{Key: "label", Value: test.inLabel}) | ||||
|  | ||||
| 				ctx.Set("datastore", store) | ||||
| 				ctx.Set("user", &types.User{Login: "Freya"}) | ||||
| 				ctx.Set("session", session.New("Otto")) | ||||
|  | ||||
| 				// prepare the mock | ||||
| 				store.On("TokenLabel", mock.AnythingOfType("*types.User"), test.inLabel).Return(test.outToken, test.errTokenLabel).Once() | ||||
|  | ||||
| 				if test.errTokenLabel == nil { | ||||
| 					// we don't need this expectation if we error on our first | ||||
| 					store.On("DelToken", mock.AnythingOfType("*types.Token")).Return(test.errDelToken).Once() | ||||
| 				} | ||||
| 				fmt.Println(test) | ||||
| 				DelToken(&ctx) | ||||
|  | ||||
| 				g.Assert(rw.Code).Equal(test.outCode) | ||||
| 				if test.outCode != 200 { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				var respjson map[string]interface{} | ||||
| 				json.Unmarshal(rw.Body.Bytes(), &respjson) | ||||
| 				fmt.Println(rw.Code, respjson) | ||||
| 			} | ||||
| 		}) | ||||
| 	}) | ||||
| } | ||||
| @@ -5,7 +5,8 @@ import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar" | ||||
|  | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| 	"github.com/drone/drone/pkg/token" | ||||
| 	"github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| // GetUserCurr accepts a request to retrieve the | ||||
| @@ -27,7 +28,7 @@ func PutUserCurr(c *gin.Context) { | ||||
| 	store := ToDatastore(c) | ||||
| 	user := ToUser(c) | ||||
|  | ||||
| 	in := &common.User{} | ||||
| 	in := &types.User{} | ||||
| 	if !c.BindWith(in, binding.JSON) { | ||||
| 		return | ||||
| 	} | ||||
| @@ -76,19 +77,15 @@ func GetUserFeed(c *gin.Context) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // GetUserTokens accepts a request to get the currently | ||||
| // authenticated user's token list from the datastore, | ||||
| // encoded and returned in JSON format. | ||||
| // | ||||
| //     GET /api/user/tokens | ||||
| // | ||||
| func GetUserTokens(c *gin.Context) { | ||||
| 	store := ToDatastore(c) | ||||
| // POST /api/user/token | ||||
| func PostUserToken(c *gin.Context) { | ||||
| 	user := ToUser(c) | ||||
| 	tokens, err := store.TokenList(user) | ||||
|  | ||||
| 	t := token.New(token.UserToken, user.Login) | ||||
| 	s, err := t.Sign(user.Hash) | ||||
| 	if err != nil { | ||||
| 		c.Fail(400, err) | ||||
| 		c.Fail(500, err) | ||||
| 	} else { | ||||
| 		c.JSON(200, &tokens) | ||||
| 		c.String(200, "application/jwt", s) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin/binding" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar" | ||||
|  | ||||
| 	common "github.com/drone/drone/pkg/types" | ||||
| 	"github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| // GetUsers accepts a request to retrieve all users | ||||
| @@ -32,9 +32,13 @@ func GetUsers(c *gin.Context) { | ||||
| func PostUser(c *gin.Context) { | ||||
| 	store := ToDatastore(c) | ||||
| 	name := c.Params.ByName("name") | ||||
| 	user := &common.User{Login: name} | ||||
| 	user := &types.User{Login: name} | ||||
| 	user.Token = c.Request.FormValue("token") | ||||
| 	user.Secret = c.Request.FormValue("secret") | ||||
| 	user.Hash = c.Request.FormValue("hash") | ||||
| 	if len(user.Hash) == 0 { | ||||
| 		user.Hash = types.GenerateToken() | ||||
| 	} | ||||
| 	if err := store.AddUser(user); err != nil { | ||||
| 		c.Fail(400, err) | ||||
| 	} else { | ||||
| @@ -75,7 +79,7 @@ func PutUser(c *gin.Context) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	in := &common.User{} | ||||
| 	in := &types.User{} | ||||
| 	if !c.BindWith(in, binding.JSON) { | ||||
| 		return | ||||
| 	} | ||||
|   | ||||
| @@ -8,10 +8,10 @@ import ( | ||||
| 	"github.com/drone/drone/pkg/store/builtin/migrate" | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/BurntSushi/migration" | ||||
| 	_ "github.com/drone/drone/Godeps/_workspace/src/github.com/go-sql-driver/mysql" | ||||
| 	_ "github.com/drone/drone/Godeps/_workspace/src/github.com/lib/pq" | ||||
| 	_ "github.com/drone/drone/Godeps/_workspace/src/github.com/mattn/go-sqlite3" | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/russross/meddler" | ||||
| 	_ "github.com/drone/drone/Godeps/_workspace/src/github.com/go-sql-driver/mysql" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| @@ -94,7 +94,6 @@ func New(db *sql.DB) store.Store { | ||||
| 		*Jobstore | ||||
| 		*Blobstore | ||||
| 		*Starstore | ||||
| 		*Tokenstore | ||||
| 		*Agentstore | ||||
| 	}{ | ||||
| 		NewUserstore(db), | ||||
| @@ -103,7 +102,6 @@ func New(db *sql.DB) store.Store { | ||||
| 		NewJobstore(db), | ||||
| 		NewBlobstore(db), | ||||
| 		NewStarstore(db), | ||||
| 		NewTokenstore(db), | ||||
| 		NewAgentstore(db), | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,43 +0,0 @@ | ||||
| package builtin | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
|  | ||||
| 	"github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| type Tokenstore struct { | ||||
| 	*sql.DB | ||||
| } | ||||
|  | ||||
| func NewTokenstore(db *sql.DB) *Tokenstore { | ||||
| 	return &Tokenstore{db} | ||||
| } | ||||
|  | ||||
| // Token returns a token by ID. | ||||
| func (db *Tokenstore) Token(id int64) (*types.Token, error) { | ||||
| 	return getToken(db, rebind(stmtTokenSelect), id) | ||||
| } | ||||
|  | ||||
| // TokenLabel returns a token by label | ||||
| func (db *Tokenstore) TokenLabel(user *types.User, label string) (*types.Token, error) { | ||||
| 	return getToken(db, rebind(stmtTokenSelectTokenUserLabel), user.ID, label) | ||||
| } | ||||
|  | ||||
| // TokenList returns a list of all user tokens. | ||||
| func (db *Tokenstore) TokenList(user *types.User) ([]*types.Token, error) { | ||||
| 	return getTokens(db, rebind(stmtTokenSelectTokenUserId), user.ID) | ||||
| } | ||||
|  | ||||
| // AddToken inserts a new token into the datastore. | ||||
| // If the token label already exists for the user | ||||
| // an error is returned. | ||||
| func (db *Tokenstore) AddToken(token *types.Token) error { | ||||
| 	return createToken(db, rebind(stmtTokenInsert), token) | ||||
| } | ||||
|  | ||||
| // DelToken removes the DelToken from the datastore. | ||||
| func (db *Tokenstore) DelToken(token *types.Token) error { | ||||
| 	var _, err = db.Exec(rebind(stmtTokenDelete), token.ID) | ||||
| 	return err | ||||
| } | ||||
| @@ -1,255 +0,0 @@ | ||||
| package builtin | ||||
|  | ||||
| // DO NOT EDIT | ||||
| // code generated by go:generate | ||||
|  | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"encoding/json" | ||||
|  | ||||
| 	. "github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| var _ = json.Marshal | ||||
|  | ||||
| // generic database interface, matching both *sql.Db and *sql.Tx | ||||
| type tokenDB interface { | ||||
| 	Exec(query string, args ...interface{}) (sql.Result, error) | ||||
| 	Query(query string, args ...interface{}) (*sql.Rows, error) | ||||
| 	QueryRow(query string, args ...interface{}) *sql.Row | ||||
| } | ||||
|  | ||||
| func getToken(db tokenDB, query string, args ...interface{}) (*Token, error) { | ||||
| 	row := db.QueryRow(query, args...) | ||||
| 	return scanToken(row) | ||||
| } | ||||
|  | ||||
| func getTokens(db tokenDB, query string, args ...interface{}) ([]*Token, error) { | ||||
| 	rows, err := db.Query(query, args...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer rows.Close() | ||||
| 	return scanTokens(rows) | ||||
| } | ||||
|  | ||||
| func createToken(db tokenDB, query string, v *Token) error { | ||||
| 	var v0 int64 | ||||
| 	var v1 string | ||||
| 	var v2 string | ||||
| 	var v3 int64 | ||||
| 	var v4 int64 | ||||
| 	v0 = v.UserID | ||||
| 	v1 = v.Kind | ||||
| 	v2 = v.Label | ||||
| 	v3 = v.Expiry | ||||
| 	v4 = v.Issued | ||||
|  | ||||
| 	res, err := db.Exec(query, | ||||
| 		&v0, | ||||
| 		&v1, | ||||
| 		&v2, | ||||
| 		&v3, | ||||
| 		&v4, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	v.ID, err = res.LastInsertId() | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func updateToken(db tokenDB, query string, v *Token) error { | ||||
| 	var v0 int64 | ||||
| 	var v1 int64 | ||||
| 	var v2 string | ||||
| 	var v3 string | ||||
| 	var v4 int64 | ||||
| 	var v5 int64 | ||||
| 	v0 = v.ID | ||||
| 	v1 = v.UserID | ||||
| 	v2 = v.Kind | ||||
| 	v3 = v.Label | ||||
| 	v4 = v.Expiry | ||||
| 	v5 = v.Issued | ||||
|  | ||||
| 	_, err := db.Exec(query, | ||||
| 		&v1, | ||||
| 		&v2, | ||||
| 		&v3, | ||||
| 		&v4, | ||||
| 		&v5, | ||||
| 		&v0, | ||||
| 	) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func scanToken(row *sql.Row) (*Token, error) { | ||||
| 	var v0 int64 | ||||
| 	var v1 int64 | ||||
| 	var v2 string | ||||
| 	var v3 string | ||||
| 	var v4 int64 | ||||
| 	var v5 int64 | ||||
|  | ||||
| 	err := row.Scan( | ||||
| 		&v0, | ||||
| 		&v1, | ||||
| 		&v2, | ||||
| 		&v3, | ||||
| 		&v4, | ||||
| 		&v5, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	v := &Token{} | ||||
| 	v.ID = v0 | ||||
| 	v.UserID = v1 | ||||
| 	v.Kind = v2 | ||||
| 	v.Label = v3 | ||||
| 	v.Expiry = v4 | ||||
| 	v.Issued = v5 | ||||
|  | ||||
| 	return v, nil | ||||
| } | ||||
|  | ||||
| func scanTokens(rows *sql.Rows) ([]*Token, error) { | ||||
| 	var err error | ||||
| 	var vv []*Token | ||||
| 	for rows.Next() { | ||||
| 		var v0 int64 | ||||
| 		var v1 int64 | ||||
| 		var v2 string | ||||
| 		var v3 string | ||||
| 		var v4 int64 | ||||
| 		var v5 int64 | ||||
| 		err = rows.Scan( | ||||
| 			&v0, | ||||
| 			&v1, | ||||
| 			&v2, | ||||
| 			&v3, | ||||
| 			&v4, | ||||
| 			&v5, | ||||
| 		) | ||||
| 		if err != nil { | ||||
| 			return vv, err | ||||
| 		} | ||||
|  | ||||
| 		v := &Token{} | ||||
| 		v.ID = v0 | ||||
| 		v.UserID = v1 | ||||
| 		v.Kind = v2 | ||||
| 		v.Label = v3 | ||||
| 		v.Expiry = v4 | ||||
| 		v.Issued = v5 | ||||
| 		vv = append(vv, v) | ||||
| 	} | ||||
| 	return vv, rows.Err() | ||||
| } | ||||
|  | ||||
| const stmtTokenSelectList = ` | ||||
| SELECT | ||||
|  token_id | ||||
| ,token_user_id | ||||
| ,token_kind | ||||
| ,token_label | ||||
| ,token_expiry | ||||
| ,token_issued | ||||
| FROM tokens | ||||
| ` | ||||
|  | ||||
| const stmtTokenSelectRange = ` | ||||
| SELECT | ||||
|  token_id | ||||
| ,token_user_id | ||||
| ,token_kind | ||||
| ,token_label | ||||
| ,token_expiry | ||||
| ,token_issued | ||||
| FROM tokens | ||||
| LIMIT ? OFFSET ? | ||||
| ` | ||||
|  | ||||
| const stmtTokenSelect = ` | ||||
| SELECT | ||||
|  token_id | ||||
| ,token_user_id | ||||
| ,token_kind | ||||
| ,token_label | ||||
| ,token_expiry | ||||
| ,token_issued | ||||
| FROM tokens | ||||
| WHERE token_id = ? | ||||
| ` | ||||
|  | ||||
| const stmtTokenSelectTokenUserId = ` | ||||
| SELECT | ||||
|  token_id | ||||
| ,token_user_id | ||||
| ,token_kind | ||||
| ,token_label | ||||
| ,token_expiry | ||||
| ,token_issued | ||||
| FROM tokens | ||||
| WHERE token_user_id = ? | ||||
| ` | ||||
|  | ||||
| const stmtTokenSelectTokenUserLabel = ` | ||||
| SELECT | ||||
|  token_id | ||||
| ,token_user_id | ||||
| ,token_kind | ||||
| ,token_label | ||||
| ,token_expiry | ||||
| ,token_issued | ||||
| FROM tokens | ||||
| WHERE token_user_id = ? | ||||
| AND token_label = ? | ||||
| ` | ||||
|  | ||||
| const stmtTokenInsert = ` | ||||
| INSERT INTO tokens ( | ||||
|  token_user_id | ||||
| ,token_kind | ||||
| ,token_label | ||||
| ,token_expiry | ||||
| ,token_issued | ||||
| ) VALUES (?,?,?,?,?); | ||||
| ` | ||||
|  | ||||
| const stmtTokenUpdate = ` | ||||
| UPDATE tokens SET | ||||
|  token_user_id = ? | ||||
| ,token_kind = ? | ||||
| ,token_label = ? | ||||
| ,token_expiry = ? | ||||
| ,token_issued = ? | ||||
| WHERE token_id = ? | ||||
| ` | ||||
|  | ||||
| const stmtTokenDelete = ` | ||||
| DELETE FROM tokens | ||||
| WHERE token_id = ? | ||||
| ` | ||||
|  | ||||
| const stmtTokenTable = ` | ||||
| CREATE TABLE IF NOT EXISTS tokens ( | ||||
|  token_id	INTEGER PRIMARY KEY AUTOINCREMENT | ||||
| ,token_user_id	INTEGER | ||||
| ,token_kind	VARCHAR | ||||
| ,token_label	VARCHAR | ||||
| ,token_expiry	INTEGER | ||||
| ,token_issued	INTEGER | ||||
| ); | ||||
| ` | ||||
|  | ||||
| const stmtTokenTokenUserIdIndex = ` | ||||
| CREATE INDEX IF NOT EXISTS ix_token_user_id ON tokens (token_user_id); | ||||
| ` | ||||
|  | ||||
| const stmtTokenTokenUserLabelIndex = ` | ||||
| CREATE UNIQUE INDEX IF NOT EXISTS ux_token_user_label ON tokens (token_user_id,token_label); | ||||
| ` | ||||
| @@ -1,149 +0,0 @@ | ||||
| package builtin | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin" | ||||
| 	"github.com/drone/drone/pkg/types" | ||||
| ) | ||||
|  | ||||
| func TestTokenstore(t *testing.T) { | ||||
| 	db := mustConnectTest() | ||||
| 	ts := NewTokenstore(db) | ||||
| 	defer db.Close() | ||||
|  | ||||
| 	g := goblin.Goblin(t) | ||||
| 	g.Describe("Tokenstore", func() { | ||||
|  | ||||
| 		// before each test be sure to purge the package | ||||
| 		// table data from the database. | ||||
| 		g.BeforeEach(func() { | ||||
| 			db.Exec("DELETE FROM tokens") | ||||
| 		}) | ||||
|  | ||||
| 		g.It("Should Add a new Token", func() { | ||||
| 			token := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			err := ts.AddToken(&token) | ||||
| 			g.Assert(err == nil).IsTrue() | ||||
| 			g.Assert(token.ID != 0).IsTrue() | ||||
| 		}) | ||||
|  | ||||
| 		g.It("Should get a Token", func() { | ||||
| 			token := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			err1 := ts.AddToken(&token) | ||||
| 			gettoken, err2 := ts.Token(token.ID) | ||||
| 			g.Assert(err1 == nil).IsTrue() | ||||
| 			g.Assert(err2 == nil).IsTrue() | ||||
| 			g.Assert(token.ID).Equal(gettoken.ID) | ||||
| 			g.Assert(token.Label).Equal(gettoken.Label) | ||||
| 			g.Assert(token.Kind).Equal(gettoken.Kind) | ||||
| 			g.Assert(token.Issued).Equal(gettoken.Issued) | ||||
| 			g.Assert(token.Expiry).Equal(gettoken.Expiry) | ||||
| 		}) | ||||
|  | ||||
| 		g.It("Should Get a Token By Label", func() { | ||||
| 			token := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			err1 := ts.AddToken(&token) | ||||
| 			gettoken, err2 := ts.TokenLabel(&types.User{ID: 1}, "foo") | ||||
| 			g.Assert(err1 == nil).IsTrue() | ||||
| 			g.Assert(err2 == nil).IsTrue() | ||||
| 			g.Assert(token.ID).Equal(gettoken.ID) | ||||
| 			g.Assert(token.Label).Equal(gettoken.Label) | ||||
| 			g.Assert(token.Kind).Equal(gettoken.Kind) | ||||
| 			g.Assert(token.Issued).Equal(gettoken.Issued) | ||||
| 			g.Assert(token.Expiry).Equal(gettoken.Expiry) | ||||
| 		}) | ||||
|  | ||||
| 		g.It("Should Enforce Unique Token Label", func() { | ||||
| 			token1 := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			token2 := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			err1 := ts.AddToken(&token1) | ||||
| 			err2 := ts.AddToken(&token2) | ||||
| 			g.Assert(err1 == nil).IsTrue() | ||||
| 			g.Assert(err2 == nil).IsFalse() | ||||
| 		}) | ||||
|  | ||||
| 		g.It("Should Get a User Token List", func() { | ||||
| 			token1 := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "bar", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			token2 := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			token3 := types.Token{ | ||||
| 				UserID: 2, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			ts.AddToken(&token1) | ||||
| 			ts.AddToken(&token2) | ||||
| 			ts.AddToken(&token3) | ||||
| 			tokens, err := ts.TokenList(&types.User{ID: 1}) | ||||
| 			g.Assert(err == nil).IsTrue() | ||||
| 			g.Assert(len(tokens)).Equal(2) | ||||
| 			g.Assert(tokens[0].ID).Equal(token1.ID) | ||||
| 			g.Assert(tokens[0].Label).Equal(token1.Label) | ||||
| 			g.Assert(tokens[0].Kind).Equal(token1.Kind) | ||||
| 			g.Assert(tokens[0].Issued).Equal(token1.Issued) | ||||
| 			g.Assert(tokens[0].Expiry).Equal(token1.Expiry) | ||||
| 		}) | ||||
|  | ||||
| 		g.It("Should Del a Token", func() { | ||||
| 			token := types.Token{ | ||||
| 				UserID: 1, | ||||
| 				Label:  "foo", | ||||
| 				Kind:   types.TokenUser, | ||||
| 				Issued: time.Now().Unix(), | ||||
| 				Expiry: time.Now().Unix() + 1000, | ||||
| 			} | ||||
| 			ts.AddToken(&token) | ||||
| 			_, err1 := ts.Token(token.ID) | ||||
| 			err2 := ts.DelToken(&token) | ||||
| 			_, err3 := ts.Token(token.ID) | ||||
| 			g.Assert(err1 == nil).IsTrue() | ||||
| 			g.Assert(err2 == nil).IsTrue() | ||||
| 			g.Assert(err3 == nil).IsFalse() | ||||
| 		}) | ||||
| 	}) | ||||
| } | ||||
| @@ -84,53 +84,6 @@ func (m *Store) DelUser(_a0 *types.User) error { | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
| func (m *Store) Token(_a0 int64) (*types.Token, error) { | ||||
| 	ret := m.Called(_a0) | ||||
|  | ||||
| 	var r0 *types.Token | ||||
| 	if ret.Get(0) != nil { | ||||
| 		r0 = ret.Get(0).(*types.Token) | ||||
| 	} | ||||
| 	r1 := ret.Error(1) | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
| func (m *Store) TokenLabel(_a0 *types.User, _a1 string) (*types.Token, error) { | ||||
| 	ret := m.Called(_a0, _a1) | ||||
|  | ||||
| 	var r0 *types.Token | ||||
| 	if ret.Get(0) != nil { | ||||
| 		r0 = ret.Get(0).(*types.Token) | ||||
| 	} | ||||
| 	r1 := ret.Error(1) | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
| func (m *Store) TokenList(_a0 *types.User) ([]*types.Token, error) { | ||||
| 	ret := m.Called(_a0) | ||||
|  | ||||
| 	var r0 []*types.Token | ||||
| 	if ret.Get(0) != nil { | ||||
| 		r0 = ret.Get(0).([]*types.Token) | ||||
| 	} | ||||
| 	r1 := ret.Error(1) | ||||
|  | ||||
| 	return r0, r1 | ||||
| } | ||||
| func (m *Store) AddToken(_a0 *types.Token) error { | ||||
| 	ret := m.Called(_a0) | ||||
|  | ||||
| 	r0 := ret.Error(0) | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
| func (m *Store) DelToken(_a0 *types.Token) error { | ||||
| 	ret := m.Called(_a0) | ||||
|  | ||||
| 	r0 := ret.Error(0) | ||||
|  | ||||
| 	return r0 | ||||
| } | ||||
| func (m *Store) Starred(_a0 *types.User, _a1 *types.Repo) (bool, error) { | ||||
| 	ret := m.Called(_a0, _a1) | ||||
|  | ||||
|   | ||||
| @@ -70,25 +70,6 @@ type Store interface { | ||||
|  | ||||
| 	// | ||||
|  | ||||
| 	// Token returns a token by ID. | ||||
| 	Token(int64) (*types.Token, error) | ||||
|  | ||||
| 	// TokenLabel returns a token by label | ||||
| 	TokenLabel(*types.User, string) (*types.Token, error) | ||||
|  | ||||
| 	// TokenList returns a list of all user tokens. | ||||
| 	TokenList(*types.User) ([]*types.Token, error) | ||||
|  | ||||
| 	// AddToken inserts a new token into the datastore. | ||||
| 	// If the token label already exists for the user | ||||
| 	// an error is returned. | ||||
| 	AddToken(*types.Token) error | ||||
|  | ||||
| 	// DelToken removes the DelToken from the datastore. | ||||
| 	DelToken(*types.Token) error | ||||
|  | ||||
| 	// | ||||
|  | ||||
| 	// Starred returns true if the user starred | ||||
| 	// the given repository. | ||||
| 	Starred(*types.User, *types.Repo) (bool, error) | ||||
|   | ||||
							
								
								
									
										98
									
								
								pkg/token/token.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								pkg/token/token.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| package token | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go" | ||||
| ) | ||||
|  | ||||
| type SecretFunc func(*Token) (string, error) | ||||
|  | ||||
| const ( | ||||
| 	UserToken = "user" | ||||
| 	SessToken = "sess" | ||||
| 	HookToken = "hook" | ||||
| ) | ||||
|  | ||||
| // Default algorithm used to sign JWT tokens. | ||||
| const SignerAlgo = "HS256" | ||||
|  | ||||
| type Token struct { | ||||
| 	Kind string | ||||
| 	Text string | ||||
| } | ||||
|  | ||||
| // Parse parses | ||||
| func Parse(raw string, fn SecretFunc) (*Token, error) { | ||||
| 	token := &Token{} | ||||
| 	parsed, err := jwt.Parse(raw, keyFunc(token, fn)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !parsed.Valid { | ||||
| 		return nil, jwt.ValidationError{} | ||||
| 	} | ||||
| 	return token, nil | ||||
| } | ||||
|  | ||||
| func ParseRequest(req *http.Request, fn SecretFunc) (*Token, error) { | ||||
| 	token := &Token{} | ||||
| 	parsed, err := jwt.ParseFromRequest(req, keyFunc(token, fn)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} else if !parsed.Valid { | ||||
| 		return nil, jwt.ValidationError{} | ||||
| 	} | ||||
| 	return token, nil | ||||
| } | ||||
|  | ||||
| func New(kind, text string) *Token { | ||||
| 	return &Token{Kind: kind, Text: text} | ||||
| } | ||||
|  | ||||
| // Sign signs the token using the given secret hash | ||||
| // and returns the string value. | ||||
| func (t *Token) Sign(secret string) (string, error) { | ||||
| 	return t.SignExpires(secret, 0) | ||||
| } | ||||
|  | ||||
| // Sign signs the token using the given secret hash | ||||
| // with an expiration date. | ||||
| func (t *Token) SignExpires(secret string, exp int64) (string, error) { | ||||
| 	token := jwt.New(jwt.SigningMethodHS256) | ||||
| 	token.Claims["type"] = t.Kind | ||||
| 	token.Claims["text"] = t.Text | ||||
| 	if exp > 0 { | ||||
| 		token.Claims["exp"] = float64(exp) | ||||
| 	} | ||||
| 	return token.SignedString([]byte(secret)) | ||||
| } | ||||
|  | ||||
| func keyFunc(token *Token, fn SecretFunc) jwt.Keyfunc { | ||||
| 	return func(t *jwt.Token) (interface{}, error) { | ||||
| 		// validate the correct algorithm is being used | ||||
| 		if t.Method.Alg() != SignerAlgo { | ||||
| 			return nil, jwt.ErrSignatureInvalid | ||||
| 		} | ||||
|  | ||||
| 		// extract the token kind and cast to | ||||
| 		// the expected type. | ||||
| 		kindv, ok := t.Claims["type"] | ||||
| 		if !ok { | ||||
| 			return nil, jwt.ValidationError{} | ||||
| 		} | ||||
| 		token.Kind, _ = kindv.(string) | ||||
|  | ||||
| 		// extract the token value and cast to | ||||
| 		// exepected type. | ||||
| 		textv, ok := t.Claims["text"] | ||||
| 		if !ok { | ||||
| 			return nil, jwt.ValidationError{} | ||||
| 		} | ||||
| 		token.Text, _ = textv.(string) | ||||
|  | ||||
| 		// invoke the callback function to retrieve | ||||
| 		// the secret key used to verify | ||||
| 		secret, err := fn(token) | ||||
| 		return []byte(secret), err | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								pkg/token/token_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								pkg/token/token_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package token | ||||
| @@ -1,18 +0,0 @@ | ||||
| package types | ||||
|  | ||||
| type Token struct { | ||||
| 	ID     int64  `meddler:"token_id,pk"   json:"-"` | ||||
| 	UserID int64  `meddler:"token_user_id" json:"-"                sql:"index:ix_token_user_id,unique:ux_token_user_label"` | ||||
| 	Login  string `meddler:"-"             json:"-"                sql:"-"` | ||||
| 	Kind   string `meddler:"token_kind"    json:"kind,omitempty"` | ||||
| 	Label  string `meddler:"token_label"   json:"label,omitempty"  sql:"unique:ux_token_user_label"` | ||||
| 	Expiry int64  `meddler:"token_expiry"  json:"expiry,omitempty"` | ||||
| 	Issued int64  `meddler:"token_issued"  json:"issued_at,omitempty"` | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	TokenUser  = "u" | ||||
| 	TokenSess  = "s" | ||||
| 	TokenHook  = "h" | ||||
| 	TokenAgent = "a" | ||||
| ) | ||||
| @@ -1,18 +1,15 @@ | ||||
| package types | ||||
|  | ||||
| import ( | ||||
| 	"crypto/md5" | ||||
| 	"crypto/rand" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // standard characters allowed in token string. | ||||
| var chars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") | ||||
|  | ||||
| // default token length | ||||
| var length = 40 | ||||
| var length = 32 | ||||
|  | ||||
| // GenerateToken generates random strings good for use in URIs to | ||||
| // identify unique objects. | ||||
| @@ -37,12 +34,3 @@ func GenerateToken() string { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // helper function to create a Gravatar Hash | ||||
| // for the given Email address. | ||||
| func CreateGravatar(email string) string { | ||||
| 	email = strings.ToLower(strings.TrimSpace(email)) | ||||
| 	hash := md5.New() | ||||
| 	hash.Write([]byte(email)) | ||||
| 	return fmt.Sprintf("%x", hash.Sum(nil)) | ||||
| } | ||||
|   | ||||
| @@ -4,13 +4,6 @@ import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func Test_CreateGravatar(t *testing.T) { | ||||
| 	var got, want = CreateGravatar("dr_cooper@caltech.edu"), "2b77ba83e2216ddcd11fe8c24b70c2a3" | ||||
| 	if got != want { | ||||
| 		t.Errorf("Got gravatar hash %s, want %s", got, want) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func Test_GenerateToken(t *testing.T) { | ||||
| 	token := GenerateToken() | ||||
| 	if len(token) != length { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user