Skip to content

Commit

Permalink
Refactor/hmac validator (#151) (#216)
Browse files Browse the repository at this point in the history
* Replace doubles with singles for style

* Bring hmac validator test closer to real life

* Tidy-up hmac validator code

* Backwards compatibility aliasing

Co-authored-by: Matas Zanevičius <matas-zanevicius@users.noreply.github.com>
  • Loading branch information
jillingk and matas-zanevicius authored Jan 15, 2024
1 parent 80bc3f4 commit e29e9d0
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 35 deletions.
32 changes: 12 additions & 20 deletions lib/adyen/utils/hmac_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def valid_notification_hmac?(notification_request_item, hmac_key)

def valid_webhook_hmac?(webhook_request_item, hmac_key)
expected_sign = calculate_webhook_hmac(webhook_request_item, hmac_key)
merchant_sign = fetch(webhook_request_item, 'additionalData.hmacSignature')
merchant_sign =
webhook_request_item.dig('additionalData', 'hmacSignature')

expected_sign == merchant_sign
end
Expand All @@ -29,29 +30,20 @@ def calculate_notification_hmac(notification_request_item, hmac_key)
def calculate_webhook_hmac(webhook_request_item, hmac_key)
data = data_to_sign(webhook_request_item)

Base64.strict_encode64(OpenSSL::HMAC.digest(HMAC_ALGORITHM, [hmac_key].pack('H*'), data))
Base64.strict_encode64(
OpenSSL::HMAC.digest(HMAC_ALGORITHM, [hmac_key].pack('H*'), data)
)
end

# TODO: Deprecate instead of aliasing
alias valid_notification_hmac? valid_webhook_hmac?
alias calculate_notification_hmac calculate_webhook_hmac

def data_to_sign(webhook_request_item)
data = WEBHOOK_VALIDATION_KEYS.map { |key| fetch(webhook_request_item, key).to_s }
.join(DATA_SEPARATOR)
end

private

def fetch(hash, keys)
value = hash
keys.to_s.split('.').each do |key|
value = if key.to_i.to_s == key
value[key.to_i]
else
value[key].nil? ? value[key.to_sym] : value[key]
end
break if value.nil?
end

value
WEBHOOK_VALIDATION_KEYS
.map { webhook_request_item.dig(*_1.split('.')).to_s }
.compact
.join(DATA_SEPARATOR)
end
end
end
Expand Down
30 changes: 15 additions & 15 deletions spec/utils/hmac_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
let(:expected_sign) { 'coqCmt/IZ4E3CzPvMY8zTjQVL5hYJUiBRg8UU+iCWo0=' }
let(:webhook_request_item) do
{
additionalData: {
hmacSignature: expected_sign
'additionalData' => {
'hmacSignature' => expected_sign
},
amount: {
value: 1130,
currency: 'EUR'
'amount' => {
'value' => 1130,
'currency' => 'EUR'
},
pspReference: '7914073381342284',
eventCode: 'AUTHORISATION',
merchantAccountCode: 'TestMerchant',
merchantReference: 'TestPayment-1407325143704',
paymentMethod: 'visa',
success: 'true'
'pspReference' => '7914073381342284',
'eventCode' => 'AUTHORISATION',
'merchantAccountCode' => 'TestMerchant',
'merchantReference' => 'TestPayment-1407325143704',
'paymentMethod' => 'visa',
'success' => 'true'
}
end

Expand All @@ -44,22 +44,22 @@
end

it 'should validate backslashes correctly' do
webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/backslash_webhook.json"))
webhook = JSON.parse(json_from_file('mocks/responses/Webhooks/backslash_webhook.json'))
expect(validator.valid_webhook_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
end

it 'should validate colons correctly' do
webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/colon_webhook.json"))
webhook = JSON.parse(json_from_file('mocks/responses/Webhooks/colon_webhook.json'))
expect(validator.valid_webhook_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
end

it 'should validate forward slashes correctly' do
webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/forwardslash_webhook.json"))
webhook = JSON.parse(json_from_file('mocks/responses/Webhooks/forwardslash_webhook.json'))
expect(validator.valid_webhook_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
end

it 'should validate mix of slashes and colon correctly' do
webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/mixed_webhook.json"))
webhook = JSON.parse(json_from_file('mocks/responses/Webhooks/mixed_webhook.json'))
expect(validator.valid_webhook_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
end
end
Expand Down

0 comments on commit e29e9d0

Please sign in to comment.