cybersource_rest_client 0.0.80 → 0.0.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/lib/AuthenticationSDK/authentication/jwt/JwtToken.rb +17 -7
  3. data/lib/AuthenticationSDK/core/Authorization.rb +2 -2
  4. data/lib/AuthenticationSDK/core/MerchantConfig.rb +277 -23
  5. data/lib/AuthenticationSDK/util/Cache.rb +144 -8
  6. data/lib/AuthenticationSDK/util/CachedMLEKId.rb +17 -0
  7. data/lib/AuthenticationSDK/util/CertificateUtility.rb +109 -29
  8. data/lib/AuthenticationSDK/util/Constants.rb +4 -0
  9. data/lib/AuthenticationSDK/util/MLEUtility.rb +167 -11
  10. data/lib/AuthenticationSDK/util/Utility.rb +37 -1
  11. data/lib/cybersource_rest_client/api/bank_account_validation_api.rb +6 -2
  12. data/lib/cybersource_rest_client/api/batches_api.rb +24 -8
  13. data/lib/cybersource_rest_client/api/billing_agreements_api.rb +18 -6
  14. data/lib/cybersource_rest_client/api/bin_lookup_api.rb +6 -2
  15. data/lib/cybersource_rest_client/api/capture_api.rb +6 -2
  16. data/lib/cybersource_rest_client/api/chargeback_details_api.rb +6 -2
  17. data/lib/cybersource_rest_client/api/chargeback_summaries_api.rb +6 -2
  18. data/lib/cybersource_rest_client/api/conversion_details_api.rb +6 -2
  19. data/lib/cybersource_rest_client/api/create_new_webhooks_api.rb +18 -6
  20. data/lib/cybersource_rest_client/api/credit_api.rb +6 -2
  21. data/lib/cybersource_rest_client/api/customer_api.rb +24 -8
  22. data/lib/cybersource_rest_client/api/customer_payment_instrument_api.rb +30 -10
  23. data/lib/cybersource_rest_client/api/customer_shipping_address_api.rb +30 -10
  24. data/lib/cybersource_rest_client/api/decision_manager_api.rb +30 -10
  25. data/lib/cybersource_rest_client/api/device_de_association_api.rb +12 -4
  26. data/lib/cybersource_rest_client/api/device_search_api.rb +12 -4
  27. data/lib/cybersource_rest_client/api/download_dtd_api.rb +6 -2
  28. data/lib/cybersource_rest_client/api/download_xsd_api.rb +6 -2
  29. data/lib/cybersource_rest_client/api/emv_tag_details_api.rb +12 -4
  30. data/lib/cybersource_rest_client/api/enrollment_api.rb +104 -0
  31. data/lib/cybersource_rest_client/api/flex_api_api.rb +6 -2
  32. data/lib/cybersource_rest_client/api/instructions_api.rb +452 -0
  33. data/lib/cybersource_rest_client/api/instrument_identifier_api.rb +36 -12
  34. data/lib/cybersource_rest_client/api/interchange_clearing_level_details_api.rb +6 -2
  35. data/lib/cybersource_rest_client/api/invoice_settings_api.rb +12 -4
  36. data/lib/cybersource_rest_client/api/invoices_api.rb +42 -14
  37. data/lib/cybersource_rest_client/api/manage_webhooks_api.rb +42 -14
  38. data/lib/cybersource_rest_client/api/merchant_boarding_api.rb +12 -4
  39. data/lib/cybersource_rest_client/api/merchant_defined_fields_api.rb +24 -8
  40. data/lib/cybersource_rest_client/api/microform_integration_api.rb +6 -2
  41. data/lib/cybersource_rest_client/api/net_fundings_api.rb +6 -2
  42. data/lib/cybersource_rest_client/api/notification_of_changes_api.rb +6 -2
  43. data/lib/cybersource_rest_client/api/offers_api.rb +12 -4
  44. data/lib/cybersource_rest_client/api/orders_api.rb +12 -4
  45. data/lib/cybersource_rest_client/api/payer_authentication_api.rb +18 -6
  46. data/lib/cybersource_rest_client/api/payment_batch_summaries_api.rb +6 -2
  47. data/lib/cybersource_rest_client/api/payment_instrument_api.rb +24 -8
  48. data/lib/cybersource_rest_client/api/payment_links_api.rb +24 -8
  49. data/lib/cybersource_rest_client/api/payment_tokens_api.rb +6 -2
  50. data/lib/cybersource_rest_client/api/payments_api.rb +36 -12
  51. data/lib/cybersource_rest_client/api/payouts_api.rb +6 -2
  52. data/lib/cybersource_rest_client/api/plans_api.rb +48 -16
  53. data/lib/cybersource_rest_client/api/purchase_and_refund_details_api.rb +6 -2
  54. data/lib/cybersource_rest_client/api/push_funds_api.rb +6 -2
  55. data/lib/cybersource_rest_client/api/refund_api.rb +12 -4
  56. data/lib/cybersource_rest_client/api/report_definitions_api.rb +12 -4
  57. data/lib/cybersource_rest_client/api/report_downloads_api.rb +6 -2
  58. data/lib/cybersource_rest_client/api/report_subscriptions_api.rb +30 -10
  59. data/lib/cybersource_rest_client/api/reports_api.rb +18 -6
  60. data/lib/cybersource_rest_client/api/retrieval_details_api.rb +6 -2
  61. data/lib/cybersource_rest_client/api/retrieval_summaries_api.rb +6 -2
  62. data/lib/cybersource_rest_client/api/reversal_api.rb +12 -4
  63. data/lib/cybersource_rest_client/api/search_transactions_api.rb +12 -4
  64. data/lib/cybersource_rest_client/api/secure_file_share_api.rb +12 -4
  65. data/lib/cybersource_rest_client/api/subscriptions_api.rb +48 -16
  66. data/lib/cybersource_rest_client/api/subscriptions_follow_ons_api.rb +12 -4
  67. data/lib/cybersource_rest_client/api/taxes_api.rb +12 -4
  68. data/lib/cybersource_rest_client/api/token_api.rb +12 -4
  69. data/lib/cybersource_rest_client/api/tokenize_api.rb +6 -2
  70. data/lib/cybersource_rest_client/api/tokenized_card_api.rb +24 -8
  71. data/lib/cybersource_rest_client/api/transaction_batches_api.rb +24 -8
  72. data/lib/cybersource_rest_client/api/transaction_details_api.rb +6 -2
  73. data/lib/cybersource_rest_client/api/transient_token_data_api.rb +12 -4
  74. data/lib/cybersource_rest_client/api/unified_checkout_capture_context_api.rb +6 -2
  75. data/lib/cybersource_rest_client/api/user_management_api.rb +6 -2
  76. data/lib/cybersource_rest_client/api/user_management_search_api.rb +6 -2
  77. data/lib/cybersource_rest_client/api/verification_api.rb +12 -4
  78. data/lib/cybersource_rest_client/api/void_api.rb +30 -10
  79. data/lib/cybersource_rest_client/api_client.rb +20 -3
  80. data/lib/cybersource_rest_client/models/acpv1instructions_decline_threshold.rb +232 -0
  81. data/lib/cybersource_rest_client/models/acpv1instructions_mandates.rb +405 -0
  82. data/lib/cybersource_rest_client/models/acpv1instructions_recurring_payment_information.rb +191 -0
  83. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconf_processor_info_payment_instrument_verifi_results.rb +213 -0
  84. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_confirmation_data.rb +229 -0
  85. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_merchant_information.rb +258 -0
  86. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_order_information.rb +377 -0
  87. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_order_information_shipping_details.rb +206 -0
  88. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_processor_information.rb +425 -0
  89. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_processor_information_payment_instrument.rb +190 -0
  90. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_attachments.rb +258 -0
  91. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_client_reference_information.rb +215 -0
  92. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_mandate_reference_data.rb +205 -0
  93. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_merchant_information.rb +334 -0
  94. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_merchant_information_merchant_descriptor.rb +250 -0
  95. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information.rb +228 -0
  96. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_amount_detail.rb +354 -0
  97. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_items.rb +367 -0
  98. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_items_additional_info.rb +231 -0
  99. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_items_policies.rb +346 -0
  100. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_line_items.rb +190 -0
  101. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_ship_to.rb +686 -0
  102. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_payment_options.rb +217 -0
  103. data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_transaction_data.rb +348 -0
  104. data/lib/cybersource_rest_client/models/acpv1tokens_assurance_data.rb +313 -0
  105. data/lib/cybersource_rest_client/models/acpv1tokens_authenticated_identities.rb +237 -0
  106. data/lib/cybersource_rest_client/models/acpv1tokens_authentication_context.rb +191 -0
  107. data/lib/cybersource_rest_client/models/acpv1tokens_bill_to.rb +400 -0
  108. data/lib/cybersource_rest_client/models/acpv1tokens_buyer_information.rb +229 -0
  109. data/lib/cybersource_rest_client/models/acpv1tokens_buyer_information_personal_identification.rb +212 -0
  110. data/lib/cybersource_rest_client/models/acpv1tokens_consent_data.rb +297 -0
  111. data/lib/cybersource_rest_client/models/acpv1tokens_consumer_identity.rb +268 -0
  112. data/lib/cybersource_rest_client/models/acpv1tokens_device_information.rb +378 -0
  113. data/lib/cybersource_rest_client/models/acpv1tokens_device_information_device_data.rb +302 -0
  114. data/lib/cybersource_rest_client/models/acpv1tokens_enrollment_reference_data.rb +207 -0
  115. data/lib/cybersource_rest_client/models/acpv1tokens_payment_information.rb +215 -0
  116. data/lib/cybersource_rest_client/models/acpv1tokens_payment_information_customer.rb +197 -0
  117. data/lib/cybersource_rest_client/models/acpv1tokens_payment_information_instrument_identifier.rb +206 -0
  118. data/lib/cybersource_rest_client/models/acpv1tokens_payment_information_payment_instrument.rb +197 -0
  119. data/lib/cybersource_rest_client/models/agentic_cancel_purchase_intent_request.rb +257 -0
  120. data/lib/cybersource_rest_client/models/agentic_card_enrollment_bad_request_response400.rb +189 -0
  121. data/lib/cybersource_rest_client/models/agentic_card_enrollment_bad_request_response400_error.rb +238 -0
  122. data/lib/cybersource_rest_client/models/agentic_card_enrollment_bad_request_response400_error_detail.rb +224 -0
  123. data/lib/cybersource_rest_client/models/agentic_card_enrollment_request.rb +325 -0
  124. data/lib/cybersource_rest_client/models/agentic_card_enrollment_response200.rb +206 -0
  125. data/lib/cybersource_rest_client/models/agentic_card_enrollment_response202.rb +219 -0
  126. data/lib/cybersource_rest_client/models/agentic_confirm_transaction_events_request.rb +247 -0
  127. data/lib/cybersource_rest_client/models/agentic_confirm_transaction_events_response202.rb +195 -0
  128. data/lib/cybersource_rest_client/models/agentic_create_purchase_intent_request.rb +327 -0
  129. data/lib/cybersource_rest_client/models/agentic_create_purchase_intent_response200.rb +222 -0
  130. data/lib/cybersource_rest_client/models/agentic_pending_purchase_intent_response202.rb +241 -0
  131. data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_request.rb +247 -0
  132. data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_response200.rb +200 -0
  133. data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_response200_transaction_response_complete.rb +244 -0
  134. data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_response200_transaction_response_with_pending_events.rb +235 -0
  135. data/lib/cybersource_rest_client/models/agentic_update_purchase_intent_request.rb +315 -0
  136. data/lib/cybersource_rest_client/models/boardingv1registrations_organization_information_business_information.rb +36 -1
  137. data/lib/cybersource_rest_client/models/boardingv1registrations_organization_information_business_information_localized_names.rb +254 -0
  138. data/lib/cybersource_rest_client/models/ptsv2payments_travel_information_transit_airline_ancillary_information.rb +22 -5
  139. data/lib/cybersource_rest_client/models/ptsv2payments_travel_information_transit_airline_ancillary_information_service.rb +39 -5
  140. data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information.rb +246 -0
  141. data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit.rb +189 -0
  142. data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit_airline.rb +889 -0
  143. data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit_airline_ancillary_information.rb +259 -0
  144. data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit_airline_ancillary_information_service.rb +213 -0
  145. data/lib/cybersource_rest_client/models/refund_capture_request.rb +1 -1
  146. data/lib/cybersource_rest_client/models/refund_payment_request.rb +1 -1
  147. data/lib/cybersource_rest_client/models/upv1capturecontexts_data.rb +4 -2
  148. data/lib/cybersource_rest_client/models/upv1capturecontexts_data_merchant_defined_information.rb +3 -2
  149. data/lib/cybersource_rest_client/utilities/jwe_utility.rb +1 -1
  150. data/lib/cybersource_rest_client.rb +64 -0
  151. metadata +68 -3
  152. /data/lib/AuthenticationSDK/util/{JWEUtility.rb → AuthJWEUtility.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74de73ed4d3ea5cdc01fd04adad6790dd6f78c8e1c8735ac77ef876fda92b6b1
4
- data.tar.gz: '09b961a66b260468680e254cd21dac203d5218663d008d556d338a5e6d02a044'
3
+ metadata.gz: 2ea8b1d8350133ff8b367e96c70a70abe706b01c701f1ed9cab11518836ee9d8
4
+ data.tar.gz: 947eb679f3c7a6de9e2946b8c621ee7ee945039f43e8eac60b5999298c24ba55
5
5
  SHA512:
6
- metadata.gz: 28a45c4a395b75f6d76d5f498acbe50a3a7a7bbe80714286ac11514dfb7ae184126ba7deac4a8fc39d2e4a6d033401f318789f75f9dd69d98cbf8350d7346064
7
- data.tar.gz: 16e5b2d1a241c7fb43dae529c5250a367d7ea591418ed812073d3a552bbe345d956521f39ff54e6e0aa9cdc67508ce7c03e29ffd15458f0eb6780d529f8ff575
6
+ metadata.gz: 374d020327bff052168ee27f74ca1dcdfe6c20e306958d5adf647b234cbec5eb90db64b1c315a2a71309f2bc3e97223c47d54874d9d3a29b4cdc2923c9841021
7
+ data.tar.gz: 280efbdd1ce57c95e8f0f5f047284408f3ee9897cdf7f39c282cefac8a4e3ab0f8aff36b846f50a2c11f4a1f389424e18cb658ccad0da8883d2a87e97ffbf1c5
@@ -8,6 +8,7 @@ require_relative '../../core/ITokenGeneration.rb'
8
8
  require_relative '../../util/Constants.rb'
9
9
  require_relative '../../util/ExceptionHandler.rb'
10
10
  require_relative '../../util/Cache.rb'
11
+ require_relative '../../util/MLEUtility.rb'
11
12
  require_relative '../../authentication/payloadDigest/digest.rb'
12
13
  require_relative '../../logging/log_factory.rb'
13
14
 
@@ -16,19 +17,19 @@ public
16
17
  @log_obj
17
18
 
18
19
  #JWT Token-generated based on the Request type
19
- def getToken(merchantconfig_obj,gmtDatetime)
20
+ def getToken(merchantconfig_obj, gmtDatetime, isResponseMLEForApi)
20
21
  @log_obj = Log.new merchantconfig_obj.log_config, "JwtToken"
21
22
 
22
23
  jwtBody = ''
23
24
  request_type = merchantconfig_obj.requestType.upcase
24
25
 
25
- jwtBody=getJwtBody(request_type, gmtDatetime, merchantconfig_obj)
26
+ jwtBody = getJwtBody(request_type, gmtDatetime, merchantconfig_obj, isResponseMLEForApi)
26
27
  claimSet = JSON.parse(jwtBody)
27
28
 
28
29
  cache_value = Cache.new.fetchCachedP12Certificate(merchantconfig_obj)
29
30
  privateKey = cache_value.private_key
30
31
  jwt_cert_obj = cache_value.cert
31
- jwt_cert_in_der= Base64.strict_encode64(jwt_cert_obj.to_der)
32
+ jwt_cert_in_der = Base64.strict_encode64(jwt_cert_obj.to_der)
32
33
 
33
34
 
34
35
  # JWT token-Generates using RS256 algorithm only
@@ -51,18 +52,27 @@ public
51
52
  raise err
52
53
  end
53
54
 
54
- def getJwtBody(request_type, gmtDatetime, merchantconfig_obj)
55
+ def getJwtBody(request_type, gmtDatetime, merchantconfig_obj, isResponseMLEForApi=false)
56
+ jwtBody = "{\n "
57
+
55
58
  if request_type == Constants::POST_REQUEST_TYPE || request_type == Constants::PUT_REQUEST_TYPE || request_type == Constants::PATCH_REQUEST_TYPE
56
59
  payload = merchantconfig_obj.requestJsonData
57
60
 
58
61
  # Note: Digest is not passed for GET calls
59
62
  digest = DigestGeneration.new.generateDigest(payload)
60
- jwtBody = "{\n \"digest\":\"" + digest + "\", \"digestAlgorithm\":\"SHA-256\", \"iat\":" + Time.parse(gmtDatetime).to_i.to_s + "}"
63
+ jwtBody = jwtBody + "\"digest\":\"" + digest + "\", \"digestAlgorithm\":\"SHA-256\", \"iat\":" + Time.parse(gmtDatetime).to_i.to_s
61
64
  elsif request_type == Constants::GET_REQUEST_TYPE || request_type == Constants::DELETE_REQUEST_TYPE
62
- jwtBody = "{\n \"iat\":" + Time.parse(gmtDatetime).to_i.to_s + "\n} \n\n"
65
+ jwtBody = jwtBody + "\"iat\":" + Time.parse(gmtDatetime).to_i.to_s
63
66
  else
64
67
  raise StandardError.new(Constants::ERROR_PREFIX + Constants::INVALID_REQUEST_TYPE_METHOD)
65
68
  end
69
+
70
+ if isResponseMLEForApi
71
+ mleKid = MLEUtility.validate_and_auto_extract_response_mle_kid(merchantconfig_obj)
72
+ jwtBody = jwtBody + ", \"v-c-response-mle-kid\":\"" + mleKid + "\""
73
+ end
74
+
75
+ jwtBody = jwtBody + "\n}"
66
76
  end
67
77
  implements TokenInterface
68
- end
78
+ end
@@ -8,7 +8,7 @@ public
8
8
  # This function calls for the generation of Signature message depending on the authentication type.
9
9
  class Authorization
10
10
  @log_obj
11
- def getToken(merchantconfig_obj, gmtdatetime)
11
+ def getToken(merchantconfig_obj, gmtdatetime, isResponseMLEForApi)
12
12
  @log_obj = Log.new merchantconfig_obj.log_config, "Authorization"
13
13
 
14
14
  authenticationType = merchantconfig_obj.authenticationType.upcase
@@ -21,7 +21,7 @@ public
21
21
  if authenticationType == Constants::AUTH_TYPE_HTTP
22
22
  token = GenerateHttpSignature.new.getToken(merchantconfig_obj, gmtdatetime)
23
23
  elsif authenticationType == Constants::AUTH_TYPE_JWT
24
- token = GenerateJwtToken.new.getToken(merchantconfig_obj, gmtdatetime)
24
+ token = GenerateJwtToken.new.getToken(merchantconfig_obj, gmtdatetime, isResponseMLEForApi)
25
25
  elsif authenticationType == Constants::AUTH_TYPE_OAUTH
26
26
  token = GenerateOAuthToken.new.getToken(merchantconfig_obj, gmtdatetime)
27
27
  else
@@ -7,7 +7,7 @@ require_relative '../util/CertificateUtility.rb'
7
7
  public
8
8
  # This fuction has all the merchantConfig properties getters and setters methods
9
9
  class Merchantconfig
10
- def initialize(cybsPropertyObj)
10
+ def initialize(cybsPropertyObj, responseMlePrivateKeyValue = nil, responseMlePrivateKeyPasswordValue = nil)
11
11
  # Common Parameters
12
12
  @merchantId = cybsPropertyObj['merchantID']
13
13
  @runEnvironment = cybsPropertyObj['runEnvironment']
@@ -50,18 +50,97 @@ public
50
50
  @keepAliveTime = cybsPropertyObj['keepAliveTime'] || 118 # Default to 118 seconds as same as default of libcurl
51
51
  # Path to client JWE pem file directory
52
52
  @pemFileDirectory = cybsPropertyObj['pemFileDirectory']
53
- @mleKeyAlias = cybsPropertyObj['mleKeyAlias']
53
+
54
+ # Optional parameter. User can pass a custom requestMleKeyAlias to fetch from the certificate.
55
+ # Older flag "mleKeyAlias" is deprecated and will be used as alias/another name for requestMleKeyAlias.
56
+ if cybsPropertyObj.has_key?('mleKeyAlias')
57
+ @requestMleKeyAlias = cybsPropertyObj['mleKeyAlias']
58
+ elsif cybsPropertyObj.has_key?('requestMleKeyAlias')
59
+ @requestMleKeyAlias = cybsPropertyObj['requestMleKeyAlias']
60
+ end
61
+
62
+ # Deprecated flag to enable MLE for request. This flag is now known as "enableRequestMLEForOptionalApisGlobally"
54
63
  @useMLEGlobally = cybsPropertyObj['useMLEGlobally']
64
+
65
+ # Flag to enable MLE (Message Level Encryption) for request body to all APIs in SDK which have optional support for MLE.
66
+ # This means the API can send both non-encrypted and encrypted requests.
67
+ # Older flag "useMLEGlobally" is deprecated and will be used as alias/another name for enableRequestMLEForOptionalApisGlobally.
55
68
  @enableRequestMLEForOptionalApisGlobally = !!(cybsPropertyObj['enableRequestMLEForOptionalApisGlobally'] || cybsPropertyObj['useMLEGlobally'])
69
+ # Flag to disable MLE (Message Level Encryption) for request body to APIs in SDK which have mandatory MLE requirement when sending calls.
56
70
  @disableRequestMLEForMandatoryApisGlobally = cybsPropertyObj['disableRequestMLEForMandatoryApisGlobally']
57
71
 
58
-
72
+ # Parameter to pass the request MLE public certificate path.
59
73
  if !cybsPropertyObj['mleForRequestPublicCertPath'].nil? && !cybsPropertyObj['mleForRequestPublicCertPath'].to_s.strip.empty?
60
74
  @mleForRequestPublicCertPath = cybsPropertyObj['mleForRequestPublicCertPath'].to_s.strip
61
75
  end
62
76
 
77
+ # Map to control MLE (Message Level Encryption) settings for individual API functions. This overrides global MLE configuration for specific APIs.
78
+ # The key is the function name of the API in the SDK, and the value is a String in the format "requestMLE::responseMLE" separated by "::",
79
+ # where the first boolean value controls MLE for the request and the second boolean value controls MLE for the response.
80
+ # Use "true" to enable or "false" to disable MLE for that specific component.
81
+
82
+ # Valid Examples:
83
+ # mapToControlMLEonAPI.put("apiFunctionName1", "true::true") - enables MLE for both request and response for apiFunctionName1
84
+ # mapToControlMLEonAPI.put("apiFunctionName2", "false::false") - disables MLE for both request and response for apiFunctionName2
85
+ # mapToControlMLEonAPI.put("apiFunctionName3", "true::false") - enables request MLE only, disables response MLE for apiFunctionName3
86
+ # mapToControlMLEonAPI.put("apiFunctionName4", "false::true") - disables request MLE, enables response MLE only for apiFunctionName4
87
+ # mapToControlMLEonAPI.put("apiFunctionName5", "false") - disables request MLE only. Since the "::" separator is not included, mleForResponse will use the default value set by the global flag
88
+ # mapToControlMLEonAPI.put("apiFunctionName6", "true") - enables request MLE only. Since the "::" separator is not included, mleForResponse will use the default value set by the global flag
89
+ # mapToControlMLEonAPI.put("apiFunctionName7", "::true") - enables response MLE only. Because the value before "::" is missing, the SDK will use the default request MLE value from the global flag
90
+ # mapToControlMLEonAPI.put("apiFunctionName8", "true::") - enables request MLE only. Since the value after the "::" separator is missing, mleForResponse will use the default value
91
+
92
+ # Invalid Examples (will be ignored or cause errors):
93
+ # mapToControlMLEonAPI.put("apiFunctionName9", "::") - both values empty, will use global defaults
94
+ # mapToControlMLEonAPI.put("apiFunctionName10", "invalid::true") - invalid first value, may cause parsing error
95
+ # mapToControlMLEonAPI.put("apiFunctionName11", "true::invalid") - invalid second value, may cause parsing error
96
+ # mapToControlMLEonAPI.put("apiFunctionName12", "true::true::false") - multiple separators not allowed
97
+ # mapToControlMLEonAPI.put("apiFunctionName13", "") - empty string not allowed
63
98
  @mapToControlMLEonAPI = cybsPropertyObj['mapToControlMLEonAPI']
64
- validateMerchantDetails
99
+
100
+ # Initialize internal maps before validation
101
+ # Both fields used for internal purpose only not exposed for merchants to set
102
+ @internalMapToControlRequestMLEonAPI = {}
103
+ @internalMapToControlResponseMLEonAPI = {}
104
+
105
+ # Set up MLE configuration first since validation depends on it
106
+ if @mapToControlMLEonAPI
107
+ begin
108
+ @mapToControlMLEonAPI = convertBooleanToStringMapType(@mapToControlMLEonAPI)
109
+ setMapToControlMLEOnAPI(@mapToControlMLEonAPI)
110
+ rescue => err
111
+ error = StandardError.new(Constants::WARNING_PREFIX + "Unable to initialise MLE control Map from config: #{err.message}")
112
+ raise error
113
+ end
114
+ end
115
+
116
+ if responseMlePrivateKeyPasswordValue.nil?
117
+ responseMlePrivateKeyPasswordValue = cybsPropertyObj['responseMlePrivateKeyPassword']
118
+ end
119
+
120
+ responseMlePrivateKeyPassword = responseMlePrivateKeyPasswordValue
121
+
122
+ if !responseMlePrivateKeyValue.nil? && !cybsPropertyObj['responseMlePrivateKey'].nil?
123
+ raise StandardError.new(Constants::ERROR_PREFIX + "The value for `responseMlePrivateKey` is provided in both the configuration object and the constructor for MerchantConfig. Please provide only one of them for response mle private key.")
124
+ end
125
+
126
+ if responseMlePrivateKeyValue.nil?
127
+ responseMlePrivateKeyValue = cybsPropertyObj['responseMlePrivateKey']
128
+ end
129
+
130
+ responseMlePrivateKeyValue = CertificateUtility.convert_key_to_JWK(responseMlePrivateKeyValue, responseMlePrivateKeyPassword)
131
+
132
+ @responseMlePrivateKey = responseMlePrivateKeyValue
133
+
134
+ @enableResponseMleGlobally = false
135
+ if !cybsPropertyObj['enableResponseMleGlobally'].nil?
136
+ @enableResponseMleGlobally = cybsPropertyObj['enableResponseMleGlobally']
137
+ end
138
+
139
+ @responseMleKID = cybsPropertyObj['responseMleKID']
140
+ @responseMlePrivateKeyFilePath = cybsPropertyObj['responseMlePrivateKeyFilePath']
141
+ @responseMlePrivateKeyFilePassword = cybsPropertyObj['responseMlePrivateKeyFilePassword']
142
+
143
+ validateMerchantDetails()
65
144
  validateMLEConfiguration(cybsPropertyObj)
66
145
  @p12KeyFilePath = File.join(@keysDirectory, @keyFilename + ".p12")
67
146
  logAllProperties(cybsPropertyObj)
@@ -252,7 +331,6 @@ public
252
331
  end
253
332
 
254
333
  def validateMLEConfiguration(cybsPropertyObj)
255
-
256
334
  if !@useMLEGlobally.nil? && !cybsPropertyObj['enableRequestMLEForOptionalApisGlobally'].nil?
257
335
  if @useMLEGlobally != cybsPropertyObj['enableRequestMLEForOptionalApisGlobally']
258
336
  raise StandardError.new(Constants::ERROR_PREFIX + "useMLEGlobally and enableRequestMLEForOptionalApisGlobally must have the same value if both are set")
@@ -275,21 +353,19 @@ public
275
353
  raise err
276
354
  end
277
355
 
278
- unless @mapToControlMLEonAPI.nil?
279
- unless @mapToControlMLEonAPI.is_a?(Hash) && @mapToControlMLEonAPI.keys.all? {|k| k.is_a?(String)} && @mapToControlMLEonAPI.values.all? { |v| [true, false].include?(v) }
280
- err = StandardError.new(Constants::ERROR_PREFIX + "mapToControlMLEonAPI must be a map with boolean values")
281
- @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
282
- raise err
283
- end
284
- end
356
+ # unless @mapToControlMLEonAPI.is_a?(Hash) && @mapToControlMLEonAPI.keys.all? {|k| k.is_a?(String)} && @mapToControlMLEonAPI.values.all? { |v| [true, false].include?(v) }
357
+ # err = StandardError.new(Constants::ERROR_PREFIX + "mapToControlMLEonAPI must be a map with boolean values")
358
+ # @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
359
+ # raise err
360
+ # end
285
361
 
286
- !@mleKeyAlias.nil? && unless @mleKeyAlias.instance_of? String
287
- (err = StandardError.new(Constants::ERROR_PREFIX + "mleKeyAlias must be a string"))
288
- @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
289
- raise err
290
- end
291
- if @mleKeyAlias.to_s.empty?
292
- @mleKeyAlias = Constants::DEFAULT_ALIAS_FOR_MLE_CERT
362
+ !@requestMleKeyAlias.nil? && unless @requestMleKeyAlias.instance_of? String
363
+ err = StandardError.new(Constants::ERROR_PREFIX + "requestMleKeyAlias must be a string")
364
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
365
+ raise err
366
+ end
367
+ if @requestMleKeyAlias.to_s.empty?
368
+ @requestMleKeyAlias = Constants::DEFAULT_ALIAS_FOR_MLE_CERT
293
369
  end
294
370
 
295
371
  if @mleForRequestPublicCertPath && !@mleForRequestPublicCertPath.to_s.strip.empty?
@@ -302,9 +378,9 @@ public
302
378
  end
303
379
 
304
380
  request_mle_configured = @enableRequestMLEForOptionalApisGlobally
305
- if !@mapToControlMLEonAPI.nil? && !@mapToControlMLEonAPI.empty?
306
- @mapToControlMLEonAPI.each do |_, value|
307
- unless [true, false].include?(value) && value
381
+ if !@internalMapToControlRequestMLEonAPI.nil? && !@internalMapToControlRequestMLEonAPI.empty?
382
+ @internalMapToControlRequestMLEonAPI.each do |_, value|
383
+ if value
308
384
  request_mle_configured = true
309
385
  break
310
386
  end
@@ -316,10 +392,179 @@ public
316
392
  @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
317
393
  raise err
318
394
  end
395
+
396
+ is_response_mle_configured = @enableResponseMleGlobally
397
+
398
+ if !@internalMapToControlResponseMLEonAPI.nil? && !@internalMapToControlResponseMLEonAPI.empty?
399
+ @internalMapToControlResponseMLEonAPI.values.each do |value|
400
+ if value == true
401
+ is_response_mle_configured = true
402
+ break
403
+ end
404
+ end
405
+ end
406
+
407
+ if is_response_mle_configured
408
+ # Validate for Auth type- Currently responseMLE feature will be enabled for JWT auth type only
409
+ if !Constants::AUTH_TYPE_JWT.eql?(@authenticationType.upcase)
410
+ err = StandardError.new(Constants::ERROR_PREFIX + "Response MLE can only be used with JWT authentication type")
411
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
412
+ raise err
413
+ end
414
+
415
+ # Check if either private key object or private key file path is provided
416
+ if @responseMlePrivateKey.nil? || @responseMlePrivateKey.to_s.strip.empty?
417
+ if @responseMlePrivateKeyFilePath.nil? || @responseMlePrivateKeyFilePath.to_s.strip.empty?
418
+ err = StandardError.new(Constants::ERROR_PREFIX + "Response MLE is enabled but no private key provided. Either set responseMlePrivateKey object or provide responseMlePrivateKeyFilePath.")
419
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
420
+ raise err
421
+ end
422
+ end
423
+
424
+ # Check that both private key object or private key file path should not be provided
425
+ if !@responseMlePrivateKey.nil? && !@responseMlePrivateKey.to_s.strip.empty? && !@responseMlePrivateKeyFilePath.nil? && !@responseMlePrivateKeyFilePath.to_s.strip.empty?
426
+ err = StandardError.new(Constants::ERROR_PREFIX + "Both responseMlePrivateKey object and responseMlePrivateKeyFilePath are provided. Please provide only one of them for response mle private key.")
427
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
428
+ raise err
429
+ end
430
+
431
+ isP12 = false
432
+ # If private key file path is provided, validate the file exists
433
+ if !@responseMlePrivateKeyFilePath.nil? && !@responseMlePrivateKeyFilePath.to_s.strip.empty?
434
+ begin
435
+ CertificateUtility.validatePathAndFile(@responseMlePrivateKeyFilePath, "responseMlePrivateKeyFilePath", @log_config)
436
+ ext = File.extname(@responseMlePrivateKeyFilePath).downcase
437
+ if ext == '.p12' || ext == '.pfx'
438
+ isP12 = true
439
+ end
440
+ rescue => err
441
+ error = StandardError.new(Constants::ERROR_PREFIX + "Invalid responseMlePrivateKeyFilePath : #{err.message}")
442
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception error)
443
+ raise error
444
+ end
445
+ end
446
+
447
+ # Validate responseMleKID is provided when response MLE is enabled
448
+ if !isP12 && (@responseMleKID.nil? || @responseMleKID.to_s.strip.empty?)
449
+ err = StandardError.new(Constants::ERROR_PREFIX + "responseMleKID is required when response MLE is enabled for non-P12/PFX files.")
450
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
451
+ raise err
452
+ end
453
+ end
454
+ end
455
+
456
+ def setMapToControlMLEOnAPI(inputMap)
457
+ # validate the map value format
458
+ validateMapToControlMLEonAPIValues(inputMap) if inputMap
459
+
460
+ # @mapToControlMLEonAPI = inputMap
461
+
462
+ if inputMap
463
+ internalRequest = {}
464
+ internalResponse = {}
465
+
466
+ inputMap.each do |apiName, rawValue|
467
+ value = rawValue.to_s
468
+
469
+ if value.include?("::")
470
+ # Format: "requestMLE::responseMLE"
471
+ requestMLE, responseMLE = value.split("::", 2)
472
+
473
+ # Set request MLE value when present
474
+ unless requestMLE.nil? || requestMLE.empty?
475
+ internalRequest[apiName] = requestMLE.to_s.strip.casecmp?("true")
476
+ end
477
+
478
+ # Set response MLE value when present
479
+ unless responseMLE.nil? || responseMLE.empty?
480
+ internalResponse[apiName] = responseMLE.to_s.strip.casecmp?("true")
481
+ end
482
+ else
483
+ # Format: "true" or "false" - applies to request MLE only
484
+ internalRequest[apiName] = value.to_s.strip.casecmp?("true")
485
+ end
486
+ end
487
+
488
+ @internalMapToControlRequestMLEonAPI = internalRequest
489
+ @internalMapToControlResponseMLEonAPI = internalResponse
490
+ end
491
+ end
492
+
493
+ # Validates the map values for MLE control API configuration.
494
+ # Allowed formats: "true::true", "false::false", "::true", "true::", "::false", "false::", "true", "false"
495
+ def validateMapToControlMLEonAPIValues(inputMap)
496
+ inputMap.each do |key, value|
497
+ if value.nil? || value == ""
498
+ err = StandardError.new(Constants::ERROR_PREFIX + "Invalid MLE control map value for key '#{key}'. Value cannot be null or empty.")
499
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
500
+ raise err
501
+ end
502
+
503
+ str = value.to_s
504
+ if str.include?("::")
505
+ parts = str.split("::", -1)
506
+
507
+ unless parts.length == 2
508
+ err = StandardError.new(Constants::ERROR_PREFIX + "Invalid MLE control map value format for key '#{key}'. Expected format: true/false for 'requestMLE::responseMLE' but got: '#{str}'")
509
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
510
+ raise err
511
+ end
512
+
513
+ requestMLE, responseMLE = parts
514
+
515
+ if !requestMLE.empty? && !isValidBooleanString?(requestMLE)
516
+ err = StandardError.new(Constants::ERROR_PREFIX + "Invalid request MLE value for key '#{key}'. Expected 'true', 'false', or empty but got: '#{requestMLE}'")
517
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
518
+ raise err
519
+ end
520
+
521
+ if !responseMLE.empty? && !isValidBooleanString?(responseMLE)
522
+ err = StandardError.new(Constants::ERROR_PREFIX + "Invalid response MLE value for key '#{key}'. Expected 'true', 'false', or empty but got: '#{responseMLE}'")
523
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
524
+ raise err
525
+ end
526
+ else
527
+ unless isValidBooleanString?(str)
528
+ err = StandardError.new(Constants::ERROR_PREFIX + "Invalid MLE control map value for key '#{key}'. Expected 'true' or 'false' for requestMLE but got: '#{str}'")
529
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
530
+ raise err
531
+ end
532
+ end
533
+ end
534
+ end
535
+
536
+ def isValidBooleanString?(s)
537
+ s.casecmp?("true") || s.casecmp?("false")
538
+ end
539
+
540
+ def convertBooleanToStringMapType(inputMap)
541
+ if inputMap.nil? || inputMap.empty?
542
+ raise StandardError.new(Constants::ERROR_PREFIX + "Unsupported null value to mapToControlMLEonAPI in merchantConfig. Expected Map<String, String> which corresponds to <'apiFunctionName','flagForRequestMLE::flagForResponseMLE'> as dataType for field.")
543
+ end
544
+
545
+ unless inputMap.is_a?(Hash)
546
+ raise TypeError.new(Constants::ERROR_PREFIX + "Unsupported datatype for field mapToControlMLEonAPI. Expected Hash<String, String> which corresponds to <'apiFunctionName','flagForRequestMLE::flagForResponseMLE'> as dataType for field but got: #{inputMap.class}")
547
+ end
548
+
549
+ keys_all_strings = inputMap.keys.all? { |k| k.is_a?(String) }
550
+ values_all_strings = inputMap.values.all? { |v| v.is_a?(String) }
551
+ values_all_bools = inputMap.values.all? { |v| v.is_a?(TrueClass) || v.is_a?(FalseClass) }
552
+
553
+ if keys_all_strings && values_all_strings
554
+ # Already Hash<String, String>
555
+ inputMap
556
+ elsif keys_all_strings && values_all_bools
557
+ # Convert Hash<String, Boolean> -> Hash<String, String>
558
+ inputMap.transform_values { |v| v.to_s }
559
+ else
560
+ err = StandardError.new("Unsupported map type combination for mapToControlMLEonAPI in merchantConfig. Expected Hash<String, String> which corresponds to <'apiFunctionName','flagForRequestMLE::flagForResponseMLE'> as dataType for field.")
561
+ @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
562
+ raise err
563
+ end
319
564
  end
