vendor: revendor

This commit is contained in:
Eric Chiang 2017-03-08 10:33:36 -08:00
parent 777eeafabc
commit e5f60fe9dd
6 changed files with 120 additions and 351 deletions

7
glide.lock generated
View file

@ -1,5 +1,5 @@
hash: 1207c251a7dab3b824746d66219beabc40f0b1d3c08ed90ac50f5bbdb8872631 hash: fbef1f81a0f86f519714bbe568692a92709c446ef487a3fa9875e58f86e14430
updated: 2017-01-25T20:32:43.599648533+01:00 updated: 2017-03-08T10:31:06.364335442-08:00
imports: imports:
- name: github.com/beevik/etree - name: github.com/beevik/etree
version: 4cd0dd976db869f817248477718071a28e978df0 version: 4cd0dd976db869f817248477718071a28e978df0
@ -8,7 +8,7 @@ imports:
subpackages: subpackages:
- crdb - crdb
- name: github.com/coreos/go-oidc - name: github.com/coreos/go-oidc
version: 2b5d73091ea4b7ddb15e3ac00077f153120b5b61 version: be73733bb8cc830d0205609b95d125215f8e9c70
- name: github.com/ghodss/yaml - name: github.com/ghodss/yaml
version: bea76d6a4713e18b7f5321a2b020738552def3ea version: bea76d6a4713e18b7f5321a2b020738552def3ea
- name: github.com/go-sql-driver/mysql - name: github.com/go-sql-driver/mysql
@ -62,7 +62,6 @@ imports:
version: 6a513affb38dc9788b449d59ffed099b8de18fa0 version: 6a513affb38dc9788b449d59ffed099b8de18fa0
subpackages: subpackages:
- context - context
- context/ctxhttp
- http2 - http2
- http2/hpack - http2/hpack
- internal/timeseries - internal/timeseries

View file

@ -1,6 +1,7 @@
package oidc package oidc
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -9,8 +10,6 @@ import (
"time" "time"
"github.com/pquerna/cachecontrol" "github.com/pquerna/cachecontrol"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
jose "gopkg.in/square/go-jose.v2" jose "gopkg.in/square/go-jose.v2"
) )
@ -163,7 +162,7 @@ func (r *remoteKeySet) updateKeys(ctx context.Context) error {
return fmt.Errorf("oidc: can't create request: %v", err) return fmt.Errorf("oidc: can't create request: %v", err)
} }
resp, err := ctxhttp.Do(ctx, clientFromContext(ctx), req) resp, err := doRequest(ctx, req)
if err != nil { if err != nil {
return fmt.Errorf("oidc: get keys failed %v", err) return fmt.Errorf("oidc: get keys failed %v", err)
} }

View file

