-
Notifications
You must be signed in to change notification settings - Fork 405
feat: make id_token mutator cache configurable #1177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,28 +38,28 @@ type MutatorIDToken struct { | |
| templates *template.Template | ||
| templatesLock sync.Mutex | ||
|
|
||
| tokenCache *ristretto.Cache[string, *idTokenCacheContainer] | ||
| tokenCacheEnabled bool | ||
| tokenCache *ristretto.Cache[string, *idTokenCacheContainer] | ||
| } | ||
|
|
||
| type CredentialsIDTokenConfig struct { | ||
| Claims string `json:"claims"` | ||
| IssuerURL string `json:"issuer_url"` | ||
| JWKSURL string `json:"jwks_url"` | ||
| TTL string `json:"ttl"` | ||
| Claims string `json:"claims"` | ||
| IssuerURL string `json:"issuer_url"` | ||
| JWKSURL string `json:"jwks_url"` | ||
| TTL string `json:"ttl"` | ||
| Cache IdTokenCacheConfig `json:"cache"` | ||
| } | ||
|
|
||
| type IdTokenCacheConfig struct { | ||
| Enabled bool `json:"enabled"` | ||
| MaxCost int `json:"max_cost"` | ||
| } | ||
|
|
||
| func (c *CredentialsIDTokenConfig) ClaimsTemplateID() string { | ||
| return fmt.Sprintf("%x", md5.Sum([]byte(c.Claims))) | ||
| } | ||
|
|
||
| func NewMutatorIDToken(c configuration.Provider, r MutatorIDTokenRegistry) *MutatorIDToken { | ||
| cache, _ := ristretto.NewCache(&ristretto.Config[string, *idTokenCacheContainer]{ | ||
| NumCounters: 10000, | ||
| MaxCost: 1 << 25, | ||
| BufferItems: 64, | ||
| }) | ||
| return &MutatorIDToken{r: r, c: c, templates: x.NewTemplate("id_token"), tokenCache: cache, tokenCacheEnabled: true} | ||
| return &MutatorIDToken{r: r, c: c, templates: x.NewTemplate("id_token")} | ||
| } | ||
|
|
||
| func (a *MutatorIDToken) GetID() string { | ||
|
|
@@ -70,10 +70,6 @@ func (a *MutatorIDToken) WithCache(t *template.Template) { | |
| a.templates = t | ||
| } | ||
|
|
||
| func (a *MutatorIDToken) SetCaching(token bool) { | ||
| a.tokenCacheEnabled = token | ||
| } | ||
|
|
||
| type idTokenCacheContainer struct { | ||
| ExpiresAt time.Time | ||
| Token string | ||
|
|
@@ -86,7 +82,7 @@ func (a *MutatorIDToken) cacheKey(config *CredentialsIDTokenConfig, ttl time.Dur | |
| } | ||
|
|
||
| func (a *MutatorIDToken) tokenFromCache(config *CredentialsIDTokenConfig, session *authn.AuthenticationSession, claims []byte, ttl time.Duration) (string, bool) { | ||
| if !a.tokenCacheEnabled { | ||
| if !config.Cache.Enabled { | ||
| return "", false | ||
| } | ||
|
|
||
|
|
@@ -106,7 +102,7 @@ func (a *MutatorIDToken) tokenFromCache(config *CredentialsIDTokenConfig, sessio | |
| } | ||
|
|
||
| func (a *MutatorIDToken) tokenToCache(config *CredentialsIDTokenConfig, session *authn.AuthenticationSession, claims []byte, ttl time.Duration, expiresAt time.Time, token string) { | ||
| if !a.tokenCacheEnabled { | ||
| if !config.Cache.Enabled { | ||
| return | ||
| } | ||
|
|
||
|
|
@@ -197,7 +193,11 @@ func (a *MutatorIDToken) Validate(config json.RawMessage) error { | |
| } | ||
|
|
||
| func (a *MutatorIDToken) Config(config json.RawMessage) (*CredentialsIDTokenConfig, error) { | ||
| var c CredentialsIDTokenConfig | ||
| c := CredentialsIDTokenConfig{ | ||
| Cache: IdTokenCacheConfig{ | ||
| Enabled: true, // default to true | ||
| }, | ||
| } | ||
| if err := a.c.MutatorConfig(a.GetID(), config, &c); err != nil { | ||
| return nil, NewErrMutatorMisconfigured(a, err) | ||
| } | ||
|
|
@@ -206,5 +206,29 @@ func (a *MutatorIDToken) Config(config json.RawMessage) (*CredentialsIDTokenConf | |
| c.TTL = "15m" | ||
| } | ||
|
|
||
| cost := int64(c.Cache.MaxCost) | ||
| if cost == 0 { | ||
| cost = 1 << 25 | ||
| } | ||
|
|
||
| if a.tokenCache == nil || a.tokenCache.MaxCost() != cost { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The On prod envs, this should not happen, and we'll keep skipping this branch.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So basically this does hot reloading if the cost changes? |
||
| cache, err := ristretto.NewCache(&ristretto.Config[string, *idTokenCacheContainer]{ | ||
| // Guessed approximation of max number of items. | ||
| NumCounters: cost * 4, | ||
| // Allocate a max | ||
| MaxCost: cost, | ||
| // This is a best-practice value. | ||
| BufferItems: 64, | ||
| Cost: func(container *idTokenCacheContainer) int64 { | ||
| return int64(len(container.Token)) | ||
| }, | ||
| }) | ||
|
|
||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| a.tokenCache = cache | ||
| } | ||
|
|
||
| return &c, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1140,6 +1140,25 @@ | |
| "pattern": "^[0-9]+(ns|us|ms|s|m|h)$", | ||
| "default": "15m", | ||
| "examples": ["1h", "1m", "30s"] | ||
| }, | ||
| "cache": { | ||
| "additionalProperties": false, | ||
| "type": "object", | ||
| "properties": { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a bunch of other caches in the config schema already. Can you make your change so that it is more similar (identical) to those other cache configurations? The
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with the remark on I think the reason for the However, one can also make the decision to let the user make this decision anyway :) For the to avoid introducing a breaking change. Currently, the And finally, this cache config has no TTL, because the id_token mutator already has a TTL config value for the JWT expiration date.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, we would probably have one namespaced cache for all of oathkeeper and then use that everywhere, but this is good for now! |
||
| "enabled": { | ||
| "title": "Enabled", | ||
| "type": "boolean", | ||
| "default": true, | ||
| "examples": [false, true], | ||
| "description": "En-/disables this component." | ||
| }, | ||
| "max_cost": { | ||
| "type": "integer", | ||
| "default": 33554432, | ||
| "title": "Maximum Cached Cost", | ||
| "description": "The cost of one cached JSON Web Token is the length of its string form." | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| "additionalProperties": false | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.