320
565
 
321
566
  def logAllProperties(merchantPropertyObj)
322
- propertyObj = Marshal.load(Marshal.dump(merchantPropertyObj))
567
+ propertyObj = Marshal.load(Marshal.dump(merchantPropertyObj))
323
568
  merchantConfig = ''
324
569
  hiddenProperties = (Constants::HIDDEN_MERCHANT_PROPERTIES).split(',')
325
570
  hiddenPropArray = Array.new
@@ -408,6 +653,15 @@ public
408
653
  attr_accessor :mleForRequestPublicCertPath
409
654
  attr_accessor :mapToControlMLEonAPI
410
655
  attr_accessor :mleKeyAlias
656
+ attr_accessor :requestMleKeyAlias
411
657
  attr_accessor :p12KeyFilePath
412
658
  attr_reader :runEnvironment
659
+ attr_accessor :enableResponseMleGlobally
660
+ attr_accessor :responseMleKID
661
+ attr_accessor :responseMlePrivateKeyFilePath
662
+ attr_accessor :responseMlePrivateKeyFilePassword
663
+ attr_accessor :responseMlePrivateKey
664
+ attr_accessor :responseMlePrivateKeyPassword
665
+ attr_accessor :internalMapToControlRequestMLEonAPI
666
+ attr_accessor :internalMapToControlResponseMLEonAPI
413
667
  end