@ -2,6 +2,7 @@
package oidc package oidc
import ( import (
"context"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -10,8 +11,6 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/net/context"
"golang.org/x/net/context/ctxhttp"
"golang.org/x/oauth2" "golang.org/x/oauth2"
jose "gopkg.in/square/go-jose.v2" jose "gopkg.in/square/go-jose.v2"
) )
@ -46,11 +45,12 @@ func ClientContext(ctx context.Context, client *http.Client) context.Context {
return context.WithValue(ctx, oauth2.HTTPClient, client) return context.WithValue(ctx, oauth2.HTTPClient, client)
} }
func clientFromContext(ctx context.Context) *http.Client { func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) {
if client, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { client := http.DefaultClient
return client if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok {
client = c
} }
return http.DefaultClient return client.Do(req.WithContext(ctx))
} }
// Provider represents an OpenID Connect server's configuration. // Provider represents an OpenID Connect server's configuration.
@ -85,7 +85,11 @@ type providerJSON struct {
// or "https://login.salesforce.com". // or "https://login.salesforce.com".
func NewProvider(ctx context.Context, issuer string) (*Provider, error) { func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration" wellKnown := strings.TrimSuffix(issuer, "/") + "/.well-known/openid-configuration"
resp, err := ctxhttp.Get(ctx, clientFromContext(ctx), wellKnown) req, err := http.NewRequest("GET", wellKnown, nil)
if err != nil {
return nil, err
}
resp, err := doRequest(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -174,7 +178,7 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource)
} }
token.SetAuthHeader(req) token.SetAuthHeader(req)
resp, err := ctxhttp.Do(ctx, clientFromContext(ctx), req) resp, err := doRequest(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -201,19 +205,35 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource)
// The ID Token only holds fields OpenID Connect requires. To access additional // The ID Token only holds fields OpenID Connect requires. To access additional
// claims returned by the server, use the Claims method. // claims returned by the server, use the Claims method.
type IDToken struct { type IDToken struct {
// The URL of the server which issued this token. This will always be the same // The URL of the server which issued this token. OpenID Connect
// as the URL used for initial discovery. // requires this value always be identical to the URL used for
// initial discovery.
//
// Note: Because of a known issue with Google Accounts' implementation
// this value may differ when using Google.
//
// See: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo
Issuer string Issuer string
// The client, or set of clients, that this token is issued for. // The client ID, or set of client IDs, that this token is issued for. For
// common uses, this is the client that initialized the auth flow.
//
// This package ensures the audience contains an expected value.
Audience []string Audience []string
// A unique string which identifies the end user. // A unique string which identifies the end user.
Subject string Subject string
// Expiry of the token. Ths package will not process tokens that have
// expired unless that validation is explicitly turned off.
Expiry time.Time
// When the token was issued by the provider.
IssuedAt time.Time IssuedAt time.Time
Expiry time.Time
Nonce string // Initial nonce provided during the authentication redirect.
//
// If present, this package ensures this is a valid nonce.
Nonce string
// Raw payload of the id_token. // Raw payload of the id_token.
claims []byte claims []byte

View file

@ -2,6 +2,7 @@ package oidc
import ( import (
"bytes" "bytes"
"context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
@ -9,61 +10,71 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/net/context"
"golang.org/x/oauth2" "golang.org/x/oauth2"
jose "gopkg.in/square/go-jose.v2" jose "gopkg.in/square/go-jose.v2"
) )
const (
issuerGoogleAccounts = "https://accounts.google.com"
issuerGoogleAccountsNoScheme = "accounts.google.com"
)
// IDTokenVerifier provides verification for ID Tokens. // IDTokenVerifier provides verification for ID Tokens.
type IDTokenVerifier struct { type IDTokenVerifier struct {
keySet *remoteKeySet keySet *remoteKeySet
config *verificationConfig config *Config
}
// verificationConfig is the unexported configuration for an IDTokenVerifier.
//
// Users interact with this struct using a VerificationOption.
type verificationConfig struct {
issuer string issuer string
// If provided, this value must be in the ID Token audiences.
audience string
// If not nil, check the expiry of the id token.
checkExpiry func() time.Time
// If specified, only these sets of algorithms may be used to sign the JWT.
requiredAlgs []string
// If not nil, don't verify nonce.
nonceSource NonceSource
} }
// VerificationOption provides additional checks on ID Tokens. // Config is the configuration for an IDTokenVerifier.
type VerificationOption interface { type Config struct {
// Unexport this method so other packages can't implement this interface. // Expected audience of the token. For a majority of the cases this is expected to be
updateConfig(c *verificationConfig) // the ID of the client that initialized the login flow. It may occasionally differ if
// the provider supports the authorizing party (azp) claim.
//
// If not provided, users must explicitly set SkipClientIDCheck.
ClientID string
// Method to verify the ID Token nonce. If a nonce is present and this method
// is nil, users must explicitly set SkipNonceCheck.
//
// If the ID Token nonce is empty, for example if the client didn't provide a nonce in
// the initial redirect, this may be nil.
ClaimNonce func(nonce string) error
// If specified, only this set of algorithms may be used to sign the JWT.
//
// Since many providers only support RS256, SupportedSigningAlgs defaults to this value.
SupportedSigningAlgs []string
// If true, no ClientID check performed. Must be true if ClientID field is empty.
SkipClientIDCheck bool
// If true, token expiry is not checked.
SkipExpiryCheck bool
// If true, nonce claim is not checked. Must be true if ClaimNonce field is empty.
SkipNonceCheck bool
// Time function to check Token expiry. Defaults to time.Now
Now func() time.Time
} }
// Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs. // Verifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.
// //
// The returned IDTokenVerifier is tied to the Provider's context and its behavior is // The returned IDTokenVerifier is tied to the Provider's context and its behavior is
// undefined once the Provider's context is canceled. // undefined once the Provider's context is canceled.
func (p *Provider) Verifier(options ...VerificationOption) *IDTokenVerifier { func (p *Provider) Verifier(config *Config) *IDTokenVerifier {
config := &verificationConfig{issuer: p.issuer}
for _, option := range options {
option.updateConfig(config)
}
return newVerifier(p.remoteKeySet, config) return newVerifier(p.remoteKeySet, config, p.issuer)
} }
func newVerifier(keySet *remoteKeySet, config *verificationConfig) *IDTokenVerifier { func newVerifier(keySet *remoteKeySet, config *Config, issuer string) *IDTokenVerifier {
// As discussed in the godocs for VerifrySigningAlg, because almost all providers // If SupportedSigningAlgs is empty defaults to only support RS256.
// only support RS256, default to only allowing it. if len(config.SupportedSigningAlgs) == 0 {
if len(config.requiredAlgs) == 0 { config.SupportedSigningAlgs = []string{RS256}
config.requiredAlgs = []string{RS256}
} }
return &IDTokenVerifier{ return &IDTokenVerifier{
keySet: keySet, keySet: keySet,
config: config, config: config,
issuer: issuer,
} }
} }
@ -89,7 +100,7 @@ func contains(sli []string, ele string) bool {
} }
// Verify parses a raw ID Token, verifies it's been signed by the provider, preforms // Verify parses a raw ID Token, verifies it's been signed by the provider, preforms
// any additional checks passed as VerifictionOptions, and returns the payload. // any additional checks depending on the Config, and returns the payload.
// //
// See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation // See: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
// //
@ -134,20 +145,38 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
} }
// Check issuer. // Check issuer.
if t.Issuer != v.config.issuer { if t.Issuer != v.issuer {
return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.config.issuer, t.Issuer) // Google sometimes returns "accounts.google.com" as the issuer claim instead of
} // the required "https://accounts.google.com". Detect this case and allow it only
// for Google.
// If a client ID has been provided, make sure it's part of the audience. //
if v.config.audience != "" { // We will not add hooks to let other providers go off spec like this.
if !contains(t.Audience, v.config.audience) { if !(v.issuer == issuerGoogleAccounts && t.Issuer == issuerGoogleAccountsNoScheme) {
return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.audience, t.Audience) return nil, fmt.Errorf("oidc: id token issued by a different provider, expected %q got %q", v.issuer, t.Issuer)
} }
} }
// If a checkExpiry is specified, make sure token is not expired. // If a client ID has been provided, make sure it's part of the audience. SkipClientIDCheck must be true if ClientID is empty.
if v.config.checkExpiry != nil { //
if t.Expiry.Before(v.config.checkExpiry()) { // This check DOES NOT ensure that the ClientID is the party to which the ID Token was issued (i.e. Authorized party).
if !v.config.SkipClientIDCheck {
if v.config.ClientID != "" {
if !contains(t.Audience, v.config.ClientID) {
return nil, fmt.Errorf("oidc: expected audience %q got %q", v.config.ClientID, t.Audience)
}
} else {
return nil, fmt.Errorf("oidc: Invalid configuration. ClientID must be provided or SkipClientIDCheck must be set.")
}
}
// If a SkipExpiryCheck is false, make sure token is not expired.
if !v.config.SkipExpiryCheck {
now := time.Now
if v.config.Now != nil {
now = v.config.Now
}
if t.Expiry.Before(now()) {
return nil, fmt.Errorf("oidc: token is expired (Token Expiry: %v)", t.Expiry) return nil, fmt.Errorf("oidc: token is expired (Token Expiry: %v)", t.Expiry)
} }
} }
@ -155,14 +184,14 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
// If a set of required algorithms has been provided, ensure that the signatures use those. // If a set of required algorithms has been provided, ensure that the signatures use those.
var keyIDs, gotAlgs []string var keyIDs, gotAlgs []string
for _, sig := range jws.Signatures { for _, sig := range jws.Signatures {
if len(v.config.requiredAlgs) == 0 || contains(v.config.requiredAlgs, sig.Header.Algorithm) { if len(v.config.SupportedSigningAlgs) == 0 || contains(v.config.SupportedSigningAlgs, sig.Header.Algorithm) {
keyIDs = append(keyIDs, sig.Header.KeyID) keyIDs = append(keyIDs, sig.Header.KeyID)
} else { } else {
gotAlgs = append(gotAlgs, sig.Header.Algorithm) gotAlgs = append(gotAlgs, sig.Header.Algorithm)
} }
} }
if len(keyIDs) == 0 { if len(keyIDs) == 0 {
return nil, fmt.Errorf("oidc: no signatures use a require algorithm, expected %q got %q", v.config.requiredAlgs, gotAlgs) return nil, fmt.Errorf("oidc: no signatures use a supported algorithm, expected %q got %q", v.config.SupportedSigningAlgs, gotAlgs)
} }
// Get keys from the remote key set. This may trigger a re-sync. // Get keys from the remote key set. This may trigger a re-sync.
@ -192,79 +221,22 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
// Check the nonce after we've verified the token. We don't want to allow unverified // Check the nonce after we've verified the token. We don't want to allow unverified
// payloads to trigger a nonce lookup. // payloads to trigger a nonce lookup.
if v.config.nonceSource != nil { // If SkipNonceCheck is not set ClaimNonce cannot be Nil.
if err := v.config.nonceSource.ClaimNonce(t.Nonce); err != nil { if !v.config.SkipNonceCheck && t.Nonce != "" {
return nil, err if v.config.ClaimNonce != nil {
if err := v.config.ClaimNonce(t.Nonce); err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("oidc: Invalid configuration. ClaimNonce must be provided or SkipNonceCheck must be set.")
} }
} }
return t, nil return t, nil
} }
// VerifyAudience ensures that an ID Token was issued for the specific client.
//
// Note that a verified token may be valid for other clients, as OpenID Connect allows a token to have
// multiple audiences.
func VerifyAudience(clientID string) VerificationOption {
return clientVerifier{clientID}
}
type clientVerifier struct {
clientID string
}
func (v clientVerifier) updateConfig(c *verificationConfig) {
c.audience = v.clientID
}
// VerifyExpiry ensures that an ID Token has not expired.
func VerifyExpiry() VerificationOption {
return expiryVerifier{}
}
type expiryVerifier struct{}
func (v expiryVerifier) updateConfig(c *verificationConfig) {
c.checkExpiry = time.Now
}
// VerifySigningAlg enforces that an ID Token is signed by a specific signing algorithm.
//
// Because so many providers only support RS256, if this verifiction option isn't used,
// the IDTokenVerifier defaults to only allowing RS256.
func VerifySigningAlg(allowedAlgs ...string) VerificationOption {
return algVerifier{allowedAlgs}
}
type algVerifier struct {
algs []string
}
func (v algVerifier) updateConfig(c *verificationConfig) {
c.requiredAlgs = v.algs
}
// Nonce returns an auth code option which requires the ID Token created by the // Nonce returns an auth code option which requires the ID Token created by the
// OpenID Connect provider to contain the specified nonce. // OpenID Connect provider to contain the specified nonce.
func Nonce(nonce string) oauth2.AuthCodeOption { func Nonce(nonce string) oauth2.AuthCodeOption {
return oauth2.SetAuthURLParam("nonce", nonce) return oauth2.SetAuthURLParam("nonce", nonce)
} }
// NonceSource represents a source which can verify a nonce is valid and has not
// been claimed before.
type NonceSource interface {
ClaimNonce(nonce string) error
}
// VerifyNonce ensures that the ID Token contains a nonce which can be claimed by the nonce source.
func VerifyNonce(source NonceSource) VerificationOption {
return nonceVerifier{source}
}
type nonceVerifier struct {
nonceSource NonceSource
}
func (n nonceVerifier) updateConfig(c *verificationConfig) {
c.nonceSource = n.nonceSource
}

