mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-30 08:06:52 +02:00
cleaned up the token implementation for #1175
This commit is contained in:
parent
cf953f19d3
commit
cdfec98cf4
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)
|
||||
|
||||
[![Build Status](https://travis-ci.org/dgrijalva/jwt-go.svg?branch=master)](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 {
|
||||
|
Loading…
Reference in New Issue
Block a user