@@ -3,7 +3,9 @@ require 'base64'
3
3
  require 'active_support'
4
4
  require 'thread'
5
5
  require_relative 'CacheValue'
6
+ require_relative 'CachedMLEKId'
6
7
  require_relative 'CertificateUtility'
8
+ require_relative 'Utility'
7
9
  require_relative '../util/Constants.rb'
8
10
  require_relative '../logging/log_factory.rb'
9
11
  require_relative '../logging/log_configuration.rb'
@@ -41,9 +43,37 @@ public
41
43
  logger = @@logger.logger
42
44
  fileModifiedTime = File.mtime(certificateFilePath)
43
45
 
46
+ if cacheKey.end_with?(Constants::MLE_CACHE_KEY_IDENTIFIER_FOR_RESPONSE_PRIVATE_KEY)
47
+ password = merchantConfig.responseMlePrivateKeyFilePassword
48
+ mlePrivateKey = nil
49
+
50
+ begin
51
+ fileExtension = File.extname(certificateFilePath).delete_prefix('.').downcase
52
+
53
+ if fileExtension == 'p12' || fileExtension == 'pfx'
54
+ mlePrivateKey = CertificateUtility.read_private_key_from_p12(certificateFilePath, password)
55
+ elsif fileExtension == 'pem' || fileExtension == 'key' || fileExtension == 'p8'
56
+ mlePrivateKey = CertificateUtility.load_private_key_from_pem_file(certificateFilePath, password)
57
+ else
58
+ err = StandardError.new(Constants::ERROR_PREFIX + "Unsupported Response MLE Private Key file format: `" + fileExtension + "`. Supported formats are: .p12, .pfx, .pem, .key, .p8")
59
+ logger.error(ExceptionHandler.new.new_api_exception err)
60
+ raise err
61
+ end
62
+
63
+ cacheValue = CacheValue.new(mlePrivateKey, nil, fileModifiedTime)
64
+
65
+ @@cache_obj.write(cacheKey, cacheValue)
66
+ rescue StandardError => e
67
+ err = StandardError.new(Constants::ERROR_PREFIX + "Error loading MLE response private key from: " + certificateFilePath + ". Error: " + e.message)
68
+ logger.error(ExceptionHandler.new.new_api_exception err)
69
+ raise err
70
+ end
71
+ return
72
+ end
73
+
44
74
  if (cacheKey.end_with?("_JWT"))