View file

@ -1,74 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.7
// Package ctxhttp provides helper functions for performing context-aware HTTP requests.
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import (
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
)
// Do sends an HTTP request with the provided http.Client and returns
// an HTTP response.
//
// If the client is nil, http.DefaultClient is used.
//
// The provided ctx must be non-nil. If it is canceled or times out,
// ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
resp, err := client.Do(req.WithContext(ctx))
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
if err != nil {
select {
case <-ctx.Done():
err = ctx.Err()
default:
}
}
return resp, err
}
// Get issues a GET request via the Do function.
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Head issues a HEAD request via the Do function.
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Post issues a POST request via the Do function.
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return Do(ctx, client, req)
}
// PostForm issues a POST request via the Do function.
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}

View file

@ -1,147 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !go1.7
package ctxhttp // import "golang.org/x/net/context/ctxhttp"
import (
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
)
func nop() {}
var (
testHookContextDoneBeforeHeaders = nop
testHookDoReturned = nop
testHookDidBodyClose = nop
)
// Do sends an HTTP request with the provided http.Client and returns an HTTP response.
// If the client is nil, http.DefaultClient is used.
// If the context is canceled or times out, ctx.Err() will be returned.
func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if client == nil {
client = http.DefaultClient
}
// TODO(djd): Respect any existing value of req.Cancel.
cancel := make(chan struct{})
req.Cancel = cancel
type responseAndError struct {
resp *http.Response
err error
}
result := make(chan responseAndError, 1)
// Make local copies of test hooks closed over by goroutines below.
// Prevents data races in tests.
testHookDoReturned := testHookDoReturned
testHookDidBodyClose := testHookDidBodyClose
go func() {
resp, err := client.Do(req)
testHookDoReturned()
result <- responseAndError{resp, err}
}()
var resp *http.Response
select {
case <-ctx.Done():
testHookContextDoneBeforeHeaders()
close(cancel)
// Clean up after the goroutine calling client.Do:
go func() {
if r := <-result; r.resp != nil {
testHookDidBodyClose()
r.resp.Body.Close()
}
}()
return nil, ctx.Err()
case r := <-result:
var err error
resp, err = r.resp, r.err
if err != nil {
return resp, err
}
}
c := make(chan struct{})
go func() {
select {
case <-ctx.Done():
close(cancel)
case <-c:
// The response's Body is closed.
}
}()
resp.Body = &notifyingReader{resp.Body, c}
return resp, nil
}
// Get issues a GET request via the Do function.
func Get(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Head issues a HEAD request via the Do function.
func Head(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
req, err := http.NewRequest("HEAD", url, nil)
if err != nil {
return nil, err
}
return Do(ctx, client, req)
}
// Post issues a POST request via the Do function.
func Post(ctx context.Context, client *http.Client, url string, bodyType string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequest("POST", url, body)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", bodyType)
return Do(ctx, client, req)
}
// PostForm issues a POST request via the Do function.
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values) (*http.Response, error) {
return Post(ctx, client, url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
// notifyingReader is an io.ReadCloser that closes the notify channel after
// Close is called or a Read fails on the underlying ReadCloser.
type notifyingReader struct {
io.ReadCloser
notify chan<- struct{}
}
func (r *notifyingReader) Read(p []byte) (int, error) {
n, err := r.ReadCloser.Read(p)
if err != nil && r.notify != nil {
close(r.notify)
r.notify = nil
}
return n, err
}
func (r *notifyingReader) Close() error {
err := r.ReadCloser.Close()
if r.notify != nil {
close(r.notify)
r.notify = nil
}
return err
}