-
Notifications
You must be signed in to change notification settings - Fork 124
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
ONYX-11330: Fix cache of signing key #2353
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 |
---|---|---|
@@ -0,0 +1,25 @@ | ||
module Authentication | ||
module AuthnJwt | ||
module SigningKey | ||
# FetchCachedSigningKey is a wrapper of FetchSigningKeyInterface interface, | ||
# in order to be able to store the signing key in our cache mechanism. If signing_key_interface don't have | ||
# fetch_signing_key it is extreme case that error need to be raised so it can be investigated so reek will ignore | ||
# this. | ||
# :reek:InstanceVariableAssumption | ||
FetchCachedSigningKey = CommandClass.new( | ||
dependencies: {}, | ||
inputs: %i[signing_key_provider] | ||
) do | ||
def call | ||
fetch_signing_key | ||
end | ||
|
||
private | ||
|
||
def fetch_signing_key | ||
@signing_key_provider.fetch_signing_key | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,12 +7,23 @@ module ValidateAndDecode | |
# for the 2nd validation | ||
ValidateAndDecodeToken ||= CommandClass.new( | ||
dependencies: { | ||
fetch_signing_key: ::Util::ConcurrencyLimitedCache.new( | ||
nessiLahav marked this conversation as resolved.
Show resolved
Hide resolved
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. Identical blocks of code found in 2 locations. Consider refactoring. 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. Identical blocks of code found in 2 locations. Consider refactoring. 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. Identical blocks of code found in 2 locations. Consider refactoring. |
||
::Util::RateLimitedCache.new( | ||
::Authentication::AuthnJwt::SigningKey::FetchCachedSigningKey.new, | ||
refreshes_per_interval: CACHE_REFRESHES_PER_INTERVAL, | ||
rate_limit_interval: CACHE_RATE_LIMIT_INTERVAL, | ||
logger: Rails.logger | ||
), | ||
max_concurrent_requests: CACHE_MAX_CONCURRENT_REQUESTS, | ||
logger: Rails.logger | ||
), | ||
verify_and_decode_token: ::Authentication::Jwt::VerifyAndDecodeToken.new, | ||
fetch_jwt_claims_to_validate: ::Authentication::AuthnJwt::ValidateAndDecode::FetchJwtClaimsToValidate.new, | ||
get_verification_option_by_jwt_claim: ::Authentication::AuthnJwt::ValidateAndDecode::GetVerificationOptionByJwtClaim.new, | ||
create_signing_key_provider: ::Authentication::AuthnJwt::SigningKey::CreateSigningKeyProvider.new, | ||
logger: Rails.logger | ||
}, | ||
inputs: %i[authentication_parameters fetch_signing_key] | ||
inputs: %i[authentication_parameters] | ||
) do | ||
extend(Forwardable) | ||
def_delegators(:@authentication_parameters, :jwt_token) | ||
|
@@ -31,12 +42,22 @@ def call | |
|
||
private | ||
|
||
def signing_key_provider | ||
@signing_key_provider ||= @create_signing_key_provider.call( | ||
authentication_parameters: @authentication_parameters | ||
) | ||
end | ||
|
||
def validate_token_exists | ||
raise Errors::Authentication::AuthnJwt::MissingToken if jwt_token.blank? | ||
end | ||
|
||
def fetch_signing_key(force_read: false) | ||
@jwks = @fetch_signing_key.call(refresh: force_read) | ||
@jwks = @fetch_signing_key.call( | ||
refresh: force_read, | ||
cache_key: signing_key_provider.signing_key_uri, | ||
signing_key_provider: signing_key_provider | ||
) | ||
@logger.debug(LogMessages::Authentication::AuthnJwt::SigningKeysFetchedFromCache.new) | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,17 @@ module AuthnJwt | |
|
||
ValidateStatus = CommandClass.new( | ||
dependencies: { | ||
create_signing_key_interface: Authentication::AuthnJwt::SigningKey::CreateSigningKeyFactory.new, | ||
fetch_signing_key: ::Util::ConcurrencyLimitedCache.new( | ||
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. Identical blocks of code found in 2 locations. Consider refactoring. 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. Identical blocks of code found in 2 locations. Consider refactoring. 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. Identical blocks of code found in 2 locations. Consider refactoring. |
||
::Util::RateLimitedCache.new( | ||
::Authentication::AuthnJwt::SigningKey::FetchCachedSigningKey.new, | ||
refreshes_per_interval: CACHE_REFRESHES_PER_INTERVAL, | ||
rate_limit_interval: CACHE_RATE_LIMIT_INTERVAL, | ||
logger: Rails.logger | ||
), | ||
max_concurrent_requests: CACHE_MAX_CONCURRENT_REQUESTS, | ||
logger: Rails.logger | ||
), | ||
create_signing_key_provider: Authentication::AuthnJwt::SigningKey::CreateSigningKeyProvider.new, | ||
fetch_issuer_value: Authentication::AuthnJwt::ValidateAndDecode::FetchIssuerValue.new, | ||
fetch_audience_value: Authentication::AuthnJwt::ValidateAndDecode::FetchAudienceValue.new, | ||
fetch_enforced_claims: Authentication::AuthnJwt::RestrictionValidation::FetchEnforcedClaims.new, | ||
|
@@ -134,25 +144,15 @@ def webservice | |
end | ||
|
||
def validate_signing_key | ||
fetch_signing_key.call | ||
@logger.debug(LogMessages::Authentication::AuthnJwt::ValidatedSigningKeyConfiguration.new) | ||
end | ||
|
||
def fetch_signing_key | ||
@fetch_signing_key ||= ::Util::ConcurrencyLimitedCache.new( | ||
::Util::RateLimitedCache.new( | ||
fetch_signing_key_interface, | ||
refreshes_per_interval: ::Authentication::AuthnJwt::CACHE_REFRESHES_PER_INTERVAL, | ||
rate_limit_interval: ::Authentication::AuthnJwt::CACHE_RATE_LIMIT_INTERVAL, | ||
logger: @logger | ||
), | ||
max_concurrent_requests: ::Authentication::AuthnJwt::CACHE_MAX_CONCURRENT_REQUESTS, | ||
logger: @logger | ||
@fetch_signing_key.call( | ||
cache_key: signing_key_provider.signing_key_uri, | ||
signing_key_provider: signing_key_provider | ||
) | ||
@logger.debug(LogMessages::Authentication::AuthnJwt::ValidatedSigningKeyConfiguration.new) | ||
end | ||
|
||
def fetch_signing_key_interface | ||
@fetch_signing_key_interface ||= @create_signing_key_interface.call( | ||
def signing_key_provider | ||
@signing_key_provider ||= @create_signing_key_provider.call( | ||
authentication_parameters: authentication_parameters | ||
) | ||
end | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,15 +20,20 @@ def initialize( | |
# This method is passed exactly the same named arguments you'd pass to the | ||
# callable object, but you can optionally include the `refresh: true` to | ||
# force recalculation. | ||
# | ||
# In this case we should call @cache[cache_key] because setting it to variable requires treatment of case cache_key | ||
# not in case before the flow runs and it can cause unexpected behaviour so reek will ignore this. | ||
# reek:DuplicateMethodCall | ||
def call(**args) | ||
cache_key = cache_key(args) | ||
@concurrency_mutex.synchronize do | ||
if @concurrent_requests >= @max_concurrent_requests | ||
@logger.debug( | ||
LogMessages::Util::ConcurrencyLimitedCacheReached.new(@max_concurrent_requests) | ||
) | ||
raise Errors::Util::ConcurrencyLimitReachedBeforeCacheInitialization unless @cache.key?(args) | ||
raise Errors::Util::ConcurrencyLimitReachedBeforeCacheInitialization unless @cache.key?(cache_key) | ||
|
||
return @cache[args] | ||
return @cache[cache_key] | ||
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. Util::ConcurrencyLimitedCache#call calls '@cache[cache_key]' 2 times 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. Util::ConcurrencyLimitedCache#call calls '@cache[cache_key]' 2 times 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. Util::ConcurrencyLimitedCache#call calls '@cache[cache_key]' 2 times 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. Util::ConcurrencyLimitedCache#call calls '@cache[cache_key]' 2 times |
||
end | ||
|
||
@concurrent_requests += 1 | ||
|
@@ -40,15 +45,15 @@ def call(**args) | |
end | ||
|
||
@semaphore.synchronize do | ||
recalculate(args) | ||
@cache[args] | ||
recalculate(args, cache_key) | ||
@cache[cache_key] | ||
end | ||
end | ||
|
||
private | ||
|
||
def recalculate(args) | ||
@cache[args] = @target.call(**args) | ||
def recalculate(args, cache_key) | ||
@cache[cache_key] = @target.call(**args) | ||
@logger.debug(LogMessages::Util::ConcurrencyLimitedCacheUpdated.new) | ||
decrease_concurrent_requests | ||
rescue => e | ||
|
@@ -66,5 +71,20 @@ def decrease_concurrent_requests | |
) | ||
end | ||
end | ||
|
||
# Function returning cache key to store/retrieve in the cache | ||
def cache_key(args) | ||
if args.key?(:cache_key) | ||
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. Util::ConcurrencyLimitedCache#cache_key refers to 'args' more than self (maybe move it to another class?) 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. Util::ConcurrencyLimitedCache#cache_key refers to 'args' more than self (maybe move it to another class?) 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. Util::ConcurrencyLimitedCache#cache_key refers to 'args' more than self (maybe move it to another class?) |
||
cache_key = args.fetch(:cache_key) | ||
@logger.debug( | ||
LogMessages::Util::ConcurrencyLimitedCacheKeyRetrieved.new( | ||
cache_key | ||
) | ||
) | ||
else | ||
cache_key = args | ||
end | ||
cache_key | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hilagross @shulifink
Please review changelog message