45
- privateKey, certificateList = CertificateUtility.getCertificateCollectionAndPrivateKeyFromP12(certificateFilePath, merchantConfig)
46
- jwtCertificate = CertificateUtility.getCertificateBasedOnKeyAlias(certificateList, merchantConfig.keyAlias)
75
+ privateKey, certificateList = Utility.getCertificateCollectionAndPrivateKeyFromP12(certificateFilePath, merchantConfig.keyPass)
76
+ jwtCertificate = Utility.getCertificateBasedOnKeyAlias(certificateList, merchantConfig.keyAlias)
47
77
 
48
78
  cacheValue = CacheValue.new(privateKey, jwtCertificate, fileModifiedTime)
49
79
 
@@ -53,10 +83,10 @@ public
53
83
 
54
84
  if (cacheKey.end_with?(Constants::MLE_CACHE_IDENTIFIER_FOR_CONFIG_CERT))
55
85
  certificateList = CertificateUtility.getCertificatesFromPemFile(certificateFilePath)
56
- mleCertificate = CertificateUtility.getCertificateBasedOnKeyAlias(certificateList, merchantConfig.mleKeyAlias)
86
+ mleCertificate = Utility.getCertificateBasedOnKeyAlias(certificateList, merchantConfig.requestMleKeyAlias)
57
87
  if (!mleCertificate)
