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.
- checksums.yaml +4 -4
- data/lib/AuthenticationSDK/authentication/jwt/JwtToken.rb +17 -7
- data/lib/AuthenticationSDK/core/Authorization.rb +2 -2
- data/lib/AuthenticationSDK/core/MerchantConfig.rb +277 -23
- data/lib/AuthenticationSDK/util/Cache.rb +144 -8
- data/lib/AuthenticationSDK/util/CachedMLEKId.rb +17 -0
- data/lib/AuthenticationSDK/util/CertificateUtility.rb +109 -29
- data/lib/AuthenticationSDK/util/Constants.rb +4 -0
- data/lib/AuthenticationSDK/util/MLEUtility.rb +167 -11
- data/lib/AuthenticationSDK/util/Utility.rb +37 -1
- data/lib/cybersource_rest_client/api/bank_account_validation_api.rb +6 -2
- data/lib/cybersource_rest_client/api/batches_api.rb +24 -8
- data/lib/cybersource_rest_client/api/billing_agreements_api.rb +18 -6
- data/lib/cybersource_rest_client/api/bin_lookup_api.rb +6 -2
- data/lib/cybersource_rest_client/api/capture_api.rb +6 -2
- data/lib/cybersource_rest_client/api/chargeback_details_api.rb +6 -2
- data/lib/cybersource_rest_client/api/chargeback_summaries_api.rb +6 -2
- data/lib/cybersource_rest_client/api/conversion_details_api.rb +6 -2
- data/lib/cybersource_rest_client/api/create_new_webhooks_api.rb +18 -6
- data/lib/cybersource_rest_client/api/credit_api.rb +6 -2
- data/lib/cybersource_rest_client/api/customer_api.rb +24 -8
- data/lib/cybersource_rest_client/api/customer_payment_instrument_api.rb +30 -10
- data/lib/cybersource_rest_client/api/customer_shipping_address_api.rb +30 -10
- data/lib/cybersource_rest_client/api/decision_manager_api.rb +30 -10
- data/lib/cybersource_rest_client/api/device_de_association_api.rb +12 -4
- data/lib/cybersource_rest_client/api/device_search_api.rb +12 -4
- data/lib/cybersource_rest_client/api/download_dtd_api.rb +6 -2
- data/lib/cybersource_rest_client/api/download_xsd_api.rb +6 -2
- data/lib/cybersource_rest_client/api/emv_tag_details_api.rb +12 -4
- data/lib/cybersource_rest_client/api/enrollment_api.rb +104 -0
- data/lib/cybersource_rest_client/api/flex_api_api.rb +6 -2
- data/lib/cybersource_rest_client/api/instructions_api.rb +452 -0
- data/lib/cybersource_rest_client/api/instrument_identifier_api.rb +36 -12
- data/lib/cybersource_rest_client/api/interchange_clearing_level_details_api.rb +6 -2
- data/lib/cybersource_rest_client/api/invoice_settings_api.rb +12 -4
- data/lib/cybersource_rest_client/api/invoices_api.rb +42 -14
- data/lib/cybersource_rest_client/api/manage_webhooks_api.rb +42 -14
- data/lib/cybersource_rest_client/api/merchant_boarding_api.rb +12 -4
- data/lib/cybersource_rest_client/api/merchant_defined_fields_api.rb +24 -8
- data/lib/cybersource_rest_client/api/microform_integration_api.rb +6 -2
- data/lib/cybersource_rest_client/api/net_fundings_api.rb +6 -2
- data/lib/cybersource_rest_client/api/notification_of_changes_api.rb +6 -2
- data/lib/cybersource_rest_client/api/offers_api.rb +12 -4
- data/lib/cybersource_rest_client/api/orders_api.rb +12 -4
- data/lib/cybersource_rest_client/api/payer_authentication_api.rb +18 -6
- data/lib/cybersource_rest_client/api/payment_batch_summaries_api.rb +6 -2
- data/lib/cybersource_rest_client/api/payment_instrument_api.rb +24 -8
- data/lib/cybersource_rest_client/api/payment_links_api.rb +24 -8
- data/lib/cybersource_rest_client/api/payment_tokens_api.rb +6 -2
- data/lib/cybersource_rest_client/api/payments_api.rb +36 -12
- data/lib/cybersource_rest_client/api/payouts_api.rb +6 -2
- data/lib/cybersource_rest_client/api/plans_api.rb +48 -16
- data/lib/cybersource_rest_client/api/purchase_and_refund_details_api.rb +6 -2
- data/lib/cybersource_rest_client/api/push_funds_api.rb +6 -2
- data/lib/cybersource_rest_client/api/refund_api.rb +12 -4
- data/lib/cybersource_rest_client/api/report_definitions_api.rb +12 -4
- data/lib/cybersource_rest_client/api/report_downloads_api.rb +6 -2
- data/lib/cybersource_rest_client/api/report_subscriptions_api.rb +30 -10
- data/lib/cybersource_rest_client/api/reports_api.rb +18 -6
- data/lib/cybersource_rest_client/api/retrieval_details_api.rb +6 -2
- data/lib/cybersource_rest_client/api/retrieval_summaries_api.rb +6 -2
- data/lib/cybersource_rest_client/api/reversal_api.rb +12 -4
- data/lib/cybersource_rest_client/api/search_transactions_api.rb +12 -4
- data/lib/cybersource_rest_client/api/secure_file_share_api.rb +12 -4
- data/lib/cybersource_rest_client/api/subscriptions_api.rb +48 -16
- data/lib/cybersource_rest_client/api/subscriptions_follow_ons_api.rb +12 -4
- data/lib/cybersource_rest_client/api/taxes_api.rb +12 -4
- data/lib/cybersource_rest_client/api/token_api.rb +12 -4
- data/lib/cybersource_rest_client/api/tokenize_api.rb +6 -2
- data/lib/cybersource_rest_client/api/tokenized_card_api.rb +24 -8
- data/lib/cybersource_rest_client/api/transaction_batches_api.rb +24 -8
- data/lib/cybersource_rest_client/api/transaction_details_api.rb +6 -2
- data/lib/cybersource_rest_client/api/transient_token_data_api.rb +12 -4
- data/lib/cybersource_rest_client/api/unified_checkout_capture_context_api.rb +6 -2
- data/lib/cybersource_rest_client/api/user_management_api.rb +6 -2
- data/lib/cybersource_rest_client/api/user_management_search_api.rb +6 -2
- data/lib/cybersource_rest_client/api/verification_api.rb +12 -4
- data/lib/cybersource_rest_client/api/void_api.rb +30 -10
- data/lib/cybersource_rest_client/api_client.rb +20 -3
- data/lib/cybersource_rest_client/models/acpv1instructions_decline_threshold.rb +232 -0
- data/lib/cybersource_rest_client/models/acpv1instructions_mandates.rb +405 -0
- data/lib/cybersource_rest_client/models/acpv1instructions_recurring_payment_information.rb +191 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconf_processor_info_payment_instrument_verifi_results.rb +213 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_confirmation_data.rb +229 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_merchant_information.rb +258 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_order_information.rb +377 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_order_information_shipping_details.rb +206 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_processor_information.rb +425 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idconfirmations_processor_information_payment_instrument.rb +190 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_attachments.rb +258 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_client_reference_information.rb +215 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_mandate_reference_data.rb +205 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_merchant_information.rb +334 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_merchant_information_merchant_descriptor.rb +250 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information.rb +228 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_amount_detail.rb +354 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_items.rb +367 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_items_additional_info.rb +231 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_items_policies.rb +346 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_line_items.rb +190 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_order_information_ship_to.rb +686 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_payment_options.rb +217 -0
- data/lib/cybersource_rest_client/models/acpv1instructionsinstruction_idcredentials_transaction_data.rb +348 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_assurance_data.rb +313 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_authenticated_identities.rb +237 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_authentication_context.rb +191 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_bill_to.rb +400 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_buyer_information.rb +229 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_buyer_information_personal_identification.rb +212 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_consent_data.rb +297 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_consumer_identity.rb +268 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_device_information.rb +378 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_device_information_device_data.rb +302 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_enrollment_reference_data.rb +207 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_payment_information.rb +215 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_payment_information_customer.rb +197 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_payment_information_instrument_identifier.rb +206 -0
- data/lib/cybersource_rest_client/models/acpv1tokens_payment_information_payment_instrument.rb +197 -0
- data/lib/cybersource_rest_client/models/agentic_cancel_purchase_intent_request.rb +257 -0
- data/lib/cybersource_rest_client/models/agentic_card_enrollment_bad_request_response400.rb +189 -0
- data/lib/cybersource_rest_client/models/agentic_card_enrollment_bad_request_response400_error.rb +238 -0
- data/lib/cybersource_rest_client/models/agentic_card_enrollment_bad_request_response400_error_detail.rb +224 -0
- data/lib/cybersource_rest_client/models/agentic_card_enrollment_request.rb +325 -0
- data/lib/cybersource_rest_client/models/agentic_card_enrollment_response200.rb +206 -0
- data/lib/cybersource_rest_client/models/agentic_card_enrollment_response202.rb +219 -0
- data/lib/cybersource_rest_client/models/agentic_confirm_transaction_events_request.rb +247 -0
- data/lib/cybersource_rest_client/models/agentic_confirm_transaction_events_response202.rb +195 -0
- data/lib/cybersource_rest_client/models/agentic_create_purchase_intent_request.rb +327 -0
- data/lib/cybersource_rest_client/models/agentic_create_purchase_intent_response200.rb +222 -0
- data/lib/cybersource_rest_client/models/agentic_pending_purchase_intent_response202.rb +241 -0
- data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_request.rb +247 -0
- data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_response200.rb +200 -0
- data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_response200_transaction_response_complete.rb +244 -0
- data/lib/cybersource_rest_client/models/agentic_retrieve_payment_credentials_response200_transaction_response_with_pending_events.rb +235 -0
- data/lib/cybersource_rest_client/models/agentic_update_purchase_intent_request.rb +315 -0
- data/lib/cybersource_rest_client/models/boardingv1registrations_organization_information_business_information.rb +36 -1
- data/lib/cybersource_rest_client/models/boardingv1registrations_organization_information_business_information_localized_names.rb +254 -0
- data/lib/cybersource_rest_client/models/ptsv2payments_travel_information_transit_airline_ancillary_information.rb +22 -5
- data/lib/cybersource_rest_client/models/ptsv2payments_travel_information_transit_airline_ancillary_information_service.rb +39 -5
- data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information.rb +246 -0
- data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit.rb +189 -0
- data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit_airline.rb +889 -0
- data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit_airline_ancillary_information.rb +259 -0
- data/lib/cybersource_rest_client/models/ptsv2paymentsidrefunds_travel_information_transit_airline_ancillary_information_service.rb +213 -0
- data/lib/cybersource_rest_client/models/refund_capture_request.rb +1 -1
- data/lib/cybersource_rest_client/models/refund_payment_request.rb +1 -1
- data/lib/cybersource_rest_client/models/upv1capturecontexts_data.rb +4 -2
- data/lib/cybersource_rest_client/models/upv1capturecontexts_data_merchant_defined_information.rb +3 -2
- data/lib/cybersource_rest_client/utilities/jwe_utility.rb +1 -1
- data/lib/cybersource_rest_client.rb +64 -0
- metadata +68 -3
- /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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2ea8b1d8350133ff8b367e96c70a70abe706b01c701f1ed9cab11518836ee9d8
|
|
4
|
+
data.tar.gz: 947eb679f3c7a6de9e2946b8c621ee7ee945039f43e8eac60b5999298c24ba55
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 = "
|
|
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 = "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
-
!@
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if @
|
|
292
|
-
@
|
|
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 !@
|
|
306
|
-
@
|
|
307
|
-
|
|
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 =
|
|
46
|
-
jwtCertificate =
|
|
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 =
|
|
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.
|
|
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 =
|
|
71
|
-
mleCertificate =
|
|
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.
|
|
75
|
-
raise ArgumentError, "No certificate found for the specified mle_key_alias '#{merchantConfig.
|
|
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
|