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",
|
"ImportPath": "github.com/dgrijalva/jwt-go",
|
||||||
"Comment": "v2.2.0-16-gc48cfd5",
|
"Comment": "v2.3.0-4-gc1da563",
|
||||||
"Rev": "c48cfd5d9711c75acb6036d2698ef3aef7bb655a"
|
"Rev": "c1da56349675b292d3200463e2c88b9aa5e02391"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/elazarl/go-bindata-assetfs",
|
"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)
|
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.
|
**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?
|
## What the heck is a JWT?
|
||||||
@ -21,8 +23,8 @@ Parsing and verifying tokens is pretty straight forward. You pass in the token
|
|||||||
```go
|
```go
|
||||||
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
|
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
|
||||||
// Don't forget to validate the alg is what you expect:
|
// Don't forget to validate the alg is what you expect:
|
||||||
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
return nil, fmt.Errorf("Unexpected signing method: %v", t.Header["alg"])
|
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
|
||||||
}
|
}
|
||||||
return myLookupKey(token.Header["kid"])
|
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)
|
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
|
## 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 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).
|
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
|
## More
|
||||||
|
|
||||||
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
|
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
|
## `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
|
#### 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.
|
* 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"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"time"
|
"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
|
package jwt_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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 (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -20,13 +19,6 @@ var TimeFunc = time.Now
|
|||||||
// Header of the token (such as `kid`) to identify which key to use.
|
// Header of the token (such as `kid`) to identify which key to use.
|
||||||
type Keyfunc func(*Token) (interface{}, error)
|
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
|
// A JWT Token. Different fields will be used depending on whether you're
|
||||||
// creating or parsing/verifying a token.
|
// creating or parsing/verifying a token.
|
||||||
type Token struct {
|
type Token struct {
|
||||||
@ -167,37 +159,6 @@ func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
|||||||
return token, vErr
|
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.
|
// Try to find the token in an http.Request.
|
||||||
// This method will call ParseMultipartForm if there's no token in the header.
|
// This method will call ParseMultipartForm if there's no token in the header.
|
||||||
// Currently, it looks in the Authorization header as well as
|
// 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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"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
|
package jwt_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"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/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs"
|
||||||
"github.com/drone/drone/pkg/remote"
|
"github.com/drone/drone/pkg/remote"
|
||||||
"github.com/drone/drone/pkg/server"
|
"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"
|
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
|
||||||
eventbus "github.com/drone/drone/pkg/bus/builtin"
|
eventbus "github.com/drone/drone/pkg/bus/builtin"
|
||||||
@ -40,11 +39,6 @@ var conf = struct {
|
|||||||
key string
|
key string
|
||||||
}
|
}
|
||||||
|
|
||||||
session struct {
|
|
||||||
expiry string
|
|
||||||
secret string
|
|
||||||
}
|
|
||||||
|
|
||||||
docker struct {
|
docker struct {
|
||||||
host string
|
host string
|
||||||
cert string
|
cert string
|
||||||
@ -76,8 +70,6 @@ func main() {
|
|||||||
flag.StringVar(&conf.server.addr, "server-addr", ":8080", "")
|
flag.StringVar(&conf.server.addr, "server-addr", ":8080", "")
|
||||||
flag.StringVar(&conf.server.cert, "server-cert", "", "")
|
flag.StringVar(&conf.server.cert, "server-cert", "", "")
|
||||||
flag.StringVar(&conf.server.key, "server-key", "", "")
|
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.driver, "remote-driver", "github", "")
|
||||||
flag.StringVar(&conf.remote.config, "remote-config", "https://github.com", "")
|
flag.StringVar(&conf.remote.config, "remote-config", "https://github.com", "")
|
||||||
flag.StringVar(&conf.database.driver, "database-driver", "sqlite3", "")
|
flag.StringVar(&conf.database.driver, "database-driver", "sqlite3", "")
|
||||||
@ -98,7 +90,6 @@ func main() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
session := session.New(conf.remote.config)
|
|
||||||
eventbus_ := eventbus.New()
|
eventbus_ := eventbus.New()
|
||||||
queue_ := queue.New()
|
queue_ := queue.New()
|
||||||
updater := runner.NewUpdater(eventbus_, store, remote)
|
updater := runner.NewUpdater(eventbus_, store, remote)
|
||||||
@ -116,8 +107,7 @@ func main() {
|
|||||||
api.Use(server.SetDatastore(store))
|
api.Use(server.SetDatastore(store))
|
||||||
api.Use(server.SetRemote(remote))
|
api.Use(server.SetRemote(remote))
|
||||||
api.Use(server.SetQueue(queue_))
|
api.Use(server.SetQueue(queue_))
|
||||||
api.Use(server.SetSession(session))
|
api.Use(server.SetUser())
|
||||||
api.Use(server.SetUser(session))
|
|
||||||
api.Use(server.SetRunner(&runner_))
|
api.Use(server.SetRunner(&runner_))
|
||||||
api.OPTIONS("/*path", func(c *gin.Context) {})
|
api.OPTIONS("/*path", func(c *gin.Context) {})
|
||||||
|
|
||||||
@ -129,9 +119,7 @@ func main() {
|
|||||||
user.PATCH("", server.PutUserCurr)
|
user.PATCH("", server.PutUserCurr)
|
||||||
user.GET("/feed", server.GetUserFeed)
|
user.GET("/feed", server.GetUserFeed)
|
||||||
user.GET("/repos", server.GetUserRepos)
|
user.GET("/repos", server.GetUserRepos)
|
||||||
user.GET("/tokens", server.GetUserTokens)
|
user.POST("/token", server.PostUserToken)
|
||||||
user.POST("/tokens", server.PostToken)
|
|
||||||
user.DELETE("/tokens/:label", server.DelToken)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
users := api.Group("/users")
|
users := api.Group("/users")
|
||||||
@ -199,7 +187,6 @@ func main() {
|
|||||||
auth.Use(server.SetHeaders())
|
auth.Use(server.SetHeaders())
|
||||||
auth.Use(server.SetDatastore(store))
|
auth.Use(server.SetDatastore(store))
|
||||||
auth.Use(server.SetRemote(remote))
|
auth.Use(server.SetRemote(remote))
|
||||||
auth.Use(server.SetSession(session))
|
|
||||||
auth.GET("", server.GetLogin)
|
auth.GET("", server.GetLogin)
|
||||||
auth.POST("", 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/Bugagazavr/go-gitlab-client"
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/hashicorp/golang-lru"
|
"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/oauth2"
|
||||||
"github.com/drone/drone/pkg/remote"
|
"github.com/drone/drone/pkg/remote"
|
||||||
|
"github.com/drone/drone/pkg/token"
|
||||||
common "github.com/drone/drone/pkg/types"
|
common "github.com/drone/drone/pkg/types"
|
||||||
"github.com/drone/drone/pkg/utils/httputil"
|
"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
|
return nil, err
|
||||||
}
|
}
|
||||||
netrc := &common.Netrc{}
|
netrc := &common.Netrc{}
|
||||||
|
netrc.Machine = url_.Host
|
||||||
|
|
||||||
switch g.CloneMode {
|
switch g.CloneMode {
|
||||||
case "oauth":
|
case "oauth":
|
||||||
netrc.Login = "oauth2"
|
netrc.Login = "oauth2"
|
||||||
netrc.Password = u.Token
|
netrc.Password = u.Token
|
||||||
case "token":
|
case "token":
|
||||||
|
t := token.New(token.HookToken, r.FullName)
|
||||||
netrc.Login = "drone-ci-token"
|
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, err
|
||||||
return netrc, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate activates a repository by adding a Post-commit hook and
|
// 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/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
|
// RedirectSha accepts a request to retvie a redirect
|
||||||
@ -79,8 +79,14 @@ func GetPullRequest(c *gin.Context) {
|
|||||||
store := ToDatastore(c)
|
store := ToDatastore(c)
|
||||||
repo := ToRepo(c)
|
repo := ToRepo(c)
|
||||||
|
|
||||||
// get the token and verify the hook is authorized
|
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||||
if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) {
|
return repo.Hash, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if parsed.Text != repo.FullName {
|
||||||
c.AbortWithStatus(403)
|
c.AbortWithStatus(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -118,8 +124,14 @@ func GetCommit(c *gin.Context) {
|
|||||||
repo := ToRepo(c)
|
repo := ToRepo(c)
|
||||||
sha := c.Params.ByName("sha")
|
sha := c.Params.ByName("sha")
|
||||||
|
|
||||||
// get the token and verify the hook is authorized
|
parsed, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||||
if c.Request.FormValue("access_token") != hash.New(repo.FullName, repo.Hash) {
|
return repo.Hash, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
c.Fail(400, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if parsed.Text != repo.FullName {
|
||||||
c.AbortWithStatus(403)
|
c.AbortWithStatus(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,8 @@ import (
|
|||||||
|
|
||||||
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
|
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/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/queue"
|
||||||
|
"github.com/drone/drone/pkg/token"
|
||||||
common "github.com/drone/drone/pkg/types"
|
common "github.com/drone/drone/pkg/types"
|
||||||
"github.com/drone/drone/pkg/utils/httputil"
|
"github.com/drone/drone/pkg/utils/httputil"
|
||||||
"github.com/drone/drone/pkg/yaml"
|
"github.com/drone/drone/pkg/yaml"
|
||||||
@ -56,8 +56,16 @@ func PostHook(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the token and verify the hook is authorized
|
// 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) {
|
||||||
log.Errorf("invalid token sent with hook.")
|
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)
|
c.AbortWithStatus(403)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,8 @@ import (
|
|||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar"
|
"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar"
|
||||||
|
|
||||||
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
|
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
|
// GetLogin accepts a request to authorize the user and to
|
||||||
@ -17,7 +18,6 @@ import (
|
|||||||
// GET /authorize
|
// GET /authorize
|
||||||
//
|
//
|
||||||
func GetLogin(c *gin.Context) {
|
func GetLogin(c *gin.Context) {
|
||||||
session := ToSession(c)
|
|
||||||
remote := ToRemote(c)
|
remote := ToRemote(c)
|
||||||
store := ToDatastore(c)
|
store := ToDatastore(c)
|
||||||
|
|
||||||
@ -65,13 +65,13 @@ func GetLogin(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// create the user account
|
// create the user account
|
||||||
u = &common.User{}
|
u = &types.User{}
|
||||||
u.Login = login.Login
|
u.Login = login.Login
|
||||||
u.Token = login.Token
|
u.Token = login.Token
|
||||||
u.Secret = login.Secret
|
u.Secret = login.Secret
|
||||||
u.Email = login.Email
|
u.Email = login.Email
|
||||||
u.Avatar = login.Avatar
|
u.Avatar = login.Avatar
|
||||||
u.Hash = common.GenerateToken()
|
u.Hash = types.GenerateToken()
|
||||||
|
|
||||||
// insert the user into the database
|
// insert the user into the database
|
||||||
if err := store.AddUser(u); err != nil {
|
if err := store.AddUser(u); err != nil {
|
||||||
@ -106,12 +106,9 @@ func GetLogin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token := &common.Token{
|
exp := time.Now().Add(time.Hour * 72).Unix()
|
||||||
Kind: common.TokenSess,
|
token := token.New(token.SessToken, u.Login)
|
||||||
Login: u.Login,
|
tokenstr, err := token.SignExpires(u.Hash, exp)
|
||||||
Issued: time.Now().UTC().Unix(),
|
|
||||||
}
|
|
||||||
tokenstr, err := session.GenerateToken(token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("cannot create token for %s. %s", u.Login, err)
|
log.Errorf("cannot create token for %s. %s", u.Login, err)
|
||||||
c.Redirect(303, "/login#error=internal_error")
|
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/github.com/gin-gonic/gin/binding"
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/gopkg.in/yaml.v2"
|
"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/remote"
|
||||||
|
"github.com/drone/drone/pkg/token"
|
||||||
common "github.com/drone/drone/pkg/types"
|
common "github.com/drone/drone/pkg/types"
|
||||||
"github.com/drone/drone/pkg/utils/httputil"
|
"github.com/drone/drone/pkg/utils/httputil"
|
||||||
"github.com/drone/drone/pkg/utils/sshutil"
|
"github.com/drone/drone/pkg/utils/sshutil"
|
||||||
@ -208,10 +208,18 @@ func PostRepo(c *gin.Context) {
|
|||||||
r.FullName,
|
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(
|
link := fmt.Sprintf(
|
||||||
"%s/api/hook?access_token=%s",
|
"%s/api/hook?access_token=%s",
|
||||||
httputil.GetURL(c.Request),
|
httputil.GetURL(c.Request),
|
||||||
hash.New(r.FullName, r.Hash),
|
sig,
|
||||||
)
|
)
|
||||||
|
|
||||||
// generate an RSA key and add to the repo
|
// 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/queue"
|
||||||
"github.com/drone/drone/pkg/remote"
|
"github.com/drone/drone/pkg/remote"
|
||||||
"github.com/drone/drone/pkg/runner"
|
"github.com/drone/drone/pkg/runner"
|
||||||
"github.com/drone/drone/pkg/server/session"
|
|
||||||
"github.com/drone/drone/pkg/store"
|
"github.com/drone/drone/pkg/store"
|
||||||
|
"github.com/drone/drone/pkg/token"
|
||||||
common "github.com/drone/drone/pkg/types"
|
common "github.com/drone/drone/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -103,10 +103,6 @@ func ToDatastore(c *gin.Context) store.Store {
|
|||||||
return c.MustGet("datastore").(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 {
|
func SetDatastore(ds store.Store) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
c.Set("datastore", ds)
|
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) {
|
return func(c *gin.Context) {
|
||||||
c.Set("session", s)
|
|
||||||
c.Next()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func SetUser(s session.Session) gin.HandlerFunc {
|
var store = ToDatastore(c)
|
||||||
return func(c *gin.Context) {
|
var user *common.User
|
||||||
ds := ToDatastore(c)
|
|
||||||
token := s.GetLogin(c.Request)
|
|
||||||
if token == nil || len(token.Login) == 0 {
|
|
||||||
c.Next()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := ds.UserLogin(token.Login)
|
_, err := token.ParseRequest(c.Request, func(t *token.Token) (string, error) {
|
||||||
if err == nil {
|
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)
|
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()
|
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/gin-gonic/gin/binding"
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar"
|
"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
|
// GetUserCurr accepts a request to retrieve the
|
||||||
@ -27,7 +28,7 @@ func PutUserCurr(c *gin.Context) {
|
|||||||
store := ToDatastore(c)
|
store := ToDatastore(c)
|
||||||
user := ToUser(c)
|
user := ToUser(c)
|
||||||
|
|
||||||
in := &common.User{}
|
in := &types.User{}
|
||||||
if !c.BindWith(in, binding.JSON) {
|
if !c.BindWith(in, binding.JSON) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -76,19 +77,15 @@ func GetUserFeed(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserTokens accepts a request to get the currently
|
// POST /api/user/token
|
||||||
// authenticated user's token list from the datastore,
|
func PostUserToken(c *gin.Context) {
|
||||||
// encoded and returned in JSON format.
|
|
||||||
//
|
|
||||||
// GET /api/user/tokens
|
|
||||||
//
|
|
||||||
func GetUserTokens(c *gin.Context) {
|
|
||||||
store := ToDatastore(c)
|
|
||||||
user := ToUser(c)
|
user := ToUser(c)
|
||||||
tokens, err := store.TokenList(user)
|
|
||||||
|
t := token.New(token.UserToken, user.Login)
|
||||||
|
s, err := t.Sign(user.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fail(400, err)
|
c.Fail(500, err)
|
||||||
} else {
|
} 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/gin-gonic/gin/binding"
|
||||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/ungerik/go-gravatar"
|
"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
|
// GetUsers accepts a request to retrieve all users
|
||||||
@ -32,9 +32,13 @@ func GetUsers(c *gin.Context) {
|
|||||||
func PostUser(c *gin.Context) {
|
func PostUser(c *gin.Context) {
|
||||||
store := ToDatastore(c)
|
store := ToDatastore(c)
|
||||||
name := c.Params.ByName("name")
|
name := c.Params.ByName("name")
|
||||||
user := &common.User{Login: name}
|
user := &types.User{Login: name}
|
||||||
user.Token = c.Request.FormValue("token")
|
user.Token = c.Request.FormValue("token")
|
||||||
user.Secret = c.Request.FormValue("secret")
|
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 {
|
if err := store.AddUser(user); err != nil {
|
||||||
c.Fail(400, err)
|
c.Fail(400, err)
|
||||||
} else {
|
} else {
|
||||||
@ -75,7 +79,7 @@ func PutUser(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
in := &common.User{}
|
in := &types.User{}
|
||||||
if !c.BindWith(in, binding.JSON) {
|
if !c.BindWith(in, binding.JSON) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ import (
|
|||||||
"github.com/drone/drone/pkg/store/builtin/migrate"
|
"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/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/lib/pq"
|
||||||
_ "github.com/drone/drone/Godeps/_workspace/src/github.com/mattn/go-sqlite3"
|
_ "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/russross/meddler"
|
||||||
_ "github.com/drone/drone/Godeps/_workspace/src/github.com/go-sql-driver/mysql"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -94,7 +94,6 @@ func New(db *sql.DB) store.Store {
|
|||||||
*Jobstore
|
*Jobstore
|
||||||
*Blobstore
|
*Blobstore
|
||||||
*Starstore
|
*Starstore
|
||||||
*Tokenstore
|
|
||||||
*Agentstore
|
*Agentstore
|
||||||
}{
|
}{
|
||||||
NewUserstore(db),
|
NewUserstore(db),
|
||||||
@ -103,7 +102,6 @@ func New(db *sql.DB) store.Store {
|
|||||||
NewJobstore(db),
|
NewJobstore(db),
|
||||||
NewBlobstore(db),
|
NewBlobstore(db),
|
||||||
NewStarstore(db),
|
NewStarstore(db),
|
||||||
NewTokenstore(db),
|
|
||||||
NewAgentstore(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
|
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) {
|
func (m *Store) Starred(_a0 *types.User, _a1 *types.Repo) (bool, error) {
|
||||||
ret := m.Called(_a0, _a1)
|
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
|
// Starred returns true if the user starred
|
||||||
// the given repository.
|
// the given repository.
|
||||||
Starred(*types.User, *types.Repo) (bool, error)
|
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
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// standard characters allowed in token string.
|
// standard characters allowed in token string.
|
||||||
var chars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
var chars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
|
||||||
|
|
||||||
// default token length
|
// default token length
|
||||||
var length = 40
|
var length = 32
|
||||||
|
|
||||||
// GenerateToken generates random strings good for use in URIs to
|
// GenerateToken generates random strings good for use in URIs to
|
||||||
// identify unique objects.
|
// 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"
|
"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) {
|
func Test_GenerateToken(t *testing.T) {
|
||||||
token := GenerateToken()
|
token := GenerateToken()
|
||||||
if len(token) != length {
|
if len(token) != length {
|
||||||
|
Loading…
Reference in New Issue
Block a user