58
88
  fileName = File.basename(certificateFilePath)
59
- logger.warn("No certificate found for the specified mle_key_alias '#{merchantConfig.mleKeyAlias}'. Using the first certificate from file #{fileName} as the MLE request certificate.")
89
+ logger.warn("No certificate found for the specified mle_key_alias '#{merchantConfig.requestMleKeyAlias}'. Using the first certificate from file #{fileName} as the MLE request certificate.")
60
90
  mleCertificate = certificateList[0]
61
91
  end
62
92
 
@@ -67,12 +97,12 @@ public
67
97
  end
68
98
 
69
99
  if (cacheKey.end_with?(Constants::MLE_CACHE_IDENTIFIER_FOR_P12_CERT))
70
- privateKey, certificateList = CertificateUtility.getCertificateCollectionAndPrivateKeyFromP12(certificateFilePath, merchantConfig)
71
- mleCertificate = CertificateUtility.getCertificateBasedOnKeyAlias(certificateList, merchantConfig.mleKeyAlias)
100
+ privateKey, certificateList = Utility.getCertificateCollectionAndPrivateKeyFromP12(certificateFilePath, merchantConfig.keyPass)
101
+ mleCertificate = Utility.getCertificateBasedOnKeyAlias(certificateList, merchantConfig.requestMleKeyAlias)
72
102
  if (!mleCertificate)
