From 0ef161e7d9d718cbd898f9201af44e85a6374dfd Mon Sep 17 00:00:00 2001 From: James Peach Date: Sat, 5 Mar 2022 19:30:34 +1100 Subject: [PATCH 1/2] Move to coreos/go-oidc/v3. Move from gopkg.in/coreos/go-oidc.v2 module to github.com/coreos/go-oidc/v3, which is the current supported version of this module. This fixes #167. Signed-off-by: James Peach --- go.mod | 5 +- go.sum | 11 +- validation/validation.go | 2 +- .../coreos/go-oidc/v3}/LICENSE | 0 .../coreos/go-oidc/v3}/NOTICE | 0 .../coreos/go-oidc/v3/oidc}/jose.go | 10 +- .../coreos/go-oidc/v3/oidc}/jwks.go | 90 ++- .../coreos/go-oidc/v3/oidc}/oidc.go | 129 ++++- .../coreos/go-oidc/v3/oidc}/verify.go | 27 +- .../pquerna/cachecontrol/.travis.yml | 10 - .../github.com/pquerna/cachecontrol/LICENSE | 202 ------- .../github.com/pquerna/cachecontrol/README.md | 107 ---- vendor/github.com/pquerna/cachecontrol/api.go | 48 -- .../cachecontrol/cacheobject/directive.go | 546 ------------------ .../pquerna/cachecontrol/cacheobject/lex.go | 93 --- .../cachecontrol/cacheobject/object.go | 387 ------------- .../cachecontrol/cacheobject/reasons.go | 95 --- .../cachecontrol/cacheobject/warning.go | 107 ---- vendor/github.com/pquerna/cachecontrol/doc.go | 25 - vendor/gopkg.in/coreos/go-oidc.v2/.gitignore | 2 - vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml | 16 - .../coreos/go-oidc.v2/CONTRIBUTING.md | 71 --- vendor/gopkg.in/coreos/go-oidc.v2/DCO | 36 -- vendor/gopkg.in/coreos/go-oidc.v2/MAINTAINERS | 3 - vendor/gopkg.in/coreos/go-oidc.v2/README.md | 72 --- .../coreos/go-oidc.v2/code-of-conduct.md | 61 -- vendor/gopkg.in/coreos/go-oidc.v2/test | 16 - vendor/gopkg.in/square/go-jose.v2/.gitignore | 1 + vendor/gopkg.in/square/go-jose.v2/.travis.yml | 5 +- vendor/gopkg.in/square/go-jose.v2/encoding.go | 14 +- vendor/gopkg.in/square/go-jose.v2/jwk.go | 170 +++++- vendor/gopkg.in/square/go-jose.v2/jws.go | 6 +- vendor/gopkg.in/square/go-jose.v2/signing.go | 12 +- vendor/modules.txt | 12 +- 34 files changed, 361 insertions(+), 2030 deletions(-) rename vendor/{gopkg.in/coreos/go-oidc.v2 => github.com/coreos/go-oidc/v3}/LICENSE (100%) rename vendor/{gopkg.in/coreos/go-oidc.v2 => github.com/coreos/go-oidc/v3}/NOTICE (100%) rename vendor/{gopkg.in/coreos/go-oidc.v2 => github.com/coreos/go-oidc/v3/oidc}/jose.go (68%) rename vendor/{gopkg.in/coreos/go-oidc.v2 => github.com/coreos/go-oidc/v3/oidc}/jwks.go (63%) rename vendor/{gopkg.in/coreos/go-oidc.v2 => github.com/coreos/go-oidc/v3/oidc}/oidc.go (74%) rename vendor/{gopkg.in/coreos/go-oidc.v2 => github.com/coreos/go-oidc/v3/oidc}/verify.go (96%) delete mode 100644 vendor/github.com/pquerna/cachecontrol/.travis.yml delete mode 100644 vendor/github.com/pquerna/cachecontrol/LICENSE delete mode 100644 vendor/github.com/pquerna/cachecontrol/README.md delete mode 100644 vendor/github.com/pquerna/cachecontrol/api.go delete mode 100644 vendor/github.com/pquerna/cachecontrol/cacheobject/directive.go delete mode 100644 vendor/github.com/pquerna/cachecontrol/cacheobject/lex.go delete mode 100644 vendor/github.com/pquerna/cachecontrol/cacheobject/object.go delete mode 100644 vendor/github.com/pquerna/cachecontrol/cacheobject/reasons.go delete mode 100644 vendor/github.com/pquerna/cachecontrol/cacheobject/warning.go delete mode 100644 vendor/github.com/pquerna/cachecontrol/doc.go delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/.gitignore delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/CONTRIBUTING.md delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/DCO delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/MAINTAINERS delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/README.md delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/code-of-conduct.md delete mode 100644 vendor/gopkg.in/coreos/go-oidc.v2/test diff --git a/go.mod b/go.mod index 59b8ec7c..ff90241b 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/cloudflare/golibs v0.0.0-20170913112048-333127dbecfc github.com/coredns/coredns v1.8.7 github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 + github.com/coreos/go-oidc/v3 v3.1.0 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect @@ -33,7 +34,6 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pkg/errors v0.9.1 - github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_model v0.2.0 github.com/prometheus/common v0.32.1 // indirect @@ -51,9 +51,8 @@ require ( golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb // indirect google.golang.org/grpc v1.43.0 // indirect - gopkg.in/coreos/go-oidc.v2 v2.1.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 - gopkg.in/square/go-jose.v2 v2.4.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect zombiezen.com/go/capnproto2 v2.18.0+incompatible diff --git a/go.sum b/go.sum index ed534dcb..fb66468d 100644 --- a/go.sum +++ b/go.sum @@ -133,6 +133,8 @@ github.com/coredns/coredns v1.8.7 h1:wVMjAnyFnY7Mc18AFO+9qbGD6ODPtdVUIlzoWrHr3hk github.com/coredns/coredns v1.8.7/go.mod h1:bFmbgEfeRz5aizL2VsQ5LRlsvJuXWkgG/MWG9zxqjVM= github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73 h1:7CNPV0LWRCa1FNmqg700pbXhzvmoaXKyfxWRkjRym7Q= github.com/coreos/go-oidc v0.0.0-20171002155002-a93f71fdfe73/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= +github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -474,8 +476,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU= -github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -674,6 +674,7 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= @@ -1052,15 +1053,13 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/coreos/go-oidc.v2 v2.1.0 h1:E8PjVFdj/SLDKB0hvb70KTbMbYVHjqztiQdSkIg8E+I= -gopkg.in/coreos/go-oidc.v2 v2.1.0/go.mod h1:fYaTe2FS96wZZwR17YTDHwG+Mw6fmyqJNxN2eNCGPCI= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A= -gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/validation/validation.go b/validation/validation.go index a9f66893..5d2d326f 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -9,9 +9,9 @@ import ( "strings" "time" + "github.com/coreos/go-oidc/v3/oidc" "github.com/pkg/errors" "golang.org/x/net/idna" - "gopkg.in/coreos/go-oidc.v2" ) const ( diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/LICENSE b/vendor/github.com/coreos/go-oidc/v3/LICENSE similarity index 100% rename from vendor/gopkg.in/coreos/go-oidc.v2/LICENSE rename to vendor/github.com/coreos/go-oidc/v3/LICENSE diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/NOTICE b/vendor/github.com/coreos/go-oidc/v3/NOTICE similarity index 100% rename from vendor/gopkg.in/coreos/go-oidc.v2/NOTICE rename to vendor/github.com/coreos/go-oidc/v3/NOTICE diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/jose.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go similarity index 68% rename from vendor/gopkg.in/coreos/go-oidc.v2/jose.go rename to vendor/github.com/coreos/go-oidc/v3/oidc/jose.go index f2e6bf43..8afa895c 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/jose.go +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jose.go @@ -1,13 +1,9 @@ -// +build !golint - -// Don't lint this file. We don't want to have to add a comment to each constant. - package oidc +// JOSE asymmetric signing algorithm values as defined by RFC 7518 +// +// see: https://tools.ietf.org/html/rfc7518#section-3.1 const ( - // JOSE asymmetric signing algorithm values as defined by RFC 7518 - // - // see: https://tools.ietf.org/html/rfc7518#section-3.1 RS256 = "RS256" // RSASSA-PKCS-v1.5 using SHA-256 RS384 = "RS384" // RSASSA-PKCS-v1.5 using SHA-384 RS512 = "RS512" // RSASSA-PKCS-v1.5 using SHA-512 diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/jwks.go b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go similarity index 63% rename from vendor/gopkg.in/coreos/go-oidc.v2/jwks.go rename to vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go index e6a82c84..a272b7ab 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/jwks.go +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/jwks.go @@ -9,56 +9,44 @@ import ( "sync" "time" - "github.com/pquerna/cachecontrol" jose "gopkg.in/square/go-jose.v2" ) -// keysExpiryDelta is the allowed clock skew between a client and the OpenID Connect -// server. -// -// When keys expire, they are valid for this amount of time after. -// -// If the keys have not expired, and an ID Token claims it was signed by a key not in -// the cache, if and only if the keys expire in this amount of time, the keys will be -// updated. -const keysExpiryDelta = 30 * time.Second - // NewRemoteKeySet returns a KeySet that can validate JSON web tokens by using HTTP // GETs to fetch JSON web token sets hosted at a remote URL. This is automatically // used by NewProvider using the URLs returned by OpenID Connect discovery, but is // exposed for providers that don't support discovery or to prevent round trips to the // discovery URL. // -// The returned KeySet is a long lived verifier that caches keys based on cache-control -// headers. Reuse a common remote key set instead of creating new ones as needed. -// -// The behavior of the returned KeySet is undefined once the context is canceled. -func NewRemoteKeySet(ctx context.Context, jwksURL string) KeySet { +// The returned KeySet is a long lived verifier that caches keys based on any +// keys change. Reuse a common remote key set instead of creating new ones as needed. +func NewRemoteKeySet(ctx context.Context, jwksURL string) *RemoteKeySet { return newRemoteKeySet(ctx, jwksURL, time.Now) } -func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *remoteKeySet { +func newRemoteKeySet(ctx context.Context, jwksURL string, now func() time.Time) *RemoteKeySet { if now == nil { now = time.Now } - return &remoteKeySet{jwksURL: jwksURL, ctx: ctx, now: now} + return &RemoteKeySet{jwksURL: jwksURL, ctx: cloneContext(ctx), now: now} } -type remoteKeySet struct { +// RemoteKeySet is a KeySet implementation that validates JSON web tokens against +// a jwks_uri endpoint. +type RemoteKeySet struct { jwksURL string ctx context.Context now func() time.Time // guard all other fields - mu sync.Mutex + mu sync.RWMutex // inflight suppresses parallel execution of updateKeys and allows // multiple goroutines to wait for its result. inflight *inflight - // A set of cached keys and their expiry. + // A set of cached keys. cachedKeys []jose.JSONWebKey - expiry time.Time } // inflight is used to wait on some in-flight request from multiple goroutines. @@ -93,7 +81,12 @@ func (i *inflight) result() ([]jose.JSONWebKey, error) { return i.keys, i.err } -func (r *remoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { +// VerifySignature validates a payload against a signature from the jwks_uri. +// +// Users MUST NOT call this method directly and should use an IDTokenVerifier +// instead. This method skips critical validations such as 'alg' values and is +// only exported to implement the KeySet interface. +func (r *RemoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, error) { jws, err := jose.ParseSigned(jwt) if err != nil { return nil, fmt.Errorf("oidc: malformed jwt: %v", err) @@ -101,7 +94,7 @@ func (r *remoteKeySet) VerifySignature(ctx context.Context, jwt string) ([]byte, return r.verify(ctx, jws) } -func (r *remoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { +func (r *RemoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { // We don't support JWTs signed with multiple signatures. keyID := "" for _, sig := range jws.Signatures { @@ -109,9 +102,7 @@ func (r *remoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ( break } - keys, expiry := r.keysFromCache() - - // Don't check expiry yet. This optimizes for when the provider is unavailable. + keys := r.keysFromCache() for _, key := range keys { if keyID == "" || key.KeyID == keyID { if payload, err := jws.Verify(&key); err == nil { @@ -120,11 +111,10 @@ func (r *remoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ( } } - if !r.now().Add(keysExpiryDelta).After(expiry) { - // Keys haven't expired, don't refresh. - return nil, errors.New("failed to verify id token signature") - } - + // If the kid doesn't match, check for new keys from the remote. This is the + // strategy recommended by the spec. + // + // https://openid.net/specs/openid-connect-core-1_0.html#RotateSigKeys keys, err := r.keysFromRemote(ctx) if err != nil { return nil, fmt.Errorf("fetching keys %v", err) @@ -140,15 +130,15 @@ func (r *remoteKeySet) verify(ctx context.Context, jws *jose.JSONWebSignature) ( return nil, errors.New("failed to verify id token signature") } -func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey, expiry time.Time) { - r.mu.Lock() - defer r.mu.Unlock() - return r.cachedKeys, r.expiry +func (r *RemoteKeySet) keysFromCache() (keys []jose.JSONWebKey) { + r.mu.RLock() + defer r.mu.RUnlock() + return r.cachedKeys } // keysFromRemote syncs the key set from the remote set, records the values in the // cache, and returns the key set. -func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) { +func (r *RemoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, error) { // Need to lock to inspect the inflight request field. r.mu.Lock() // If there's not a current inflight request, create one. @@ -160,7 +150,7 @@ func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, e // once the goroutine is done. go func() { // Sync keys and finish inflight when that's done. - keys, expiry, err := r.updateKeys() + keys, err := r.updateKeys() r.inflight.done(keys, err) @@ -171,7 +161,6 @@ func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, e if err == nil { r.cachedKeys = keys - r.expiry = expiry } // Free inflight so a different request can run. @@ -189,40 +178,31 @@ func (r *remoteKeySet) keysFromRemote(ctx context.Context) ([]jose.JSONWebKey, e } } -func (r *remoteKeySet) updateKeys() ([]jose.JSONWebKey, time.Time, error) { +func (r *RemoteKeySet) updateKeys() ([]jose.JSONWebKey, error) { req, err := http.NewRequest("GET", r.jwksURL, nil) if err != nil { - return nil, time.Time{}, fmt.Errorf("oidc: can't create request: %v", err) + return nil, fmt.Errorf("oidc: can't create request: %v", err) } resp, err := doRequest(r.ctx, req) if err != nil { - return nil, time.Time{}, fmt.Errorf("oidc: get keys failed %v", err) + return nil, fmt.Errorf("oidc: get keys failed %v", err) } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, time.Time{}, fmt.Errorf("unable to read response body: %v", err) + return nil, fmt.Errorf("unable to read response body: %v", err) } if resp.StatusCode != http.StatusOK { - return nil, time.Time{}, fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body) + return nil, fmt.Errorf("oidc: get keys failed: %s %s", resp.Status, body) } var keySet jose.JSONWebKeySet err = unmarshalResp(resp, body, &keySet) if err != nil { - return nil, time.Time{}, fmt.Errorf("oidc: failed to decode keys: %v %s", err, body) + return nil, fmt.Errorf("oidc: failed to decode keys: %v %s", err, body) } - - // If the server doesn't provide cache control headers, assume the - // keys expire immediately. - expiry := r.now() - - _, e, err := cachecontrol.CachableResponse(req, resp, cachecontrol.Options{}) - if err == nil && e.After(expiry) { - expiry = e - } - return keySet.Keys, expiry, nil + return keySet.Keys, nil } diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go similarity index 74% rename from vendor/gopkg.in/coreos/go-oidc.v2/oidc.go rename to vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go index 508b39d3..3e1d80e0 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/oidc.go +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/oidc.go @@ -17,7 +17,6 @@ import ( "time" "golang.org/x/oauth2" - jose "gopkg.in/square/go-jose.v2" ) const ( @@ -40,6 +39,10 @@ var ( errInvalidAtHash = errors.New("access token hash does not match value in ID token") ) +type contextKey int + +var issuerURLKey contextKey + // ClientContext returns a new Context that carries the provided HTTP client. // // This method sets the same context key used by the golang.org/x/oauth2 package, @@ -55,6 +58,36 @@ func ClientContext(ctx context.Context, client *http.Client) context.Context { return context.WithValue(ctx, oauth2.HTTPClient, client) } +// cloneContext copies a context's bag-of-values into a new context that isn't +// associated with its cancellation. This is used to initialize remote keys sets +// which run in the background and aren't associated with the initial context. +func cloneContext(ctx context.Context) context.Context { + cp := context.Background() + if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { + cp = ClientContext(cp, c) + } + return cp +} + +// InsecureIssuerURLContext allows discovery to work when the issuer_url reported +// by upstream is mismatched with the discovery URL. This is meant for integration +// with off-spec providers such as Azure. +// +// discoveryBaseURL := "https://login.microsoftonline.com/organizations/v2.0" +// issuerURL := "https://login.microsoftonline.com/my-tenantid/v2.0" +// +// ctx := oidc.InsecureIssuerURLContext(parentContext, issuerURL) +// +// // Provider will be discovered with the discoveryBaseURL, but use issuerURL +// // for future issuer validation. +// provider, err := oidc.NewProvider(ctx, discoveryBaseURL) +// +// This is insecure because validating the correct issuer is critical for multi-tenant +// proivders. Any overrides here MUST be carefully reviewed. +func InsecureIssuerURLContext(ctx context.Context, issuerURL string) context.Context { + return context.WithValue(ctx, issuerURLKey, issuerURL) +} + func doRequest(ctx context.Context, req *http.Request) (*http.Response, error) { client := http.DefaultClient if c, ok := ctx.Value(oauth2.HTTPClient).(*http.Client); ok { @@ -69,6 +102,7 @@ type Provider struct { authURL string tokenURL string userInfoURL string + algorithms []string // Raw claims returned by the server. rawClaims []byte @@ -76,17 +110,28 @@ type Provider struct { remoteKeySet KeySet } -type cachedKeys struct { - keys []jose.JSONWebKey - expiry time.Time +type providerJSON struct { + Issuer string `json:"issuer"` + AuthURL string `json:"authorization_endpoint"` + TokenURL string `json:"token_endpoint"` + JWKSURL string `json:"jwks_uri"` + UserInfoURL string `json:"userinfo_endpoint"` + Algorithms []string `json:"id_token_signing_alg_values_supported"` } -type providerJSON struct { - Issuer string `json:"issuer"` - AuthURL string `json:"authorization_endpoint"` - TokenURL string `json:"token_endpoint"` - JWKSURL string `json:"jwks_uri"` - UserInfoURL string `json:"userinfo_endpoint"` +// supportedAlgorithms is a list of algorithms explicitly supported by this +// package. If a provider supports other algorithms, such as HS256 or none, +// those values won't be passed to the IDTokenVerifier. +var supportedAlgorithms = map[string]bool{ + RS256: true, + RS384: true, + RS512: true, + ES256: true, + ES384: true, + ES512: true, + PS256: true, + PS384: true, + PS512: true, } // NewProvider uses the OpenID Connect discovery mechanism to construct a Provider. @@ -120,16 +165,27 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) { return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err) } - if p.Issuer != issuer { + issuerURL, skipIssuerValidation := ctx.Value(issuerURLKey).(string) + if !skipIssuerValidation { + issuerURL = issuer + } + if p.Issuer != issuerURL && !skipIssuerValidation { return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer) } + var algs []string + for _, a := range p.Algorithms { + if supportedAlgorithms[a] { + algs = append(algs, a) + } + } return &Provider{ - issuer: p.Issuer, + issuer: issuerURL, authURL: p.AuthURL, tokenURL: p.TokenURL, userInfoURL: p.UserInfoURL, + algorithms: algs, rawClaims: body, - remoteKeySet: NewRemoteKeySet(ctx, p.JWKSURL), + remoteKeySet: NewRemoteKeySet(cloneContext(ctx), p.JWKSURL), }, nil } @@ -168,6 +224,16 @@ type UserInfo struct { claims []byte } +type userInfoRaw struct { + Subject string `json:"sub"` + Profile string `json:"profile"` + Email string `json:"email"` + // Handle providers that return email_verified as a string + // https://forums.aws.amazon.com/thread.jspa?messageID=949441󧳁 and + // https://discuss.elastic.co/t/openid-error-after-authenticating-against-aws-cognito/206018/11 + EmailVerified stringAsBool `json:"email_verified"` +} + // Claims unmarshals the raw JSON object claims into the provided object. func (u *UserInfo) Claims(v interface{}) error { if u.claims == nil { @@ -206,12 +272,27 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource) return nil, fmt.Errorf("%s: %s", resp.Status, body) } - var userInfo UserInfo + ct := resp.Header.Get("Content-Type") + mediaType, _, parseErr := mime.ParseMediaType(ct) + if parseErr == nil && mediaType == "application/jwt" { + payload, err := p.remoteKeySet.VerifySignature(ctx, string(body)) + if err != nil { + return nil, fmt.Errorf("oidc: invalid userinfo jwt signature %v", err) + } + body = payload + } + + var userInfo userInfoRaw if err := json.Unmarshal(body, &userInfo); err != nil { return nil, fmt.Errorf("oidc: failed to decode userinfo: %v", err) } - userInfo.claims = body - return &userInfo, nil + return &UserInfo{ + Subject: userInfo.Subject, + Profile: userInfo.Profile, + Email: userInfo.Email, + EmailVerified: bool(userInfo.EmailVerified), + claims: body, + }, nil } // IDToken is an OpenID Connect extension that provides a predictable representation @@ -333,6 +414,20 @@ type claimSource struct { AccessToken string `json:"access_token"` } +type stringAsBool bool + +func (sb *stringAsBool) UnmarshalJSON(b []byte) error { + switch string(b) { + case "true", `"true"`: + *sb = true + case "false", `"false"`: + *sb = false + default: + return errors.New("invalid value for boolean") + } + return nil +} + type audience []string func (a *audience) UnmarshalJSON(b []byte) error { @@ -345,7 +440,7 @@ func (a *audience) UnmarshalJSON(b []byte) error { if err := json.Unmarshal(b, &auds); err != nil { return err } - *a = audience(auds) + *a = auds return nil } diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go similarity index 96% rename from vendor/gopkg.in/coreos/go-oidc.v2/verify.go rename to vendor/github.com/coreos/go-oidc/v3/oidc/verify.go index ff7555db..dc6b56df 100644 --- a/vendor/gopkg.in/coreos/go-oidc.v2/verify.go +++ b/vendor/github.com/coreos/go-oidc/v3/oidc/verify.go @@ -79,7 +79,9 @@ type Config struct { ClientID string // 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. + // If the IDTokenVerifier is created from a provider with (*Provider).Verifier, this + // defaults to the set of algorithms the provider supports. Otherwise this values + // defaults to RS256. SupportedSigningAlgs []string // If true, no ClientID check performed. Must be true if ClientID field is empty. @@ -105,6 +107,13 @@ type Config struct { // The returned IDTokenVerifier is tied to the Provider's context and its behavior is // undefined once the Provider's context is canceled. func (p *Provider) Verifier(config *Config) *IDTokenVerifier { + if len(config.SupportedSigningAlgs) == 0 && len(p.algorithms) > 0 { + // Make a copy so we don't modify the config values. + cp := &Config{} + *cp = *config + cp.SupportedSigningAlgs = p.algorithms + config = cp + } return NewVerifier(p.issuer, p.remoteKeySet, config) } @@ -162,21 +171,7 @@ func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src return token.claims, nil } -func parseClaim(raw []byte, name string, v interface{}) error { - var parsed map[string]json.RawMessage - if err := json.Unmarshal(raw, &parsed); err != nil { - return err - } - - val, ok := parsed[name] - if !ok { - return fmt.Errorf("claim doesn't exist: %s", name) - } - - return json.Unmarshal([]byte(val), v) -} - -// 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, performs // any additional checks depending on the Config, and returns the payload. // // Verify does NOT do nonce validation, which is the callers responsibility. diff --git a/vendor/github.com/pquerna/cachecontrol/.travis.yml b/vendor/github.com/pquerna/cachecontrol/.travis.yml deleted file mode 100644 index f140b497..00000000 --- a/vendor/github.com/pquerna/cachecontrol/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go - -install: - - go get -d -v ./... - - go get -u github.com/stretchr/testify/require - -go: - - 1.7 - - 1.8 - - tip diff --git a/vendor/github.com/pquerna/cachecontrol/LICENSE b/vendor/github.com/pquerna/cachecontrol/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/vendor/github.com/pquerna/cachecontrol/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/github.com/pquerna/cachecontrol/README.md b/vendor/github.com/pquerna/cachecontrol/README.md deleted file mode 100644 index da0b428f..00000000 --- a/vendor/github.com/pquerna/cachecontrol/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# cachecontrol: HTTP Caching Parser and Interpretation - -[![GoDoc](https://godoc.org/github.com/pquerna/cachecontrol?status.svg)](https://godoc.org/github.com/pquerna/cachecontrol)[![Build Status](https://travis-ci.org/pquerna/cachecontrol.svg?branch=master)](https://travis-ci.org/pquerna/cachecontrol) - - - -`cachecontrol` implements [RFC 7234](http://tools.ietf.org/html/rfc7234) __Hypertext Transfer Protocol (HTTP/1.1): Caching__. It does this by parsing the `Cache-Control` and other headers, providing information about requests and responses -- but `cachecontrol` does not implement an actual cache backend, just the control plane to make decisions about if a particular response is cachable. - -# Usage - -`cachecontrol.CachableResponse` returns an array of [reasons](https://godoc.org/github.com/pquerna/cachecontrol/cacheobject#Reason) why a response should not be cached and when it expires. In the case that `len(reasons) == 0`, the response is cachable according to the RFC. However, some people want non-compliant caches for various business use cases, so each reason is specifically named, so if your cache wants to cache `POST` requests, it can easily do that, but still be RFC compliant in other situations. - -# Examples - -## Can you cache Example.com? - -```go -package main - -import ( - "github.com/pquerna/cachecontrol" - - "fmt" - "io/ioutil" - "net/http" -) - -func main() { - req, _ := http.NewRequest("GET", "http://www.example.com/", nil) - - res, _ := http.DefaultClient.Do(req) - _, _ = ioutil.ReadAll(res.Body) - - reasons, expires, _ := cachecontrol.CachableResponse(req, res, cachecontrol.Options{}) - - fmt.Println("Reasons to not cache: ", reasons) - fmt.Println("Expiration: ", expires.String()) -} -``` - -## Can I use this in a high performance caching server? - -`cachecontrol` is divided into two packages: `cachecontrol` with a high level API, and a lower level `cacheobject` package. Use [Object](https://godoc.org/github.com/pquerna/cachecontrol/cacheobject#Object) in a high performance use case where you have previously parsed headers containing dates or would like to avoid memory allocations. - -```go -package main - -import ( - "github.com/pquerna/cachecontrol/cacheobject" - - "fmt" - "io/ioutil" - "net/http" -) - -func main() { - req, _ := http.NewRequest("GET", "http://www.example.com/", nil) - - res, _ := http.DefaultClient.Do(req) - _, _ = ioutil.ReadAll(res.Body) - - reqDir, _ := cacheobject.ParseRequestCacheControl(req.Header.Get("Cache-Control")) - - resDir, _ := cacheobject.ParseResponseCacheControl(res.Header.Get("Cache-Control")) - expiresHeader, _ := http.ParseTime(res.Header.Get("Expires")) - dateHeader, _ := http.ParseTime(res.Header.Get("Date")) - lastModifiedHeader, _ := http.ParseTime(res.Header.Get("Last-Modified")) - - obj := cacheobject.Object{ - RespDirectives: resDir, - RespHeaders: res.Header, - RespStatusCode: res.StatusCode, - RespExpiresHeader: expiresHeader, - RespDateHeader: dateHeader, - RespLastModifiedHeader: lastModifiedHeader, - - ReqDirectives: reqDir, - ReqHeaders: req.Header, - ReqMethod: req.Method, - - NowUTC: time.Now().UTC(), - } - rv := cacheobject.ObjectResults{} - - cacheobject.CachableObject(&obj, &rv) - cacheobject.ExpirationObject(&obj, &rv) - - fmt.Println("Errors: ", rv.OutErr) - fmt.Println("Reasons to not cache: ", rv.OutReasons) - fmt.Println("Warning headers to add: ", rv.OutWarnings) - fmt.Println("Expiration: ", rv.OutExpirationTime.String()) -} -``` - -## Improvements, bugs, adding features, and taking cachecontrol new directions! - -Please [open issues in Github](https://github.com/pquerna/cachecontrol/issues) for ideas, bugs, and general thoughts. Pull requests are of course preferred :) - -# Credits - -`cachecontrol` has recieved significant contributions from: - -* [Paul Querna](https://github.com/pquerna) - -## License - -`cachecontrol` is licensed under the [Apache License, Version 2.0](./LICENSE) diff --git a/vendor/github.com/pquerna/cachecontrol/api.go b/vendor/github.com/pquerna/cachecontrol/api.go deleted file mode 100644 index f6f28585..00000000 --- a/vendor/github.com/pquerna/cachecontrol/api.go +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2015 Paul Querna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cachecontrol - -import ( - "github.com/pquerna/cachecontrol/cacheobject" - - "net/http" - "time" -) - -type Options struct { - // Set to True for a prviate cache, which is not shared amoung users (eg, in a browser) - // Set to False for a "shared" cache, which is more common in a server context. - PrivateCache bool -} - -// Given an HTTP Request, the future Status Code, and an ResponseWriter, -// determine the possible reasons a response SHOULD NOT be cached. -func CachableResponseWriter(req *http.Request, - statusCode int, - resp http.ResponseWriter, - opts Options) ([]cacheobject.Reason, time.Time, error) { - return cacheobject.UsingRequestResponse(req, statusCode, resp.Header(), opts.PrivateCache) -} - -// Given an HTTP Request and Response, determine the possible reasons a response SHOULD NOT -// be cached. -func CachableResponse(req *http.Request, - resp *http.Response, - opts Options) ([]cacheobject.Reason, time.Time, error) { - return cacheobject.UsingRequestResponse(req, resp.StatusCode, resp.Header, opts.PrivateCache) -} diff --git a/vendor/github.com/pquerna/cachecontrol/cacheobject/directive.go b/vendor/github.com/pquerna/cachecontrol/cacheobject/directive.go deleted file mode 100644 index afa640e7..00000000 --- a/vendor/github.com/pquerna/cachecontrol/cacheobject/directive.go +++ /dev/null @@ -1,546 +0,0 @@ -/** - * Copyright 2015 Paul Querna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cacheobject - -import ( - "errors" - "math" - "net/http" - "net/textproto" - "strconv" - "strings" -) - -// TODO(pquerna): add extensions from here: http://www.iana.org/assignments/http-cache-directives/http-cache-directives.xhtml - -var ( - ErrQuoteMismatch = errors.New("Missing closing quote") - ErrMaxAgeDeltaSeconds = errors.New("Failed to parse delta-seconds in `max-age`") - ErrSMaxAgeDeltaSeconds = errors.New("Failed to parse delta-seconds in `s-maxage`") - ErrMaxStaleDeltaSeconds = errors.New("Failed to parse delta-seconds in `min-fresh`") - ErrMinFreshDeltaSeconds = errors.New("Failed to parse delta-seconds in `min-fresh`") - ErrNoCacheNoArgs = errors.New("Unexpected argument to `no-cache`") - ErrNoStoreNoArgs = errors.New("Unexpected argument to `no-store`") - ErrNoTransformNoArgs = errors.New("Unexpected argument to `no-transform`") - ErrOnlyIfCachedNoArgs = errors.New("Unexpected argument to `only-if-cached`") - ErrMustRevalidateNoArgs = errors.New("Unexpected argument to `must-revalidate`") - ErrPublicNoArgs = errors.New("Unexpected argument to `public`") - ErrProxyRevalidateNoArgs = errors.New("Unexpected argument to `proxy-revalidate`") - // Experimental - ErrImmutableNoArgs = errors.New("Unexpected argument to `immutable`") - ErrStaleIfErrorDeltaSeconds = errors.New("Failed to parse delta-seconds in `stale-if-error`") - ErrStaleWhileRevalidateDeltaSeconds = errors.New("Failed to parse delta-seconds in `stale-while-revalidate`") -) - -func whitespace(b byte) bool { - if b == '\t' || b == ' ' { - return true - } - return false -} - -func parse(value string, cd cacheDirective) error { - var err error = nil - i := 0 - - for i < len(value) && err == nil { - // eat leading whitespace or commas - if whitespace(value[i]) || value[i] == ',' { - i++ - continue - } - - j := i + 1 - - for j < len(value) { - if !isToken(value[j]) { - break - } - j++ - } - - token := strings.ToLower(value[i:j]) - tokenHasFields := hasFieldNames(token) - /* - println("GOT TOKEN:") - println(" i -> ", i) - println(" j -> ", j) - println(" token -> ", token) - */ - - if j+1 < len(value) && value[j] == '=' { - k := j + 1 - // minimum size two bytes of "", but we let httpUnquote handle it. - if k < len(value) && value[k] == '"' { - eaten, result := httpUnquote(value[k:]) - if eaten == -1 { - return ErrQuoteMismatch - } - i = k + eaten - - err = cd.addPair(token, result) - } else { - z := k - for z < len(value) { - if tokenHasFields { - if whitespace(value[z]) { - break - } - } else { - if whitespace(value[z]) || value[z] == ',' { - break - } - } - z++ - } - i = z - - result := value[k:z] - if result != "" && result[len(result)-1] == ',' { - result = result[:len(result)-1] - } - - err = cd.addPair(token, result) - } - } else { - if token != "," { - err = cd.addToken(token) - } - i = j - } - } - - return err -} - -// DeltaSeconds specifies a non-negative integer, representing -// time in seconds: http://tools.ietf.org/html/rfc7234#section-1.2.1 -// -// When set to -1, this means unset. -// -type DeltaSeconds int32 - -// Parser for delta-seconds, a uint31, more or less: -// http://tools.ietf.org/html/rfc7234#section-1.2.1 -func parseDeltaSeconds(v string) (DeltaSeconds, error) { - n, err := strconv.ParseUint(v, 10, 32) - if err != nil { - if numError, ok := err.(*strconv.NumError); ok { - if numError.Err == strconv.ErrRange { - return DeltaSeconds(math.MaxInt32), nil - } - } - return DeltaSeconds(-1), err - } else { - if n > math.MaxInt32 { - return DeltaSeconds(math.MaxInt32), nil - } else { - return DeltaSeconds(n), nil - } - } -} - -// Fields present in a header. -type FieldNames map[string]bool - -// internal interface for shared methods of RequestCacheDirectives and ResponseCacheDirectives -type cacheDirective interface { - addToken(s string) error - addPair(s string, v string) error -} - -// LOW LEVEL API: Repersentation of possible request directives in a `Cache-Control` header: http://tools.ietf.org/html/rfc7234#section-5.2.1 -// -// Note: Many fields will be `nil` in practice. -// -type RequestCacheDirectives struct { - - // max-age(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.1 - // - // The "max-age" request directive indicates that the client is - // unwilling to accept a response whose age is greater than the - // specified number of seconds. Unless the max-stale request directive - // is also present, the client is not willing to accept a stale - // response. - MaxAge DeltaSeconds - - // max-stale(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.2 - // - // The "max-stale" request directive indicates that the client is - // willing to accept a response that has exceeded its freshness - // lifetime. If max-stale is assigned a value, then the client is - // willing to accept a response that has exceeded its freshness lifetime - // by no more than the specified number of seconds. If no value is - // assigned to max-stale, then the client is willing to accept a stale - // response of any age. - MaxStale DeltaSeconds - - // min-fresh(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.1.3 - // - // The "min-fresh" request directive indicates that the client is - // willing to accept a response whose freshness lifetime is no less than - // its current age plus the specified time in seconds. That is, the - // client wants a response that will still be fresh for at least the - // specified number of seconds. - MinFresh DeltaSeconds - - // no-cache(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.4 - // - // The "no-cache" request directive indicates that a cache MUST NOT use - // a stored response to satisfy the request without successful - // validation on the origin server. - NoCache bool - - // no-store(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.5 - // - // The "no-store" request directive indicates that a cache MUST NOT - // store any part of either this request or any response to it. This - // directive applies to both private and shared caches. - NoStore bool - - // no-transform(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.6 - // - // The "no-transform" request directive indicates that an intermediary - // (whether or not it implements a cache) MUST NOT transform the - // payload, as defined in Section 5.7.2 of RFC7230. - NoTransform bool - - // only-if-cached(bool): http://tools.ietf.org/html/rfc7234#section-5.2.1.7 - // - // The "only-if-cached" request directive indicates that the client only - // wishes to obtain a stored response. - OnlyIfCached bool - - // Extensions: http://tools.ietf.org/html/rfc7234#section-5.2.3 - // - // The Cache-Control header field can be extended through the use of one - // or more cache-extension tokens, each with an optional value. A cache - // MUST ignore unrecognized cache directives. - Extensions []string -} - -func (cd *RequestCacheDirectives) addToken(token string) error { - var err error = nil - - switch token { - case "max-age": - err = ErrMaxAgeDeltaSeconds - case "max-stale": - err = ErrMaxStaleDeltaSeconds - case "min-fresh": - err = ErrMinFreshDeltaSeconds - case "no-cache": - cd.NoCache = true - case "no-store": - cd.NoStore = true - case "no-transform": - cd.NoTransform = true - case "only-if-cached": - cd.OnlyIfCached = true - default: - cd.Extensions = append(cd.Extensions, token) - } - return err -} - -func (cd *RequestCacheDirectives) addPair(token string, v string) error { - var err error = nil - - switch token { - case "max-age": - cd.MaxAge, err = parseDeltaSeconds(v) - if err != nil { - err = ErrMaxAgeDeltaSeconds - } - case "max-stale": - cd.MaxStale, err = parseDeltaSeconds(v) - if err != nil { - err = ErrMaxStaleDeltaSeconds - } - case "min-fresh": - cd.MinFresh, err = parseDeltaSeconds(v) - if err != nil { - err = ErrMinFreshDeltaSeconds - } - case "no-cache": - err = ErrNoCacheNoArgs - case "no-store": - err = ErrNoStoreNoArgs - case "no-transform": - err = ErrNoTransformNoArgs - case "only-if-cached": - err = ErrOnlyIfCachedNoArgs - default: - // TODO(pquerna): this sucks, making user re-parse - cd.Extensions = append(cd.Extensions, token+"="+v) - } - - return err -} - -// LOW LEVEL API: Parses a Cache Control Header from a Request into a set of directives. -func ParseRequestCacheControl(value string) (*RequestCacheDirectives, error) { - cd := &RequestCacheDirectives{ - MaxAge: -1, - MaxStale: -1, - MinFresh: -1, - } - - err := parse(value, cd) - if err != nil { - return nil, err - } - return cd, nil -} - -// LOW LEVEL API: Repersentation of possible response directives in a `Cache-Control` header: http://tools.ietf.org/html/rfc7234#section-5.2.2 -// -// Note: Many fields will be `nil` in practice. -// -type ResponseCacheDirectives struct { - - // must-revalidate(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.1 - // - // The "must-revalidate" response directive indicates that once it has - // become stale, a cache MUST NOT use the response to satisfy subsequent - // requests without successful validation on the origin server. - MustRevalidate bool - - // no-cache(FieldName): http://tools.ietf.org/html/rfc7234#section-5.2.2.2 - // - // The "no-cache" response directive indicates that the response MUST - // NOT be used to satisfy a subsequent request without successful - // validation on the origin server. - // - // If the no-cache response directive specifies one or more field-names, - // then a cache MAY use the response to satisfy a subsequent request, - // subject to any other restrictions on caching. However, any header - // fields in the response that have the field-name(s) listed MUST NOT be - // sent in the response to a subsequent request without successful - // revalidation with the origin server. - NoCache FieldNames - - // no-cache(cast-to-bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.2 - // - // While the RFC defines optional field-names on a no-cache directive, - // many applications only want to know if any no-cache directives were - // present at all. - NoCachePresent bool - - // no-store(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.3 - // - // The "no-store" request directive indicates that a cache MUST NOT - // store any part of either this request or any response to it. This - // directive applies to both private and shared caches. - NoStore bool - - // no-transform(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.4 - // - // The "no-transform" response directive indicates that an intermediary - // (regardless of whether it implements a cache) MUST NOT transform the - // payload, as defined in Section 5.7.2 of RFC7230. - NoTransform bool - - // public(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.5 - // - // The "public" response directive indicates that any cache MAY store - // the response, even if the response would normally be non-cacheable or - // cacheable only within a private cache. - Public bool - - // private(FieldName): http://tools.ietf.org/html/rfc7234#section-5.2.2.6 - // - // The "private" response directive indicates that the response message - // is intended for a single user and MUST NOT be stored by a shared - // cache. A private cache MAY store the response and reuse it for later - // requests, even if the response would normally be non-cacheable. - // - // If the private response directive specifies one or more field-names, - // this requirement is limited to the field-values associated with the - // listed response header fields. That is, a shared cache MUST NOT - // store the specified field-names(s), whereas it MAY store the - // remainder of the response message. - Private FieldNames - - // private(cast-to-bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.6 - // - // While the RFC defines optional field-names on a private directive, - // many applications only want to know if any private directives were - // present at all. - PrivatePresent bool - - // proxy-revalidate(bool): http://tools.ietf.org/html/rfc7234#section-5.2.2.7 - // - // The "proxy-revalidate" response directive has the same meaning as the - // must-revalidate response directive, except that it does not apply to - // private caches. - ProxyRevalidate bool - - // max-age(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.2.8 - // - // The "max-age" response directive indicates that the response is to be - // considered stale after its age is greater than the specified number - // of seconds. - MaxAge DeltaSeconds - - // s-maxage(delta seconds): http://tools.ietf.org/html/rfc7234#section-5.2.2.9 - // - // The "s-maxage" response directive indicates that, in shared caches, - // the maximum age specified by this directive overrides the maximum age - // specified by either the max-age directive or the Expires header - // field. The s-maxage directive also implies the semantics of the - // proxy-revalidate response directive. - SMaxAge DeltaSeconds - - //// - // Experimental features - // - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#Extension_Cache-Control_directives - // - https://www.fastly.com/blog/stale-while-revalidate-stale-if-error-available-today - //// - - // immutable(cast-to-bool): experimental feature - Immutable bool - - // stale-if-error(delta seconds): experimental feature - StaleIfError DeltaSeconds - - // stale-while-revalidate(delta seconds): experimental feature - StaleWhileRevalidate DeltaSeconds - - // Extensions: http://tools.ietf.org/html/rfc7234#section-5.2.3 - // - // The Cache-Control header field can be extended through the use of one - // or more cache-extension tokens, each with an optional value. A cache - // MUST ignore unrecognized cache directives. - Extensions []string -} - -// LOW LEVEL API: Parses a Cache Control Header from a Response into a set of directives. -func ParseResponseCacheControl(value string) (*ResponseCacheDirectives, error) { - cd := &ResponseCacheDirectives{ - MaxAge: -1, - SMaxAge: -1, - // Exerimantal stale timeouts - StaleIfError: -1, - StaleWhileRevalidate: -1, - } - - err := parse(value, cd) - if err != nil { - return nil, err - } - return cd, nil -} - -func (cd *ResponseCacheDirectives) addToken(token string) error { - var err error = nil - switch token { - case "must-revalidate": - cd.MustRevalidate = true - case "no-cache": - cd.NoCachePresent = true - case "no-store": - cd.NoStore = true - case "no-transform": - cd.NoTransform = true - case "public": - cd.Public = true - case "private": - cd.PrivatePresent = true - case "proxy-revalidate": - cd.ProxyRevalidate = true - case "max-age": - err = ErrMaxAgeDeltaSeconds - case "s-maxage": - err = ErrSMaxAgeDeltaSeconds - // Experimental - case "immutable": - cd.Immutable = true - case "stale-if-error": - err = ErrMaxAgeDeltaSeconds - case "stale-while-revalidate": - err = ErrMaxAgeDeltaSeconds - default: - cd.Extensions = append(cd.Extensions, token) - } - return err -} - -func hasFieldNames(token string) bool { - switch token { - case "no-cache": - return true - case "private": - return true - } - return false -} - -func (cd *ResponseCacheDirectives) addPair(token string, v string) error { - var err error = nil - - switch token { - case "must-revalidate": - err = ErrMustRevalidateNoArgs - case "no-cache": - cd.NoCachePresent = true - tokens := strings.Split(v, ",") - if cd.NoCache == nil { - cd.NoCache = make(FieldNames) - } - for _, t := range tokens { - k := http.CanonicalHeaderKey(textproto.TrimString(t)) - cd.NoCache[k] = true - } - case "no-store": - err = ErrNoStoreNoArgs - case "no-transform": - err = ErrNoTransformNoArgs - case "public": - err = ErrPublicNoArgs - case "private": - cd.PrivatePresent = true - tokens := strings.Split(v, ",") - if cd.Private == nil { - cd.Private = make(FieldNames) - } - for _, t := range tokens { - k := http.CanonicalHeaderKey(textproto.TrimString(t)) - cd.Private[k] = true - } - case "proxy-revalidate": - err = ErrProxyRevalidateNoArgs - case "max-age": - cd.MaxAge, err = parseDeltaSeconds(v) - case "s-maxage": - cd.SMaxAge, err = parseDeltaSeconds(v) - // Experimental - case "immutable": - err = ErrImmutableNoArgs - case "stale-if-error": - cd.StaleIfError, err = parseDeltaSeconds(v) - case "stale-while-revalidate": - cd.StaleWhileRevalidate, err = parseDeltaSeconds(v) - default: - // TODO(pquerna): this sucks, making user re-parse, and its technically not 'quoted' like the original, - // but this is still easier, just a SplitN on "=" - cd.Extensions = append(cd.Extensions, token+"="+v) - } - - return err -} diff --git a/vendor/github.com/pquerna/cachecontrol/cacheobject/lex.go b/vendor/github.com/pquerna/cachecontrol/cacheobject/lex.go deleted file mode 100644 index c658e09b..00000000 --- a/vendor/github.com/pquerna/cachecontrol/cacheobject/lex.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2009 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. - -package cacheobject - -// This file deals with lexical matters of HTTP - -func isSeparator(c byte) bool { - switch c { - case '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t': - return true - } - return false -} - -func isCtl(c byte) bool { return (0 <= c && c <= 31) || c == 127 } - -func isChar(c byte) bool { return 0 <= c && c <= 127 } - -func isAnyText(c byte) bool { return !isCtl(c) } - -func isQdText(c byte) bool { return isAnyText(c) && c != '"' } - -func isToken(c byte) bool { return isChar(c) && !isCtl(c) && !isSeparator(c) } - -// Valid escaped sequences are not specified in RFC 2616, so for now, we assume -// that they coincide with the common sense ones used by GO. Malformed -// characters should probably not be treated as errors by a robust (forgiving) -// parser, so we replace them with the '?' character. -func httpUnquotePair(b byte) byte { - // skip the first byte, which should always be '\' - switch b { - case 'a': - return '\a' - case 'b': - return '\b' - case 'f': - return '\f' - case 'n': - return '\n' - case 'r': - return '\r' - case 't': - return '\t' - case 'v': - return '\v' - case '\\': - return '\\' - case '\'': - return '\'' - case '"': - return '"' - } - return '?' -} - -// raw must begin with a valid quoted string. Only the first quoted string is -// parsed and is unquoted in result. eaten is the number of bytes parsed, or -1 -// upon failure. -func httpUnquote(raw string) (eaten int, result string) { - buf := make([]byte, len(raw)) - if raw[0] != '"' { - return -1, "" - } - eaten = 1 - j := 0 // # of bytes written in buf - for i := 1; i < len(raw); i++ { - switch b := raw[i]; b { - case '"': - eaten++ - buf = buf[0:j] - return i + 1, string(buf) - case '\\': - if len(raw) < i+2 { - return -1, "" - } - buf[j] = httpUnquotePair(raw[i+1]) - eaten += 2 - j++ - i++ - default: - if isQdText(b) { - buf[j] = b - } else { - buf[j] = '?' - } - eaten++ - j++ - } - } - return -1, "" -} diff --git a/vendor/github.com/pquerna/cachecontrol/cacheobject/object.go b/vendor/github.com/pquerna/cachecontrol/cacheobject/object.go deleted file mode 100644 index c20a1d65..00000000 --- a/vendor/github.com/pquerna/cachecontrol/cacheobject/object.go +++ /dev/null @@ -1,387 +0,0 @@ -/** - * Copyright 2015 Paul Querna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cacheobject - -import ( - "net/http" - "time" -) - -// LOW LEVEL API: Repersents a potentially cachable HTTP object. -// -// This struct is designed to be serialized efficiently, so in a high -// performance caching server, things like Date-Strings don't need to be -// parsed for every use of a cached object. -type Object struct { - CacheIsPrivate bool - - RespDirectives *ResponseCacheDirectives - RespHeaders http.Header - RespStatusCode int - RespExpiresHeader time.Time - RespDateHeader time.Time - RespLastModifiedHeader time.Time - - ReqDirectives *RequestCacheDirectives - ReqHeaders http.Header - ReqMethod string - - NowUTC time.Time -} - -// LOW LEVEL API: Repersents the results of examinig an Object with -// CachableObject and ExpirationObject. -// -// TODO(pquerna): decide if this is a good idea or bad -type ObjectResults struct { - OutReasons []Reason - OutWarnings []Warning - OutExpirationTime time.Time - OutErr error -} - -// LOW LEVEL API: Check if a object is cachable. -func CachableObject(obj *Object, rv *ObjectResults) { - rv.OutReasons = nil - rv.OutWarnings = nil - rv.OutErr = nil - - switch obj.ReqMethod { - case "GET": - break - case "HEAD": - break - case "POST": - /** - POST: http://tools.ietf.org/html/rfc7231#section-4.3.3 - - Responses to POST requests are only cacheable when they include - explicit freshness information (see Section 4.2.1 of [RFC7234]). - However, POST caching is not widely implemented. For cases where an - origin server wishes the client to be able to cache the result of a - POST in a way that can be reused by a later GET, the origin server - MAY send a 200 (OK) response containing the result and a - Content-Location header field that has the same value as the POST's - effective request URI (Section 3.1.4.2). - */ - if !hasFreshness(obj.ReqDirectives, obj.RespDirectives, obj.RespHeaders, obj.RespExpiresHeader, obj.CacheIsPrivate) { - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPOST) - } - - case "PUT": - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodPUT) - - case "DELETE": - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodDELETE) - - case "CONNECT": - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodCONNECT) - - case "OPTIONS": - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodOPTIONS) - - case "TRACE": - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodTRACE) - - // HTTP Extension Methods: http://www.iana.org/assignments/http-methods/http-methods.xhtml - // - // To my knowledge, none of them are cachable. Please open a ticket if this is not the case! - // - default: - rv.OutReasons = append(rv.OutReasons, ReasonRequestMethodUnkown) - } - - if obj.ReqDirectives.NoStore { - rv.OutReasons = append(rv.OutReasons, ReasonRequestNoStore) - } - - // Storing Responses to Authenticated Requests: http://tools.ietf.org/html/rfc7234#section-3.2 - authz := obj.ReqHeaders.Get("Authorization") - if authz != "" { - if obj.RespDirectives.MustRevalidate || - obj.RespDirectives.Public || - obj.RespDirectives.SMaxAge != -1 { - // Expires of some kind present, this is potentially OK. - } else { - rv.OutReasons = append(rv.OutReasons, ReasonRequestAuthorizationHeader) - } - } - - if obj.RespDirectives.PrivatePresent && !obj.CacheIsPrivate { - rv.OutReasons = append(rv.OutReasons, ReasonResponsePrivate) - } - - if obj.RespDirectives.NoStore { - rv.OutReasons = append(rv.OutReasons, ReasonResponseNoStore) - } - - /* - the response either: - - * contains an Expires header field (see Section 5.3), or - - * contains a max-age response directive (see Section 5.2.2.8), or - - * contains a s-maxage response directive (see Section 5.2.2.9) - and the cache is shared, or - - * contains a Cache Control Extension (see Section 5.2.3) that - allows it to be cached, or - - * has a status code that is defined as cacheable by default (see - Section 4.2.2), or - - * contains a public response directive (see Section 5.2.2.5). - */ - - expires := obj.RespHeaders.Get("Expires") != "" - statusCachable := cachableStatusCode(obj.RespStatusCode) - - if expires || - obj.RespDirectives.MaxAge != -1 || - (obj.RespDirectives.SMaxAge != -1 && !obj.CacheIsPrivate) || - statusCachable || - obj.RespDirectives.Public { - /* cachable by default, at least one of the above conditions was true */ - } else { - rv.OutReasons = append(rv.OutReasons, ReasonResponseUncachableByDefault) - } -} - -var twentyFourHours = time.Duration(24 * time.Hour) - -const debug = false - -// LOW LEVEL API: Update an objects expiration time. -func ExpirationObject(obj *Object, rv *ObjectResults) { - /** - * Okay, lets calculate Freshness/Expiration now. woo: - * http://tools.ietf.org/html/rfc7234#section-4.2 - */ - - /* - o If the cache is shared and the s-maxage response directive - (Section 5.2.2.9) is present, use its value, or - - o If the max-age response directive (Section 5.2.2.8) is present, - use its value, or - - o If the Expires response header field (Section 5.3) is present, use - its value minus the value of the Date response header field, or - - o Otherwise, no explicit expiration time is present in the response. - A heuristic freshness lifetime might be applicable; see - Section 4.2.2. - */ - - var expiresTime time.Time - - if obj.RespDirectives.SMaxAge != -1 && !obj.CacheIsPrivate { - expiresTime = obj.NowUTC.Add(time.Second * time.Duration(obj.RespDirectives.SMaxAge)) - } else if obj.RespDirectives.MaxAge != -1 { - expiresTime = obj.NowUTC.UTC().Add(time.Second * time.Duration(obj.RespDirectives.MaxAge)) - } else if !obj.RespExpiresHeader.IsZero() { - serverDate := obj.RespDateHeader - if serverDate.IsZero() { - // common enough case when a Date: header has not yet been added to an - // active response. - serverDate = obj.NowUTC - } - expiresTime = obj.NowUTC.Add(obj.RespExpiresHeader.Sub(serverDate)) - } else if !obj.RespLastModifiedHeader.IsZero() { - // heuristic freshness lifetime - rv.OutWarnings = append(rv.OutWarnings, WarningHeuristicExpiration) - - // http://httpd.apache.org/docs/2.4/mod/mod_cache.html#cachelastmodifiedfactor - // CacheMaxExpire defaults to 24 hours - // CacheLastModifiedFactor: is 0.1 - // - // expiry-period = MIN(time-since-last-modified-date * factor, 24 hours) - // - // obj.NowUTC - - since := obj.RespLastModifiedHeader.Sub(obj.NowUTC) - since = time.Duration(float64(since) * -0.1) - - if since > twentyFourHours { - expiresTime = obj.NowUTC.Add(twentyFourHours) - } else { - expiresTime = obj.NowUTC.Add(since) - } - - if debug { - println("Now UTC: ", obj.NowUTC.String()) - println("Last-Modified: ", obj.RespLastModifiedHeader.String()) - println("Since: ", since.String()) - println("TwentyFourHours: ", twentyFourHours.String()) - println("Expiration: ", expiresTime.String()) - } - } else { - // TODO(pquerna): what should the default behavoir be for expiration time? - } - - rv.OutExpirationTime = expiresTime -} - -// Evaluate cachability based on an HTTP request, and parts of the response. -func UsingRequestResponse(req *http.Request, - statusCode int, - respHeaders http.Header, - privateCache bool) ([]Reason, time.Time, error) { - reasons, time, _, _, err := UsingRequestResponseWithObject(req, statusCode, respHeaders, privateCache) - return reasons, time, err -} - -// Evaluate cachability based on an HTTP request, and parts of the response. -// Returns the parsed Object as well. -func UsingRequestResponseWithObject(req *http.Request, - statusCode int, - respHeaders http.Header, - privateCache bool) ([]Reason, time.Time, []Warning, *Object, error) { - var reqHeaders http.Header - var reqMethod string - - var reqDir *RequestCacheDirectives = nil - respDir, err := ParseResponseCacheControl(respHeaders.Get("Cache-Control")) - if err != nil { - return nil, time.Time{}, nil, nil, err - } - - if req != nil { - reqDir, err = ParseRequestCacheControl(req.Header.Get("Cache-Control")) - if err != nil { - return nil, time.Time{}, nil, nil, err - } - reqHeaders = req.Header - reqMethod = req.Method - } - - var expiresHeader time.Time - var dateHeader time.Time - var lastModifiedHeader time.Time - - if respHeaders.Get("Expires") != "" { - expiresHeader, err = http.ParseTime(respHeaders.Get("Expires")) - if err != nil { - // sometimes servers will return `Expires: 0` or `Expires: -1` to - // indicate expired content - expiresHeader = time.Time{} - } - expiresHeader = expiresHeader.UTC() - } - - if respHeaders.Get("Date") != "" { - dateHeader, err = http.ParseTime(respHeaders.Get("Date")) - if err != nil { - return nil, time.Time{}, nil, nil, err - } - dateHeader = dateHeader.UTC() - } - - if respHeaders.Get("Last-Modified") != "" { - lastModifiedHeader, err = http.ParseTime(respHeaders.Get("Last-Modified")) - if err != nil { - return nil, time.Time{}, nil, nil, err - } - lastModifiedHeader = lastModifiedHeader.UTC() - } - - obj := Object{ - CacheIsPrivate: privateCache, - - RespDirectives: respDir, - RespHeaders: respHeaders, - RespStatusCode: statusCode, - RespExpiresHeader: expiresHeader, - RespDateHeader: dateHeader, - RespLastModifiedHeader: lastModifiedHeader, - - ReqDirectives: reqDir, - ReqHeaders: reqHeaders, - ReqMethod: reqMethod, - - NowUTC: time.Now().UTC(), - } - rv := ObjectResults{} - - CachableObject(&obj, &rv) - if rv.OutErr != nil { - return nil, time.Time{}, nil, nil, rv.OutErr - } - - ExpirationObject(&obj, &rv) - if rv.OutErr != nil { - return nil, time.Time{}, nil, nil, rv.OutErr - } - - return rv.OutReasons, rv.OutExpirationTime, rv.OutWarnings, &obj, nil -} - -// calculate if a freshness directive is present: http://tools.ietf.org/html/rfc7234#section-4.2.1 -func hasFreshness(reqDir *RequestCacheDirectives, respDir *ResponseCacheDirectives, respHeaders http.Header, respExpires time.Time, privateCache bool) bool { - if !privateCache && respDir.SMaxAge != -1 { - return true - } - - if respDir.MaxAge != -1 { - return true - } - - if !respExpires.IsZero() || respHeaders.Get("Expires") != "" { - return true - } - - return false -} - -func cachableStatusCode(statusCode int) bool { - /* - Responses with status codes that are defined as cacheable by default - (e.g., 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501 in - this specification) can be reused by a cache with heuristic - expiration unless otherwise indicated by the method definition or - explicit cache controls [RFC7234]; all other status codes are not - cacheable by default. - */ - switch statusCode { - case 200: - return true - case 203: - return true - case 204: - return true - case 206: - return true - case 300: - return true - case 301: - return true - case 404: - return true - case 405: - return true - case 410: - return true - case 414: - return true - case 501: - return true - default: - return false - } -} diff --git a/vendor/github.com/pquerna/cachecontrol/cacheobject/reasons.go b/vendor/github.com/pquerna/cachecontrol/cacheobject/reasons.go deleted file mode 100644 index f53d1ad5..00000000 --- a/vendor/github.com/pquerna/cachecontrol/cacheobject/reasons.go +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Copyright 2015 Paul Querna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cacheobject - -// Repersents a potential Reason to not cache an object. -// -// Applications may wish to ignore specific reasons, which will make them non-RFC -// compliant, but this type gives them specific cases they can choose to ignore, -// making them compliant in as many cases as they can. -type Reason int - -const ( - - // The request method was POST and an Expiration header was not supplied. - ReasonRequestMethodPOST Reason = iota - - // The request method was PUT and PUTs are not cachable. - ReasonRequestMethodPUT - - // The request method was DELETE and DELETEs are not cachable. - ReasonRequestMethodDELETE - - // The request method was CONNECT and CONNECTs are not cachable. - ReasonRequestMethodCONNECT - - // The request method was OPTIONS and OPTIONS are not cachable. - ReasonRequestMethodOPTIONS - - // The request method was TRACE and TRACEs are not cachable. - ReasonRequestMethodTRACE - - // The request method was not recognized by cachecontrol, and should not be cached. - ReasonRequestMethodUnkown - - // The request included an Cache-Control: no-store header - ReasonRequestNoStore - - // The request included an Authorization header without an explicit Public or Expiration time: http://tools.ietf.org/html/rfc7234#section-3.2 - ReasonRequestAuthorizationHeader - - // The response included an Cache-Control: no-store header - ReasonResponseNoStore - - // The response included an Cache-Control: private header and this is not a Private cache - ReasonResponsePrivate - - // The response failed to meet at least one of the conditions specified in RFC 7234 section 3: http://tools.ietf.org/html/rfc7234#section-3 - ReasonResponseUncachableByDefault -) - -func (r Reason) String() string { - switch r { - case ReasonRequestMethodPOST: - return "ReasonRequestMethodPOST" - case ReasonRequestMethodPUT: - return "ReasonRequestMethodPUT" - case ReasonRequestMethodDELETE: - return "ReasonRequestMethodDELETE" - case ReasonRequestMethodCONNECT: - return "ReasonRequestMethodCONNECT" - case ReasonRequestMethodOPTIONS: - return "ReasonRequestMethodOPTIONS" - case ReasonRequestMethodTRACE: - return "ReasonRequestMethodTRACE" - case ReasonRequestMethodUnkown: - return "ReasonRequestMethodUnkown" - case ReasonRequestNoStore: - return "ReasonRequestNoStore" - case ReasonRequestAuthorizationHeader: - return "ReasonRequestAuthorizationHeader" - case ReasonResponseNoStore: - return "ReasonResponseNoStore" - case ReasonResponsePrivate: - return "ReasonResponsePrivate" - case ReasonResponseUncachableByDefault: - return "ReasonResponseUncachableByDefault" - } - - panic(r) -} diff --git a/vendor/github.com/pquerna/cachecontrol/cacheobject/warning.go b/vendor/github.com/pquerna/cachecontrol/cacheobject/warning.go deleted file mode 100644 index 82f89413..00000000 --- a/vendor/github.com/pquerna/cachecontrol/cacheobject/warning.go +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright 2015 Paul Querna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package cacheobject - -import ( - "fmt" - "net/http" - "time" -) - -// Repersents an HTTP Warning: http://tools.ietf.org/html/rfc7234#section-5.5 -type Warning int - -const ( - // Response is Stale - // A cache SHOULD generate this whenever the sent response is stale. - WarningResponseIsStale Warning = 110 - - // Revalidation Failed - // A cache SHOULD generate this when sending a stale - // response because an attempt to validate the response failed, due to an - // inability to reach the server. - WarningRevalidationFailed Warning = 111 - - // Disconnected Operation - // A cache SHOULD generate this if it is intentionally disconnected from - // the rest of the network for a period of time. - WarningDisconnectedOperation Warning = 112 - - // Heuristic Expiration - // - // A cache SHOULD generate this if it heuristically chose a freshness - // lifetime greater than 24 hours and the response's age is greater than - // 24 hours. - WarningHeuristicExpiration Warning = 113 - - // Miscellaneous Warning - // - // The warning text can include arbitrary information to be presented to - // a human user or logged. A system receiving this warning MUST NOT - // take any automated action, besides presenting the warning to the - // user. - WarningMiscellaneousWarning Warning = 199 - - // Transformation Applied - // - // This Warning code MUST be added by a proxy if it applies any - // transformation to the representation, such as changing the - // content-coding, media-type, or modifying the representation data, - // unless this Warning code already appears in the response. - WarningTransformationApplied Warning = 214 - - // Miscellaneous Persistent Warning - // - // The warning text can include arbitrary information to be presented to - // a human user or logged. A system receiving this warning MUST NOT - // take any automated action. - WarningMiscellaneousPersistentWarning Warning = 299 -) - -func (w Warning) HeaderString(agent string, date time.Time) string { - if agent == "" { - agent = "-" - } else { - // TODO(pquerna): this doesn't escape agent if it contains bad things. - agent = `"` + agent + `"` - } - return fmt.Sprintf(`%d %s "%s" %s`, w, agent, w.String(), date.Format(http.TimeFormat)) -} - -func (w Warning) String() string { - switch w { - case WarningResponseIsStale: - return "Response is Stale" - case WarningRevalidationFailed: - return "Revalidation Failed" - case WarningDisconnectedOperation: - return "Disconnected Operation" - case WarningHeuristicExpiration: - return "Heuristic Expiration" - case WarningMiscellaneousWarning: - // TODO(pquerna): ideally had a better way to override this one code. - return "Miscellaneous Warning" - case WarningTransformationApplied: - return "Transformation Applied" - case WarningMiscellaneousPersistentWarning: - // TODO(pquerna): same as WarningMiscellaneousWarning - return "Miscellaneous Persistent Warning" - } - - panic(w) -} diff --git a/vendor/github.com/pquerna/cachecontrol/doc.go b/vendor/github.com/pquerna/cachecontrol/doc.go deleted file mode 100644 index 3fe7ed0d..00000000 --- a/vendor/github.com/pquerna/cachecontrol/doc.go +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright 2015 Paul Querna - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package cachecontrol implements the logic for HTTP Caching -// -// Deciding if an HTTP Response can be cached is often harder -// and more bug prone than an actual cache storage backend. -// cachecontrol provides a simple interface to determine if -// request and response pairs are cachable as defined under -// RFC 7234 http://tools.ietf.org/html/rfc7234 -package cachecontrol diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/.gitignore b/vendor/gopkg.in/coreos/go-oidc.v2/.gitignore deleted file mode 100644 index c96f2f47..00000000 --- a/vendor/gopkg.in/coreos/go-oidc.v2/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bin -/gopath diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml b/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml deleted file mode 100644 index 6ff9dd96..00000000 --- a/vendor/gopkg.in/coreos/go-oidc.v2/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -language: go - -go: - - "1.9" - - "1.10" - -install: - - go get -v -t github.com/coreos/go-oidc/... - - go get golang.org/x/tools/cmd/cover - - go get github.com/golang/lint/golint - -script: - - ./test - -notifications: - email: false diff --git a/vendor/gopkg.in/coreos/go-oidc.v2/CONTRIBUTING.md b/vendor/gopkg.in/coreos/go-oidc.v2/CONTRIBUTING.md deleted file mode 100644 index 6662073a..00000000 --- a/vendor/gopkg.in/coreos/go-oidc.v2/CONTRIBUTING.md +++ /dev/null @@ -1,71 +0,0 @@ -# How to Contribute - -CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via -GitHub pull requests. This document outlines some of the conventions on -development workflow, commit message formatting, contact points and other -resources to make it easier to get your contribution accepted. - -# Certificate of Origin - -By contributing to this project you agree to the Developer Certificate of -Origin (DCO). This document was created by the Linux Kernel community and is a -simple statement that you, as a contributor, have the legal right to make the -contribution. See the [DCO](DCO) file for details. - -# Email and Chat - -The project currently uses the general CoreOS email list and IRC channel: -- Email: [coreos-dev](https://groups.google.com/forum/#!forum/coreos-dev) -- IRC: #[coreos](irc://irc.freenode.org:6667/#coreos) IRC channel on freenode.org - -Please avoid emailing maintainers found in the MAINTAINERS file directly. They -are very busy and read the mailing lists. - -## Getting Started - -- Fork the repository on GitHub -- Read the [README](README.md) for build and test instructions -- Play with the project, submit bugs, submit patches! - -## Contribution Flow - -This is a rough outline of what a contributor's workflow looks like: - -- Create a topic branch from where you want to base your work (usually master). -- Make commits of logical units. -- Make sure your commit messages are in the proper format (see below). -- Push your changes to a topic branch in your fork of the repository. -- Make sure the tests pass, and add any new tests as appropriate. -- Submit a pull request to the original repository. - -Thanks for your contributions! - -### Format of the Commit Message - -We follow a rough convention for commit messages that is designed to answer two -questions: what changed and why. The subject line should feature the what and -the body of the commit should describe the why. - -``` -scripts: add the test-cluster command - -this uses tmux to setup a test cluster that you can easily kill and -start for debugging. - -Fixes #38 -``` - -The format can be described more formally as follows: - -``` -: - - - -