From 976ab49e01f05f6acee70dc5a3c00c5ce79950da Mon Sep 17 00:00:00 2001 From: aeneasr Date: Fri, 21 Feb 2020 15:33:07 +0100 Subject: [PATCH 1/2] fix: Improve id_token performance with caching --- .schemas/config.schema.json | 2 +- .../signer_default_integration_test.go | 41 ++ go.mod | 4 + go.sum | 10 + helper/bearer_test.go | 2 +- pipeline/authn/authenticator_jwt_test.go | 4 +- ...authenticator_oauth2_introspection_test.go | 4 +- pipeline/mutate/mutator_cookie_test.go | 28 +- pipeline/mutate/mutator_id_token.go | 109 ++++-- pipeline/mutate/mutator_id_token_test.go | 367 +++++++++++------- 10 files changed, 397 insertions(+), 174 deletions(-) create mode 100644 credentials/signer_default_integration_test.go diff --git a/.schemas/config.schema.json b/.schemas/config.schema.json index 09c8ccbc8c..bfe195bd0c 100644 --- a/.schemas/config.schema.json +++ b/.schemas/config.schema.json @@ -848,7 +848,7 @@ "title": "Expire After", "description": "Sets the time-to-live of the JSON Web Token.", "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", - "default": "1m", + "default": "15m", "examples": [ "1h", "1m", diff --git a/credentials/signer_default_integration_test.go b/credentials/signer_default_integration_test.go new file mode 100644 index 0000000000..9a36a36a2f --- /dev/null +++ b/credentials/signer_default_integration_test.go @@ -0,0 +1,41 @@ +package credentials_test + +import ( + "context" + "net/url" + "testing" + "time" + + "github.com/dgrijalva/jwt-go" + + "github.com/ory/oathkeeper/internal" +) + +func BenchmarkDefaultSigner(b *testing.B) { + conf := internal.NewConfigurationWithDefaults() + reg := internal.NewRegistry(conf) + ctx := context.Background() + + for alg, keys := range map[string]string{ + "RS256": "file://../test/stub/jwks-rsa-multiple.json", + "ES256": "file://../test/stub/jwks-ecdsa.json", + "HS256": "file://../test/stub/jwks-hs.json", + } { + b.Run("alg="+alg, func(b *testing.B) { + jwks, _ := url.Parse(keys) + for i := 0; i < b.N; i++ { + if _, err := reg.CredentialsSigner().Sign(ctx, jwks, jwt.MapClaims{ + "custom-claim2": 3.14159, + "custom-claim3": true, + "exp": time.Now().Add(time.Minute).Unix(), + "iat": time.Now().Unix(), + "iss": "issuer", + "nbf": time.Now().Unix(), + "sub": "some subject", + }); err != nil { + b.Fatal(err) + } + } + }) + } +} diff --git a/go.mod b/go.mod index ea7f25bb9d..e8b7d050d3 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,12 @@ module github.com/ory/oathkeeper require ( github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/sprig v2.20.0+incompatible + github.com/allegro/bigcache v1.2.1 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7 github.com/blang/semver v3.5.1+incompatible github.com/bxcodec/faker v2.0.1+incompatible + github.com/dgraph-io/ristretto v0.0.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dlclark/regexp2 v1.2.0 github.com/fsnotify/fsnotify v1.4.7 @@ -22,7 +24,9 @@ require ( github.com/gobuffalo/packr/v2 v2.0.0-rc.15 github.com/gobwas/glob v0.2.3 github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/mock v1.3.1 + github.com/golang/protobuf v1.3.3 // indirect github.com/google/uuid v1.1.1 github.com/gorilla/mux v1.7.1 // indirect github.com/huandu/xstrings v1.2.0 // indirect diff --git a/go.sum b/go.sum index 13d5b57e4e..fc5551231a 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f h1:zvClvFQwU++UpIUBGC8YmD github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= +github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= @@ -56,6 +58,7 @@ github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEe github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -81,8 +84,11 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+1tLs= +github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= @@ -363,6 +369,8 @@ github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hz github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= @@ -372,6 +380,8 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= diff --git a/helper/bearer_test.go b/helper/bearer_test.go index ed41b07a9c..1c3582e388 100644 --- a/helper/bearer_test.go +++ b/helper/bearer_test.go @@ -52,7 +52,7 @@ func TestBearerTokenFromRequest(t *testing.T) { customQueryParameterName := "Custom-Auth" request := &http.Request{ Form: map[string][]string{ - customQueryParameterName: []string{expectedToken}, + customQueryParameterName: {expectedToken}, }, } tokenLocation := helper.BearerTokenLocation{QueryParameter: &customQueryParameterName} diff --git a/pipeline/authn/authenticator_jwt_test.go b/pipeline/authn/authenticator_jwt_test.go index bd9e66e683..7ae1953e45 100644 --- a/pipeline/authn/authenticator_jwt_test.go +++ b/pipeline/authn/authenticator_jwt_test.go @@ -109,7 +109,7 @@ func TestAuthenticatorJWT(t *testing.T) { d: "should return error saying that authenticator is not responsible for validating the request, as the token was not provided in a proper location (custom query parameter)", r: &http.Request{ Form: map[string][]string{ - "someOtherQueryParam": []string{ + "someOtherQueryParam": { gen(keys[1], jwt.MapClaims{ "sub": "sub", "exp": now.Add(time.Hour).Unix(), @@ -148,7 +148,7 @@ func TestAuthenticatorJWT(t *testing.T) { d: "should pass because the valid JWT token was provided in a proper location (custom query parameter)", r: &http.Request{ Form: map[string][]string{ - "token": []string{ + "token": { gen(keys[1], jwt.MapClaims{ "sub": "sub", "exp": now.Add(time.Hour).Unix(), diff --git a/pipeline/authn/authenticator_oauth2_introspection_test.go b/pipeline/authn/authenticator_oauth2_introspection_test.go index 3f88e20395..613d12d2f2 100644 --- a/pipeline/authn/authenticator_oauth2_introspection_test.go +++ b/pipeline/authn/authenticator_oauth2_introspection_test.go @@ -93,7 +93,7 @@ func TestAuthenticatorOAuth2Introspection(t *testing.T) { d: "should return error saying that authenticator is not responsible for validating the request, as the token was not provided in a proper location (custom query parameter)", r: &http.Request{ Form: map[string][]string{ - "someOtherQueryParam": []string{"token"}, + "someOtherQueryParam": {"token"}, }, Header: http.Header{"Authorization": {"bearer token"}}, }, @@ -127,7 +127,7 @@ func TestAuthenticatorOAuth2Introspection(t *testing.T) { d: "should pass because the valid token was provided in a proper location (custom query parameter)", r: &http.Request{ Form: map[string][]string{ - "token": []string{"token"}, + "token": {"token"}, }, }, config: []byte(`{"token_from": {"query_parameter": "token"}}`), diff --git a/pipeline/mutate/mutator_cookie_test.go b/pipeline/mutate/mutator_cookie_test.go index 4ea65a7d55..776baae4ec 100644 --- a/pipeline/mutate/mutator_cookie_test.go +++ b/pipeline/mutate/mutator_cookie_test.go @@ -45,7 +45,7 @@ func TestCredentialsIssuerCookies(t *testing.T) { Rule: &rule.Rule{ID: "test-rule"}, Config: json.RawMessage([]byte(`{"cookies": {"user": "{{ print .Subject }}"}}`)), Request: &http.Request{Header: http.Header{}}, - Match: []*http.Cookie{&http.Cookie{Name: "user", Value: "foo"}}, + Match: []*http.Cookie{{Name: "user", Value: "foo"}}, Err: nil, }, "Unknown Config Field": { @@ -61,7 +61,7 @@ func TestCredentialsIssuerCookies(t *testing.T) { Rule: &rule.Rule{ID: "test-rule3"}, Config: json.RawMessage([]byte(`{"cookies": {"user": "realm:resources:users:{{ print .Subject }}"}}`)), Request: &http.Request{Header: http.Header{}}, - Match: []*http.Cookie{&http.Cookie{Name: "user", Value: "realm:resources:users:foo"}}, + Match: []*http.Cookie{{Name: "user", Value: "realm:resources:users:foo"}}, Err: nil, }, "Subject & Extras": { @@ -70,9 +70,9 @@ func TestCredentialsIssuerCookies(t *testing.T) { Config: json.RawMessage([]byte(`{"cookies":{"user": "{{ print .Subject }}", "issuer": "{{ print .Extra.iss }}", "audience": "{{ print .Extra.aud }}"}}`)), Request: &http.Request{Header: http.Header{}}, Match: []*http.Cookie{ - &http.Cookie{Name: "user", Value: "foo"}, - &http.Cookie{Name: "issuer", Value: "issuer"}, - &http.Cookie{Name: "audience", Value: "audience"}, + {Name: "user", Value: "foo"}, + {Name: "issuer", Value: "issuer"}, + {Name: "audience", Value: "audience"}, }, Err: nil, }, @@ -82,7 +82,7 @@ func TestCredentialsIssuerCookies(t *testing.T) { Config: json.RawMessage([]byte(`{"cookies":{"kitchensink": "{{ print .Subject }} {{ print .Extra.iss }} {{ print .Extra.aud }}"}}`)), Request: &http.Request{Header: http.Header{}}, Match: []*http.Cookie{ - &http.Cookie{Name: "kitchensink", Value: "foo issuer audience"}, + {Name: "kitchensink", Value: "foo issuer audience"}, }, Err: nil, }, @@ -94,9 +94,9 @@ func TestCredentialsIssuerCookies(t *testing.T) { Header: http.Header{"Cookie": []string{"user=admin;issuer=issuer;audience=audience"}}, }, Match: []*http.Cookie{ - &http.Cookie{Name: "user", Value: "anonymous"}, - &http.Cookie{Name: "issuer", Value: ""}, - &http.Cookie{Name: "audience", Value: ""}, + {Name: "user", Value: "anonymous"}, + {Name: "issuer", Value: ""}, + {Name: "audience", Value: ""}, }, Err: nil, }, @@ -105,7 +105,7 @@ func TestCredentialsIssuerCookies(t *testing.T) { Rule: &rule.Rule{ID: "test-rule7"}, Config: json.RawMessage([]byte(`{"cookies":{"issuer": "{{ print .Extra.iss }}"}}`)), Request: &http.Request{Header: http.Header{}}, - Match: []*http.Cookie{&http.Cookie{Name: "issuer", Value: ""}}, + Match: []*http.Cookie{{Name: "issuer", Value: ""}}, Err: nil, }, "Nested Extras": { @@ -128,10 +128,10 @@ func TestCredentialsIssuerCookies(t *testing.T) { }}`)), Request: &http.Request{Header: http.Header{}}, Match: []*http.Cookie{ - &http.Cookie{Name: "nested-int", Value: "10"}, - &http.Cookie{Name: "nested-float64", Value: "3.14159"}, - &http.Cookie{Name: "nested-bool", Value: "true"}, - &http.Cookie{Name: "nested-nonexistent", Value: ""}, + {Name: "nested-int", Value: "10"}, + {Name: "nested-float64", Value: "3.14159"}, + {Name: "nested-bool", Value: "true"}, + {Name: "nested-nonexistent", Value: ""}, }, Err: nil, }, diff --git a/pipeline/mutate/mutator_id_token.go b/pipeline/mutate/mutator_id_token.go index d5cc825cc9..3de0aa4655 100644 --- a/pipeline/mutate/mutator_id_token.go +++ b/pipeline/mutate/mutator_id_token.go @@ -22,7 +22,7 @@ package mutate import ( "bytes" - "crypto/sha256" + "crypto/md5" "encoding/json" "fmt" "net/http" @@ -30,6 +30,8 @@ import ( "text/template" "time" + "github.com/dgraph-io/ristretto" + "github.com/dgrijalva/jwt-go" "github.com/pborman/uuid" @@ -46,9 +48,12 @@ type MutatorIDTokenRegistry interface { } type MutatorIDToken struct { - c configuration.Provider - r MutatorIDTokenRegistry - t *template.Template + c configuration.Provider + r MutatorIDTokenRegistry + templates *template.Template + + tokenCache *ristretto.Cache + tokenCacheEnabled bool } type CredentialsIDTokenConfig struct { @@ -59,11 +64,16 @@ type CredentialsIDTokenConfig struct { } func (c *CredentialsIDTokenConfig) ClaimsTemplateID() string { - return fmt.Sprintf("%x", sha256.Sum256([]byte(c.Claims))) + return fmt.Sprintf("%x", md5.Sum([]byte(c.Claims))) } func NewMutatorIDToken(c configuration.Provider, r MutatorIDTokenRegistry) *MutatorIDToken { - return &MutatorIDToken{r: r, c: c, t: newTemplate("id_token")} + cache, _ := ristretto.NewCache(&ristretto.Config{ + NumCounters: 10000, + MaxCost: 1 << 25, + BufferItems: 64, + }) + return &MutatorIDToken{r: r, c: c, templates: newTemplate("id_token"), tokenCache: cache, tokenCacheEnabled: true} } func (a *MutatorIDToken) GetID() string { @@ -71,7 +81,57 @@ func (a *MutatorIDToken) GetID() string { } func (a *MutatorIDToken) WithCache(t *template.Template) { - a.t = t + a.templates = t +} + +func (a *MutatorIDToken) SetCaching(token bool) { + a.tokenCacheEnabled = token +} + +type idTokenCacheContainer struct { + ExpiresAt time.Time + TTL time.Duration + Token string +} + +func (a *MutatorIDToken) cacheKey(config *CredentialsIDTokenConfig, ttl time.Duration, claims []byte, session *authn.AuthenticationSession) string { + return fmt.Sprintf("%x", + md5.Sum([]byte(fmt.Sprintf("%s|%s|%s|%s|%s", config.IssuerURL, ttl, config.JWKSURL, claims, session.Subject))), + ) +} + +func (a *MutatorIDToken) tokenFromCache(config *CredentialsIDTokenConfig, session *authn.AuthenticationSession, claims []byte, ttl time.Duration) (string, bool) { + if !a.tokenCacheEnabled { + return "", false + } + + key := a.cacheKey(config, ttl, claims, session) + + item, found := a.tokenCache.Get(key) + if !found { + return "", false + } + + container := item.(*idTokenCacheContainer) + if container.ExpiresAt.Before(time.Now().Add(ttl * 1 / 10)) { + a.tokenCache.Del(key) + return "", false + } + + return container.Token, true +} + +func (a *MutatorIDToken) tokenToCache(config *CredentialsIDTokenConfig, session *authn.AuthenticationSession, claims []byte, ttl time.Duration, expiresAt time.Time, token string) { + if !a.tokenCacheEnabled { + return + } + + key := a.cacheKey(config, ttl, claims, session) + a.tokenCache.Set(key, &idTokenCacheContainer{ + TTL: ttl, + ExpiresAt: expiresAt, + Token: token, + }, 0) } func (a *MutatorIDToken) Mutate(r *http.Request, session *authn.AuthenticationSession, config json.RawMessage, rl pipeline.Rule) error { @@ -81,11 +141,17 @@ func (a *MutatorIDToken) Mutate(r *http.Request, session *authn.AuthenticationSe return err } + ttl, err := time.ParseDuration(c.TTL) + if err != nil { + return errors.WithStack(err) + } + + var templateClaims []byte if len(c.Claims) > 0 { - t := a.t.Lookup(c.ClaimsTemplateID()) + t := a.templates.Lookup(c.ClaimsTemplateID()) if t == nil { var err error - t, err = a.t.New(c.ClaimsTemplateID()).Parse(c.Claims) + t, err = a.templates.New(c.ClaimsTemplateID()).Parse(c.Claims) if err != nil { return errors.Wrapf(err, `error parsing claims template in rule "%s"`, rl.GetID()) } @@ -96,22 +162,20 @@ func (a *MutatorIDToken) Mutate(r *http.Request, session *authn.AuthenticationSe return errors.Wrapf(err, `error executing claims template in rule "%s"`, rl.GetID()) } + templateClaims = b.Bytes() if err := json.NewDecoder(&b).Decode(&claims); err != nil { return errors.WithStack(err) } } - if c.TTL == "" { - c.TTL = "1m" - } - - ttl, err := time.ParseDuration(c.TTL) - if err != nil { - return errors.WithStack(err) + if token, ok := a.tokenFromCache(c, session, templateClaims, ttl); ok { + session.SetHeader("Authorization", "Bearer "+token) + return nil } now := time.Now().UTC() - claims["exp"] = now.Add(ttl).Unix() + exp := now.Add(ttl) + claims["exp"] = exp.Unix() claims["jti"] = uuid.New() claims["iat"] = now.Unix() claims["iss"] = c.IssuerURL @@ -123,15 +187,12 @@ func (a *MutatorIDToken) Mutate(r *http.Request, session *authn.AuthenticationSe return errors.WithStack(err) } - signed, err := a.r.CredentialsSigner().Sign( - r.Context(), - jwks, - claims, - ) + signed, err := a.r.CredentialsSigner().Sign(r.Context(), jwks, claims) if err != nil { return err } + a.tokenToCache(c, session, templateClaims, ttl, exp, signed) session.SetHeader("Authorization", "Bearer "+signed) return nil } @@ -151,5 +212,9 @@ func (a *MutatorIDToken) Config(config json.RawMessage) (*CredentialsIDTokenConf return nil, NewErrMutatorMisconfigured(a, err) } + if c.TTL == "" { + c.TTL = "15m" + } + return &c, nil } diff --git a/pipeline/mutate/mutator_id_token_test.go b/pipeline/mutate/mutator_id_token_test.go index 0aaf67ce43..46709972a1 100644 --- a/pipeline/mutate/mutator_id_token_test.go +++ b/pipeline/mutate/mutator_id_token_test.go @@ -53,7 +53,7 @@ import ( "github.com/stretchr/testify/require" ) -type testCase struct { +type idTokenTestCase struct { Rule *rule.Rule Session *authn.AuthenticationSession Config json.RawMessage @@ -63,6 +63,90 @@ type testCase struct { Err error } +var idTokenTestCases = []idTokenTestCase{ + { + Rule: &rule.Rule{ID: "test-rule1"}, + Session: &authn.AuthenticationSession{Subject: "foo"}, + Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Subject }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{"custom-claim": "foo"}, + K: "file://../../test/stub/jwks-hs.json", + }, + { + Rule: &rule.Rule{ID: "test-rule2"}, + Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1"}}, + Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{"custom-claim": "value1"}, + K: "file://../../test/stub/jwks-rsa-multiple.json", + }, + { + Rule: &rule.Rule{ID: "test-rule3"}, + Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1", "def": "value2"}}, + Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}/{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{"custom-claim": "value1/value2"}, + K: "file://../../test/stub/jwks-ecdsa.json", + }, + { + Rule: &rule.Rule{ID: "test-rule4"}, + Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1", "def": "value2"}}, + Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}\", \"custom-claim2\": \"{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{"custom-claim": "value1", "custom-claim2": "value2"}, + K: "file://../../test/stub/jwks-ecdsa.json", + }, + { + Rule: &rule.Rule{ID: "test-rule5"}, + Session: &authn.AuthenticationSession{}, + Config: json.RawMessage([]byte(`{"bad": "key"}`)), + Match: jwt.MapClaims{}, + K: "file://../../test/stub/jwks-hs.json", + Err: errors.New(`mutator matching this route is misconfigured or disabled`), + }, + { + Rule: &rule.Rule{ID: "test-rule6"}, + Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{}}, + Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{"custom-claim": ""}, + K: "file://../../test/stub/jwks-rsa-multiple.json", + }, + { + Rule: &rule.Rule{ID: "test-rule7"}, + Session: &authn.AuthenticationSession{ + Subject: "foo", + Extra: map[string]interface{}{ + "nested": map[string]interface{}{ + "int": int(10), + "float64": float64(3.14159), + "bool": true, + }, + }, + }, + Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": {{ print .Extra.nested.int }},\"custom-claim2\": {{ print .Extra.nested.float64 }},\"custom-claim3\": {{ print .Extra.nested.bool }},\"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{ + "custom-claim": float64(10), // the json decoder always converts to float64 + "custom-claim2": 3.14159, + "custom-claim3": true, + }, + K: "file://../../test/stub/jwks-ecdsa.json", + }, + { + Rule: &rule.Rule{ID: "test-rule8"}, + Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"example.com/some-claims": []string{"Foo", "Bar"}}}, + Config: json.RawMessage([]byte(`{"claims":"{\"custom-claim\": \"{{- (index .Extra \"example.com/some-claims\") | join \",\" -}}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{"custom-claim": "Foo,Bar"}, + K: "file://../../test/stub/jwks-hs.json", + }, + { + Rule: &rule.Rule{ID: "test-rule9"}, + Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"malicious": "evil"}}, + Config: json.RawMessage([]byte(`{"claims": "{\"iss\": \"{{ print .Extra.malicious }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), + Match: jwt.MapClaims{}, + K: "file://../../test/stub/jwks-ecdsa.json", + }, +} + +func parseToken(h http.Header) string { + return strings.Replace(h.Get("Authorization"), "Bearer ", "", 1) +} + func TestMutatorIDToken(t *testing.T) { conf := internal.NewConfigurationWithDefaults() reg := internal.NewRegistry(conf) @@ -74,142 +158,123 @@ func TestMutatorIDToken(t *testing.T) { viper.Set("mutators.id_token.config.issuer_url", "/foo/bar") t.Run("method=mutate", func(t *testing.T) { - r := &http.Request{} - - t.Run("caching=off", func(t *testing.T) { - var testCases = []testCase{ - { - Rule: &rule.Rule{ID: "test-rule1"}, - Session: &authn.AuthenticationSession{Subject: "foo"}, - Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Subject }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{"custom-claim": "foo"}, - K: "file://../../test/stub/jwks-hs.json", - }, - { - Rule: &rule.Rule{ID: "test-rule2"}, - Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1"}}, - Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{"custom-claim": "value1"}, - K: "file://../../test/stub/jwks-rsa-multiple.json", - }, - { - Rule: &rule.Rule{ID: "test-rule3"}, - Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1", "def": "value2"}}, - Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}/{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{"custom-claim": "value1/value2"}, - K: "file://../../test/stub/jwks-ecdsa.json", - }, - { - Rule: &rule.Rule{ID: "test-rule4"}, - Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1", "def": "value2"}}, - Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}\", \"custom-claim2\": \"{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{"custom-claim": "value1", "custom-claim2": "value2"}, - K: "file://../../test/stub/jwks-ecdsa.json", - }, - { - Rule: &rule.Rule{ID: "test-rule5"}, - Session: &authn.AuthenticationSession{}, - Config: json.RawMessage([]byte(`{"bad": "key"}`)), - Match: jwt.MapClaims{}, - K: "file://../../test/stub/jwks-hs.json", - Err: errors.New(`mutator matching this route is misconfigured or disabled`), - }, - { - Rule: &rule.Rule{ID: "test-rule6"}, - Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{}}, - Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{"custom-claim": ""}, - K: "file://../../test/stub/jwks-rsa-multiple.json", - }, - { - Rule: &rule.Rule{ID: "test-rule7"}, - Session: &authn.AuthenticationSession{ - Subject: "foo", - Extra: map[string]interface{}{ - "nested": map[string]interface{}{ - "int": int(10), - "float64": float64(3.14159), - "bool": true, - }, - }, - }, - Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": {{ print .Extra.nested.int }},\"custom-claim2\": {{ print .Extra.nested.float64 }},\"custom-claim3\": {{ print .Extra.nested.bool }},\"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{ - "custom-claim": float64(10), // the json decoder always converts to float64 - "custom-claim2": 3.14159, - "custom-claim3": true, - }, - K: "file://../../test/stub/jwks-ecdsa.json", - }, - { - Rule: &rule.Rule{ID: "test-rule8"}, - Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"example.com/some-claims": []string{"Foo", "Bar"}}}, - Config: json.RawMessage([]byte(`{"claims":"{\"custom-claim\": \"{{- (index .Extra \"example.com/some-claims\") | join \",\" -}}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{"custom-claim": "Foo,Bar"}, - K: "file://../../test/stub/jwks-hs.json", - }, - { - Rule: &rule.Rule{ID: "test-rule9"}, - Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"malicious": "evil"}}, - Config: json.RawMessage([]byte(`{"claims": "{\"iss\": \"{{ print .Extra.malicious }}\", \"aud\": [\"foo\", \"bar\"]}"}`)), - Match: jwt.MapClaims{}, - K: "file://../../test/stub/jwks-ecdsa.json", - }, - } - - for i, tc := range testCases { + t.Run("case=token generation and validation", func(t *testing.T) { + for i, tc := range idTokenTestCases { t.Run(fmt.Sprintf("case=%d", i), func(t *testing.T) { - if tc.Ttl == 0 { - tc.Ttl = time.Second * 5 - } - tc.Config, _ = sjson.SetBytes(tc.Config, "jwks_url", tc.K) tc.Config, _ = sjson.SetBytes(tc.Config, "ttl", tc.Ttl.String()) err := a.Mutate(r, tc.Session, tc.Config, tc.Rule) - if tc.Err == nil { - require.NoError(t, err) - - token := strings.Replace(tc.Session.Header.Get("Authorization"), "Bearer ", "", 1) - - result, err := reg.CredentialsVerifier().Verify(context.Background(), token, &credentials.ValidationContext{ - Algorithms: []string{"RS256", "HS256", "ES256"}, - Audiences: []string{"foo", "bar"}, - KeyURLs: []url.URL{*urlx.ParseOrPanic(tc.K)}, - }) - require.NoError(t, err, "token: %s", token) - - ttl := time.Minute // default from config is time.Minute - if tc.Ttl > 0 { - ttl = tc.Ttl - } - assert.Equal(t, "foo", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["sub"])) - assert.Equal(t, "/foo/bar", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["iss"])) - assert.True(t, time.Now().Add(ttl).Unix() >= int64(result.Claims.(jwt.MapClaims)["exp"].(float64))) - - for key, val := range tc.Match { - assert.Equal(t, val, result.Claims.(jwt.MapClaims)[key]) - } - - } else { + if tc.Err != nil { assert.EqualError(t, err, tc.Err.Error()) + return + } + require.NoError(t, err) + + token := parseToken(tc.Session.Header) + result, err := reg.CredentialsVerifier().Verify(context.Background(), token, &credentials.ValidationContext{ + Algorithms: []string{"RS256", "HS256", "ES256"}, + Audiences: []string{"foo", "bar"}, + KeyURLs: []url.URL{*urlx.ParseOrPanic(tc.K)}, + }) + require.NoError(t, err, "token: %s", token) + + ttl := time.Minute // default from config is time.Minute + if tc.Ttl > 0 { + ttl = tc.Ttl + } + assert.Equal(t, "foo", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["sub"])) + assert.Equal(t, "/foo/bar", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["iss"])) + assert.True(t, time.Now().Add(ttl).Unix() >= int64(result.Claims.(jwt.MapClaims)["exp"].(float64))) + + for key, val := range tc.Match { + assert.Equal(t, val, result.Claims.(jwt.MapClaims)[key]) } }) } }) - t.Run("caching=on", func(t *testing.T) { + t.Run("case=test token cache", func(t *testing.T) { + mutate := func(t *testing.T, session authn.AuthenticationSession, config json.RawMessage) string { + require.NoError(t, a.Mutate(new(http.Request), &session, config, &rule.Rule{ID: "1"})) + return parseToken(session.Header) + } + + session := &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"bar": "baz"}} + config := json.RawMessage([]byte(`{"ttl": "3s", "claims": "{\"foo\": \"{{ print .Extra.bar }}\", \"aud\": [\"foo\"]}", "jwks_url": "file://../../test/stub/jwks-ecdsa.json"}`)) + + t.Parallel() + t.Run("subcase=different tokens because expired", func(t *testing.T) { + config, _ := sjson.SetBytes(config, "ttl", "100ms") + prev := mutate(t, *session, config) + time.Sleep(time.Millisecond * 90) + assert.NotEqual(t, prev, mutate(t, *session, config)) + }) + + t.Run("subcase=same tokens because expired is long enough", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) // give the cache buffers some time + assert.Equal(t, prev, mutate(t, *session, config)) + }) + + t.Run("subcase=different tokens because expired is long but was reached", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second * 3) // give the cache buffers some time + assert.NotEqual(t, prev, mutate(t, *session, config)) + }) - tc := testCase{ + t.Run("subcase=different tokens because different subjects", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) + s := *session + s.Subject = "not-foo" + assert.NotEqual(t, prev, mutate(t, s, config)) + }) + + t.Run("subcase=different tokens because session extra changed", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) + s := *session + s.Extra = map[string]interface{}{"bar": "not-baz"} + assert.NotEqual(t, prev, mutate(t, s, config)) + }) + + t.Run("subcase=different tokens because claim options changed", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) + config := json.RawMessage([]byte(`{"ttl": "3s", "claims": "{\"foo\": \"{{ print .Extra.bar }}\", \"aud\": [\"not-foo\"]}", "jwks_url": "file://../../test/stub/jwks-ecdsa.json"}`)) + assert.NotEqual(t, prev, mutate(t, *session, config)) + }) + + t.Run("subcase=same tokens because session extra changed but claims ignore the extra claims", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) + s := *session + s.Extra = map[string]interface{}{"bar": "baz", "not-bar": "whatever"} + assert.Equal(t, prev, mutate(t, s, config)) + }) + + t.Run("subcase=different tokens because issuer changed", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) + config, _ := sjson.SetBytes(config, "issuer_url", "/not-baz/not-bar") + assert.NotEqual(t, prev, mutate(t, *session, config)) + }) + + t.Run("subcase=different tokens because JWKS source changed", func(t *testing.T) { + prev := mutate(t, *session, config) + time.Sleep(time.Second) + config, _ := sjson.SetBytes(config, "jwks_url", "file://../../test/stub/jwks-hs.json") + assert.NotEqual(t, prev, mutate(t, *session, config)) + }) + }) + + t.Run("case=ensure template cache", func(t *testing.T) { + tc := idTokenTestCase{ Rule: &rule.Rule{ID: "test-rule"}, Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1", "def": "value2"}}, Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}/{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}", "jwks_url": "file://../../test/stub/jwks-ecdsa.json"}`)), - K: "file://../../test/stub/jwks-ecdsa.json", } - - // viper.Set(configuration.ViperKeyMutatorIDTokenJWKSURL, tc.K) - // viper.Set(configuration.ViperKeyMutatorIDTokenTTL, tc.Ttl) - cache := template.New("rules") var cfg CredentialsIDTokenConfig @@ -219,12 +284,9 @@ func TestMutatorIDToken(t *testing.T) { require.NoError(t, err) a.(*MutatorIDToken).WithCache(cache) - - err = a.Mutate(r, tc.Session, tc.Config, tc.Rule) - require.NoError(t, err) + require.NoError(t, a.Mutate(r, tc.Session, tc.Config, tc.Rule)) token := strings.Replace(tc.Session.Header.Get("Authorization"), "Bearer ", "", 1) - result, err := reg.CredentialsVerifier().Verify(context.Background(), token, &credentials.ValidationContext{ Algorithms: []string{"RS256", "HS256", "ES256"}, Audiences: []string{"override"}, @@ -232,15 +294,10 @@ func TestMutatorIDToken(t *testing.T) { }) require.NoError(t, err, "token: %s (%s) %v", token, cfg.ClaimsTemplateID(), cache.Lookup(cfg.ClaimsTemplateID())) - ttl := time.Minute // default from config is time.Minute - if tc.Ttl > 0 { - ttl = tc.Ttl - } assert.Equal(t, "foo", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["sub"])) assert.Equal(t, "/foo/bar", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["iss"])) assert.Equal(t, "override", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["custom-claim"])) assert.Equal(t, "override", fmt.Sprintf("%s", result.Claims.(jwt.MapClaims)["aud"].([]interface{})[0])) - assert.True(t, time.Now().Add(ttl).Unix() >= int64(result.Claims.(jwt.MapClaims)["exp"].(float64))) }) }) @@ -260,8 +317,6 @@ func TestMutatorIDToken(t *testing.T) { t.Run(fmt.Sprintf("case=%d", k), func(t *testing.T) { viper.Reset() viper.Set(configuration.ViperKeyMutatorIDTokenIsEnabled, tc.e) - // viper.Set(configuration.ViperKeyMutatorIDTokenIssuerURL, tc.i) - // viper.Set(configuration.ViperKeyMutatorIDTokenJWKSURL, tc.j) err := a.Validate(json.RawMessage(`{"issuer_url":"` + tc.i + `", "jwks_url": "` + tc.j + `"}`)) if tc.pass { require.NoError(t, err) @@ -272,3 +327,51 @@ func TestMutatorIDToken(t *testing.T) { } }) } + +func BenchmarkMutatorIDToken(b *testing.B) { + issuers := []string{"foo", "bar", "baz", "zab"} + + conf := internal.NewConfigurationWithDefaults() + reg := internal.NewRegistry(conf) + rl := &rule.Rule{ID: "test-rule"} + r := &http.Request{} + + var tcs []idTokenTestCase + for _, tc := range idTokenTestCases { + if tc.Err == nil { + tcs = append(tcs, tc) + } + } + + a, err := reg.PipelineMutator("id_token") + require.NoError(b, err) + + for alg, key := range map[string]string{ + "RS256": "file://../../test/stub/jwks-rsa-multiple.json", + "HS256": "file://../../test/stub/jwks-hs.json", + "ES256": "file://../../test/stub/jwks-ecdsa.json", + } { + b.Run("alg="+alg, func(b *testing.B) { + for _, enableCache := range []bool{true, false} { + a.(*MutatorIDToken).SetCaching(enableCache) + b.Run(fmt.Sprintf("cache=%v", enableCache), func(b *testing.B) { + var tc idTokenTestCase + var config []byte + + b.ResetTimer() + for i := 0; i < b.N; i++ { + tc = tcs[i%len(tcs)] + config, _ = sjson.SetBytes(tc.Config, "jwks_url", key) + viper.Set("mutators.id_token.config.issuer_url", "/"+issuers[i%len(issuers)]) + + b.StartTimer() + err := a.Mutate(r, tc.Session, config, rl) + b.StopTimer() + + require.NoError(b, err) + } + }) + } + }) + } +} From b34848597807dfbf9ae23d5cc420f27645bdf610 Mon Sep 17 00:00:00 2001 From: aeneasr Date: Thu, 5 Mar 2020 14:37:54 +0100 Subject: [PATCH 2/2] u --- credentials/fetcher_default.go | 1 + go.mod | 6 ------ go.sum | 19 +++++-------------- pipeline/mutate/mutator_id_token_test.go | 1 + 4 files changed, 7 insertions(+), 20 deletions(-) diff --git a/credentials/fetcher_default.go b/credentials/fetcher_default.go index 99977974e2..7f2c422e6b 100644 --- a/credentials/fetcher_default.go +++ b/credentials/fetcher_default.go @@ -132,6 +132,7 @@ func (s *FetcherDefault) ResolveKey(ctx context.Context, locations []url.URL, ki return nil, errors.WithStack(herodot. ErrInternalServerError. + WithDetail("jwks_urls", fmt.Sprintf("%v", locations)). WithReasonf(`JSON Web Key ID "%s" with use "%s" could not be found in any of the provided URIs.`, kid, use). WithDebug("Check that the provided JSON Web Key URIs contain a key that can verify the signature of the provided JSON Web Key ID."), ) diff --git a/go.mod b/go.mod index e8b7d050d3..a163ffa7c1 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/ory/oathkeeper require ( github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/sprig v2.20.0+incompatible - github.com/allegro/bigcache v1.2.1 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/auth0/go-jwt-middleware v0.0.0-20170425171159-5493cabe49f7 github.com/blang/semver v3.5.1+incompatible @@ -17,14 +16,12 @@ require ( github.com/go-openapi/runtime v0.19.5 github.com/go-openapi/strfmt v0.19.3 github.com/go-openapi/swag v0.19.5 - github.com/go-openapi/validate v0.19.3 github.com/go-sql-driver/mysql v1.4.1 github.com/go-swagger/go-swagger v0.21.1-0.20200107003254-1c98855b472d github.com/gobuffalo/httptest v1.0.2 github.com/gobuffalo/packr/v2 v2.0.0-rc.15 github.com/gobwas/glob v0.2.3 github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/mock v1.3.1 github.com/golang/protobuf v1.3.3 // indirect github.com/google/uuid v1.1.1 @@ -67,7 +64,4 @@ require ( gopkg.in/square/go-jose.v2 v2.3.1 ) -// Fix for https://github.com/golang/lint/issues/436 -replace github.com/golang/lint => github.com/golang/lint v0.0.0-20190227174305-8f45f776aaf1 - go 1.13 diff --git a/go.sum b/go.sum index fc5551231a..bcd3d6d126 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,7 @@ github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+q github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= @@ -35,8 +36,6 @@ github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f h1:zvClvFQwU++UpIUBGC8YmD github.com/ajg/form v0.0.0-20160822230020-523a5da1a92f/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc= -github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= @@ -88,6 +87,7 @@ github.com/dgraph-io/ristretto v0.0.1 h1:cJwdnj42uV8Jg4+KLrYovLiCgIfz9wtWm6E6KA+ github.com/dgraph-io/ristretto v0.0.1/go.mod h1:T40EBc7CJke8TkpiYfGGKAeFjSaxuFXhuXRyumBd6RE= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= @@ -369,8 +369,6 @@ github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hz github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= @@ -568,23 +566,15 @@ github.com/ory/herodot v0.6.2 h1:zOb5MsuMn7AH9/Ewc/EK83yqcNViK1m1l3C2UuP3RcA= github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow= github.com/ory/jsonschema/v3 v3.0.1 h1:xzV7w2rt/Qn+jvh71joIXNKKOCqqNyTlaIxdxU0IQJc= github.com/ory/jsonschema/v3 v3.0.1/go.mod h1:jgLHekkFk0uiGdEWGleC+tOm6JSSP8cbf17PnBuGXlw= -github.com/ory/ladon v1.0.1/go.mod h1:1VhCA2mBtaMhRUS6VS0d9qrNVDQnFXqSRb5D0NvQUPY= github.com/ory/ladon v1.1.0 h1:6tgazU2J3Z3odPs1f0qn729kRXCAtlJROliuWUHedV0= github.com/ory/ladon v1.1.0/go.mod h1:25bNc/Glx/8xCH7MbItDxjvviAmFQ+aYxb1V1SE5wlg= github.com/ory/pagination v0.0.1/go.mod h1:d1ToRROAUleriPhmb2dYbhANhhLwZ8s395m2yJCDFh8= -github.com/ory/sdk/swagutil v0.0.0-20200123152503-0d50960e70bd h1:QrEYSnaOX6kpyBcQGUlExcI4RwCq2S/Wta/zbgT74Kk= -github.com/ory/sdk/swagutil v0.0.0-20200123152503-0d50960e70bd/go.mod h1:Ufg1eAyz+Zt3+oweSZVThG13ewewWCKwBmoNmK8Z0co= -github.com/ory/sdk/swagutil v0.0.0-20200131170418-ead0c2285f93 h1:+QLNv/tFJbwIpJ7fYGx07G0+IxO8FhhyaVGVkX6RAFM= -github.com/ory/sdk/swagutil v0.0.0-20200131170418-ead0c2285f93/go.mod h1:Ufg1eAyz+Zt3+oweSZVThG13ewewWCKwBmoNmK8Z0co= github.com/ory/sdk/swagutil v0.0.0-20200202121523-307941feee4b h1:xn3WcZ8Oy285KYiCnoscQxkyRfJZT+KhIbU9LEhPLyw= github.com/ory/sdk/swagutil v0.0.0-20200202121523-307941feee4b/go.mod h1:Ufg1eAyz+Zt3+oweSZVThG13ewewWCKwBmoNmK8Z0co= github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28= github.com/ory/viper v1.5.7 h1:VeXfcBgTG3SCMlw4hmXkazXPZbyvBTdUjS7Dxm3gOjQ= github.com/ory/viper v1.5.7/go.mod h1:+Mfm2gCDqtYRn5gVaLsBtXACO59zITETZQ/jQwc9SZo= -github.com/ory/x v0.0.87/go.mod h1:wrnJRjIfYXFY/AUiuUlcIUpLBDxFtWc+8x6toAeLZXU= github.com/ory/x v0.0.88/go.mod h1:wrnJRjIfYXFY/AUiuUlcIUpLBDxFtWc+8x6toAeLZXU= -github.com/ory/x v0.0.91 h1:4sySRGI1dExt3FpvXcnenpagoM6oQeEvboQ53/tcY9g= -github.com/ory/x v0.0.91/go.mod h1:lfcTaGXpTZs7IEQAW00r9EtTCOxD//SiP5uWtNiz31g= github.com/ory/x v0.0.93 h1:lZG4tjrkJ8cxI85463kD7Cq8h1YxZcPVzCkFcu2WXXI= github.com/ory/x v0.0.93/go.mod h1:lfcTaGXpTZs7IEQAW00r9EtTCOxD//SiP5uWtNiz31g= github.com/ory/x v0.0.95 h1:DBPmINrK39lL0NThrg6iSIV22aTEU44ehHVvRDk5tc4= @@ -663,6 +653,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.0/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -783,6 +774,7 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -906,7 +898,6 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190711191110-9a621aea19f8/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191224055732-dd894d0a8a40 h1:UyP2XDSgSc8ldYCxAK735zQxeH3Gd81sK7Iy7AoaVxk= golang.org/x/tools v0.0.0-20191224055732-dd894d0a8a40/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -914,6 +905,7 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200203215610-ab391d50b528 h1:iINh7uA444sE+iZXG/dsGMWccpjX751evDOE4UvDiaA= golang.org/x/tools v0.0.0-20200203215610-ab391d50b528/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -954,7 +946,6 @@ gopkg.in/mail.v2 v2.0.0-20180731213649-a0242b2233b4/go.mod h1:htwXN1Qh09vZJ1NVKx gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.1.9/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/pipeline/mutate/mutator_id_token_test.go b/pipeline/mutate/mutator_id_token_test.go index 46709972a1..69f63c1ae3 100644 --- a/pipeline/mutate/mutator_id_token_test.go +++ b/pipeline/mutate/mutator_id_token_test.go @@ -274,6 +274,7 @@ func TestMutatorIDToken(t *testing.T) { Rule: &rule.Rule{ID: "test-rule"}, Session: &authn.AuthenticationSession{Subject: "foo", Extra: map[string]interface{}{"abc": "value1", "def": "value2"}}, Config: json.RawMessage([]byte(`{"claims": "{\"custom-claim\": \"{{ print .Extra.abc }}/{{ print .Extra.def }}\", \"aud\": [\"foo\", \"bar\"]}", "jwks_url": "file://../../test/stub/jwks-ecdsa.json"}`)), + K: "file://../../test/stub/jwks-ecdsa.json", } cache := template.New("rules")