73
103
  fileName = File.basename(certificateFilePath)
74
- logger.error("No certificate found for the specified mle_key_alias '#{merchantConfig.mleKeyAlias}' in file #{fileName}.")
75
- raise ArgumentError, "No certificate found for the specified mle_key_alias '#{merchantConfig.mleKeyAlias}' in file #{fileName}."
104
+ logger.error("No certificate found for the specified mle_key_alias '#{merchantConfig.requestMleKeyAlias}' in file #{fileName}.")
105
+ raise ArgumentError, "No certificate found for the specified mle_key_alias '#{merchantConfig.requestMleKeyAlias}' in file #{fileName}."
76
106
  end
77
107
 
78
108
  cacheValue = CacheValue.new(privateKey, mleCertificate, fileModifiedTime)
@@ -129,6 +159,112 @@ public
129
159
  cachedCertificateInfo ? cachedCertificateInfo.cert : nil
130
160
  end
131
161
 
162
+ def getMLEResponsePrivateKeyFromFilePath(merchantConfig)
163
+ merchantId = merchantConfig.merchantId
164
+ keyIdentifier = Constants::MLE_CACHE_KEY_IDENTIFIER_FOR_RESPONSE_PRIVATE_KEY
165
+ cacheIdentifier = "#{merchantId}_#{keyIdentifier}"
166
+ mleResponsePrivateKeyFilePath = merchantConfig.responseMlePrivateKeyFilePath
167
+ cachedCertificateInfo = nil
168
+
169
+ @@mutex.synchronize do
170
+ cachedCertificateInfo = @@cache_obj.read(cacheIdentifier)
171
+
172
+ if cachedCertificateInfo.nil? || cachedCertificateInfo.file_modified_time != File.mtime(mleResponsePrivateKeyFilePath)
173
+ setupCache(cacheIdentifier, mleResponsePrivateKeyFilePath, merchantConfig)
174
+ cachedCertificateInfo = @@cache_obj.read(cacheIdentifier)
175
+ end
176
+ end
177
+
178
+ cachedCertificateInfo ? cachedCertificateInfo.private_key : nil
179
+ end
180
+
181
+ def get_mle_kid_data_from_cache(merchant_config)
182
+ cache_key = merchant_config.responseMlePrivateKeyFilePath + Constants::RESPONSE_MLE_P12_PFX_CACHE_IDENTIFIER
183
+ file_path = merchant_config.responseMlePrivateKeyFilePath
184
+
185
+ @@mutex.synchronize do
186
+ if !@@cache_obj.exist?(cache_key)
187
+ setup_mle_kid_cache(merchant_config)
188
+ else
189
+ cached_mle_kid = @@cache_obj.read(cache_key)
190
+ file_modified_time = File.mtime(file_path).to_i
191
+
192
+ if cached_mle_kid.nil? || cached_mle_kid.last_modified_timestamp != file_modified_time
193
+ if !Cache.class_variable_defined?(:@@logger) || @@logger.nil?
194
+ @@logger = Log.new merchant_config.log_config, "Cache"
195
+ end
196
+ logger = @@logger.logger
197
+ logger.info("MLE KID cache outdated or file modified. Refreshing cache for: #{file_path}")
198
+ setup_mle_kid_cache(merchant_config)
199
+ end
200
+ end
201
+
202
+ return @@cache_obj.read(cache_key)
203
+ end
204
+ end
205
+
206
+ private
207
+
208
+ def setup_mle_kid_cache(merchant_config)
209
+ file_path = nil
210
+ cache_key = nil
211
+
212
+ begin
213
+ if !Cache.class_variable_defined?(:@@logger) || @@logger.nil?
214
+ @@logger = Log.new merchant_config.log_config, "Cache"
215
+ end
216
+ logger = @@logger.logger
217
+
218
+ file_path = merchant_config.responseMlePrivateKeyFilePath
219
+ cache_key = merchant_config.responseMlePrivateKeyFilePath + Constants::RESPONSE_MLE_P12_PFX_CACHE_IDENTIFIER
220
+
221
+ # Get certificate from P12 file
222
+ _, certificate_list = Utility.getCertificateCollectionAndPrivateKeyFromP12(
223
+ file_path,
224
+ merchant_config.responseMlePrivateKeyFilePassword
225
+ )
226
+
227
+ merchant_cert = Utility.getCertificateBasedOnKeyAlias(certificate_list, merchant_config.merchantId)
228
+
229
+ if merchant_cert.nil?
230
+ raise StandardError.new("No certificate found for Response MLE Private Key file with merchant ID alias #{merchant_config.merchantId}")
231
+ end
232
+
233
+ # Check if this is a CyberSource-generated P12 file
234
+ is_cybersource_p12 = Utility.isP12GeneratedByCyberSource(
235
+ file_path,
236
+ merchant_config.responseMlePrivateKeyFilePassword,
237
+ logger
238
+ )
239
+
240
+ cached_mle_kid = CachedMLEKId.new
241
+ cached_mle_kid.last_modified_timestamp = File.mtime(file_path).to_i
242
+
243
+ if is_cybersource_p12
244
+ begin
245
+ mle_kid = MLEUtility.extract_serial_number_from_certificate(merchant_cert)
246
+ cached_mle_kid.kid = mle_kid
247
+ rescue ArgumentError => e
248
+ raise StandardError.new("Failed to extract serial number from certificate for Response MLE: #{e.message}")
249
+ end
250
+ end
251
+
252
+ @@cache_obj.write(cache_key, cached_mle_kid)
253
+
254
+ rescue StandardError => e
255
+ logger.error("Failed to load MLE KID from Response MLE Private Key file: #{file_path}. Error: #{e.message}")
256
+
257
+ if !cache_key.nil?
258
+ failure_marker = CachedMLEKId.new
259
+ failure_marker.kid = nil
260
+ failure_marker.last_modified_timestamp = 0
261
+ @@cache_obj.write(cache_key, failure_marker)
262
+ end
263
+ end
264
+ end
265
+
266
+ public
267
+
132
268
  # <b>DEPRECATED:</b> This method has been marked as Deprecated and will be removed in coming releases.
133
269
  def fetchPEMFileForNetworkTokenization(filePath)
134
270
  warn("[DEPRECATED] 'fetchPEMFileForNetworkTokenization' method is deprecated and will be removed in coming releases.")
@@ -0,0 +1,17 @@
1
+ # Cache value object to store MLE KID data
2
+ class CachedMLEKId
3
+ attr_accessor :kid, :last_modified_timestamp
4
+
5
+ def initialize(kid = nil, last_modified_timestamp = nil)
6
+ @kid = kid
7
+ @last_modified_timestamp = last_modified_timestamp
8
+ end
9
+
10
+ def to_s
11
+ "CachedMLEKId(kid: #{@kid ? 'present' : 'nil'}, last_modified_timestamp: #{@last_modified_timestamp})"
12
+ end
13
+
14
+ def empty?
15
+ @kid.nil? && @last_modified_timestamp.nil?
16
+ end
17
+ end