Skip to content

Commit 97c3c99

Browse files
authored
Merge pull request #143 from CyberSource/feature/final-mle
Final MLE PR
2 parents 45ddb19 + 294b3f7 commit 97c3c99

File tree

69 files changed

+718
-497
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+718
-497
lines changed

generator/cybersource-ruby-template/api.mustache

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ module {{moduleName}}
180180
sdk_tracker = SdkTracker.new
181181
post_body = sdk_tracker.insert_developer_id_tracker(post_body, '{{dataType}}', @api_client.config.host, @api_client.merchantconfig.defaultDeveloperId)
182182
{{/bodyParam}}
183-
is_mle_supported_by_cybs_for_api = {{#vendorExtensions.x-devcenter-metaData.isMLEsupported}}true{{/vendorExtensions.x-devcenter-metaData.isMLEsupported}}{{^vendorExtensions.x-devcenter-metaData.isMLEsupported}}false{{/vendorExtensions.x-devcenter-metaData.isMLEsupported}}
184-
if MLEUtility.check_is_mle_for_API(@api_client.merchantconfig, is_mle_supported_by_cybs_for_api, ["{{operationId}}","{{operationId}}_with_http_info"])
183+
inbound_mle_status = "{{#vendorExtensions.x-devcenter-metaData.mleForRequest}}{{vendorExtensions.x-devcenter-metaData.mleForRequest}}{{/vendorExtensions.x-devcenter-metaData.mleForRequest}}{{^vendorExtensions.x-devcenter-metaData.mleForRequest}}false{{/vendorExtensions.x-devcenter-metaData.mleForRequest}}"
184+
if MLEUtility.check_is_mle_for_API(@api_client.merchantconfig, inbound_mle_status, ["{{operationId}}","{{operationId}}_with_http_info"])
185185
post_body = MLEUtility.encrypt_request_payload(@api_client.merchantconfig, post_body)
186186
end
187187
auth_names = [{{#authMethods}}'{{name}}'{{#hasMore}}, {{/hasMore}}{{/authMethods}}]

lib/AuthenticationSDK/authentication/jwt/JwtToken.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def getToken(merchantconfig_obj,gmtDatetime)
2525
jwtBody=getJwtBody(request_type, gmtDatetime, merchantconfig_obj)
2626
claimSet = JSON.parse(jwtBody)
2727

28-
cache_value = Cache.new.fetchJwtCertsAndKeys(merchantconfig_obj)
28+
cache_value = Cache.new.fetchCachedP12Certificate(merchantconfig_obj)
2929
privateKey = cache_value.private_key
3030
jwt_cert_obj = cache_value.cert
3131
jwt_cert_in_der= Base64.strict_encode64(jwt_cert_obj.to_der)

lib/AuthenticationSDK/core/MerchantConfig.rb

Lines changed: 143 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,69 @@
22
require_relative '../util/ExceptionHandler.rb'
33
require_relative '../logging/log_factory.rb'
44
require_relative '../logging/log_configuration.rb'
5+
require_relative '../util/CertificateUtility.rb'
56

67
public
78
# This fuction has all the merchantConfig properties getters and setters methods
89
class Merchantconfig
910
def initialize(cybsPropertyObj)
10-
# Common Parameters
11-
@merchantId = cybsPropertyObj['merchantID']
12-
@runEnvironment = cybsPropertyObj['runEnvironment']
13-
@intermediateHost = cybsPropertyObj['intermediateHost']
14-
@defaultDeveloperId = cybsPropertyObj['defaultDeveloperId']
15-
@authenticationType = cybsPropertyObj['authenticationType']
16-
@proxyAddress = cybsPropertyObj['proxyAddress']
17-
@proxyPort = cybsPropertyObj['proxyPort']
18-
@getId = ''
19-
@requestHost = ''
20-
@requestTarget = ''
21-
@requestJsonData = ''
22-
# HTTP Parameters
23-
@merchantSecretKey = cybsPropertyObj['merchantsecretKey']
24-
@merchantKeyId = cybsPropertyObj['merchantKeyId']
25-
# JWT Parameters
26-
@keysDirectory = cybsPropertyObj['keysDirectory']
27-
@keyAlias = cybsPropertyObj['keyAlias']
28-
@keyPass = cybsPropertyObj['keyPass']
29-
@keyFilename = cybsPropertyObj['keyFilename']
30-
@useMetaKey = cybsPropertyObj['useMetaKey']
31-
@portfolioID = cybsPropertyObj['portfolioID']
32-
@solutionId = cybsPropertyObj['solutionId']
33-
# MutualAuth & OAuth Parameters
34-
@enableClientCert = cybsPropertyObj['enableClientCert']
35-
@clientCertDirectory = cybsPropertyObj['clientCertDirectory']
36-
@sslClientCert = cybsPropertyObj['sslClientCert']
37-
@privateKey = cybsPropertyObj['privateKey']
38-
@sslKeyPassword = cybsPropertyObj['sslKeyPassword']
39-
@clientId = cybsPropertyObj['clientId']
40-
@clientSecret = cybsPropertyObj['clientSecret']
41-
@accessToken = cybsPropertyObj['accessToken']
42-
@refreshToken = cybsPropertyObj['refreshToken']
43-
# LogConfiguration
44-
@log_config = LogConfiguration.new(cybsPropertyObj['logConfiguration'])
45-
# Custom Default Headers
46-
@defaultCustomHeaders = cybsPropertyObj['defaultCustomHeaders']
47-
# Keep Alive Time for Connection Pooling
48-
@keepAliveTime = cybsPropertyObj['keepAliveTime'] || 118 # Default to 118 seconds as same as default of libcurl
49-
# Path to client JWE pem file directory
50-
@pemFileDirectory = cybsPropertyObj['pemFileDirectory']
51-
@mleKeyAlias = cybsPropertyObj['mleKeyAlias']
52-
@useMLEGlobally = cybsPropertyObj['useMLEGlobally']
53-
@mapToControlMLEonAPI = cybsPropertyObj['mapToControlMLEonAPI']
54-
validateMerchantDetails
55-
logAllProperties(cybsPropertyObj)
56-
validateMLEConfiguration
11+
# Common Parameters
12+
@merchantId = cybsPropertyObj['merchantID']
13+
@runEnvironment = cybsPropertyObj['runEnvironment']
14+
@intermediateHost = cybsPropertyObj['intermediateHost']
15+
@defaultDeveloperId = cybsPropertyObj['defaultDeveloperId']
16+
@authenticationType = cybsPropertyObj['authenticationType']
17+
@proxyAddress = cybsPropertyObj['proxyAddress']
18+
@proxyPort = cybsPropertyObj['proxyPort']
19+
@getId = ''
20+
@requestHost = ''
21+
@requestTarget = ''
22+
@requestJsonData = ''
23+
# HTTP Parameters
24+
@merchantSecretKey = cybsPropertyObj['merchantsecretKey']
25+
@merchantKeyId = cybsPropertyObj['merchantKeyId']
26+
# JWT Parameters
27+
@keysDirectory = cybsPropertyObj['keysDirectory']
28+
@keyAlias = cybsPropertyObj['keyAlias']
29+
@keyPass = cybsPropertyObj['keyPass']
30+
@keyFilename = cybsPropertyObj['keyFilename']
31+
@useMetaKey = cybsPropertyObj['useMetaKey']
32+
@portfolioID = cybsPropertyObj['portfolioID']
33+
@solutionId = cybsPropertyObj['solutionId']
34+
@p12KeyFilePath = nil
35+
# MutualAuth & OAuth Parameters
36+
@enableClientCert = cybsPropertyObj['enableClientCert']
37+
@clientCertDirectory = cybsPropertyObj['clientCertDirectory']
38+
@sslClientCert = cybsPropertyObj['sslClientCert']
39+
@privateKey = cybsPropertyObj['privateKey']
40+
@sslKeyPassword = cybsPropertyObj['sslKeyPassword']
41+
@clientId = cybsPropertyObj['clientId']
42+
@clientSecret = cybsPropertyObj['clientSecret']
43+
@accessToken = cybsPropertyObj['accessToken']
44+
@refreshToken = cybsPropertyObj['refreshToken']
45+
# LogConfiguration
46+
@log_config = LogConfiguration.new(cybsPropertyObj['logConfiguration'])
47+
# Custom Default Headers
48+
@defaultCustomHeaders = cybsPropertyObj['defaultCustomHeaders']
49+
# Keep Alive Time for Connection Pooling
50+
@keepAliveTime = cybsPropertyObj['keepAliveTime'] || 118 # Default to 118 seconds as same as default of libcurl
51+
# Path to client JWE pem file directory
52+
@pemFileDirectory = cybsPropertyObj['pemFileDirectory']
53+
@mleKeyAlias = cybsPropertyObj['mleKeyAlias']
54+
@useMLEGlobally = cybsPropertyObj['useMLEGlobally']
55+
@enableRequestMLEForOptionalApisGlobally = !!(cybsPropertyObj['enableRequestMLEForOptionalApisGlobally'] || cybsPropertyObj['useMLEGlobally'])
56+
@disableRequestMLEForMandatoryApisGlobally = cybsPropertyObj['disableRequestMLEForMandatoryApisGlobally']
57+
58+
59+
if !cybsPropertyObj['mleForRequestPublicCertPath'].nil? && !cybsPropertyObj['mleForRequestPublicCertPath'].to_s.strip.empty?
60+
@mleForRequestPublicCertPath = cybsPropertyObj['mleForRequestPublicCertPath'].to_s.strip
61+
end
62+
63+
@mapToControlMLEonAPI = cybsPropertyObj['mapToControlMLEonAPI']
64+
validateMerchantDetails
65+
validateMLEConfiguration(cybsPropertyObj)
66+
@p12KeyFilePath = File.join(@keysDirectory, @keyFilename + ".p12")
67+
logAllProperties(cybsPropertyObj)
5768
end
5869

5970
#fall back logic
@@ -164,6 +175,9 @@ def validateMerchantDetails()
164175
elsif !@keyFilename.instance_of? String
165176
@keyFilename=@keyFilename.to_s
166177
end
178+
if !check_key_file
179+
@log_obj.logger.error(ExceptionHandler.new.new_custom_error "Error finding or accessing the Key Directory or Key File. Please review the values in the merchant configuration.")
180+
end
167181
end
168182
if @authenticationType.upcase == Constants::AUTH_TYPE_MUTUAL_AUTH
169183
if @clientId.to_s.empty?
@@ -237,16 +251,30 @@ def validateMerchantDetails()
237251
end
238252
end
239253

240-
def validateMLEConfiguration
241-
if @useMLEGlobally.nil?
242-
@useMLEGlobally = false
254+
def validateMLEConfiguration(cybsPropertyObj)
255+
256+
if !@useMLEGlobally.nil? && !cybsPropertyObj['enableRequestMLEForOptionalApisGlobally'].nil?
257+
if @useMLEGlobally != cybsPropertyObj['enableRequestMLEForOptionalApisGlobally']
258+
raise StandardError.new(Constants::ERROR_PREFIX + "useMLEGlobally and enableRequestMLEForOptionalApisGlobally must have the same value if both are set")
259+
end
260+
end
261+
262+
if @disableRequestMLEForMandatoryApisGlobally.nil?
263+
@disableRequestMLEForMandatoryApisGlobally = false
264+
end
265+
266+
unless [true, false].include?(@disableRequestMLEForMandatoryApisGlobally)
267+
err = StandardError.new(Constants::ERROR_PREFIX + "disableRequestMLEForMandatoryApisGlobally must be a boolean")
268+
@log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
269+
raise err
243270
end
244271

245-
unless [true, false].include?(@useMLEGlobally)
246-
err = StandardError.new(Constants::ERROR_PREFIX + "useMLEGlobally must be a boolean")
272+
unless [true, false].include?(@enableRequestMLEForOptionalApisGlobally)
273+
err = StandardError.new(Constants::ERROR_PREFIX + "enableRequestMLEForOptionalApisGlobally must be a boolean")
247274
@log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
248275
raise err
249276
end
277+
250278
unless @mapToControlMLEonAPI.nil?
251279
unless @mapToControlMLEonAPI.is_a?(Hash) && @mapToControlMLEonAPI.keys.all? {|k| k.is_a?(String)} && @mapToControlMLEonAPI.values.all? { |v| [true, false].include?(v) }
252280
err = StandardError.new(Constants::ERROR_PREFIX + "mapToControlMLEonAPI must be a map with boolean values")
@@ -264,21 +292,39 @@ def validateMLEConfiguration
264292
@mleKeyAlias = Constants::DEFAULT_ALIAS_FOR_MLE_CERT
265293
end
266294

267-
mle_configured = @useMLEGlobally
268-
if !@mapToControlMLEonAPI.nil? && !@mapToControlMLEonAPI.empty?
269-
@mapToControlMLEonAPI.each do |_, value|
270-
unless [true, false].include?(value) && value
271-
mle_configured = true
272-
break
273-
end
295+
if @mleForRequestPublicCertPath && !@mleForRequestPublicCertPath.to_s.strip.empty?
296+
begin
297+
CertificateUtility.validatePathAndFile(@mleForRequestPublicCertPath, "mleForRequestPublicCertPath", @log_config)
298+
rescue => err
299+
@log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
300+
raise err
274301
end
275302
end
276303

277-
if mle_configured && !Constants::AUTH_TYPE_JWT.eql?(@authenticationType.upcase)
278-
err = StandardError.new(Constants::ERROR_PREFIX + "MLE can only be used with JWT authentication")
279-
@log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
280-
raise err
281-
end
304+
# # verify the input path for mle Cert should be correct else throw error in both case mle=true/false
305+
# if @mleForRequestPublicCertPath && [email protected]_s.strip.empty?
306+
# unless File.exist?(@mleForRequestPublicCertPath) && File.readable?(@mleForRequestPublicCertPath)
307+
# err = StandardError.new(Constants::ERROR_PREFIX + "Invalid mleForRequestPublicCertPath: file does not exist or is not readable")
308+
# @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
309+
# raise err
310+
# end
311+
# end
312+
313+
# mle_configured = @enableRequestMLEForOptionalApisGlobally
314+
315+
# @mapToControlMLEonAPI.each do |_, value|
316+
# unless [true, false].include?(value) && value
317+
# mle_configured = true
318+
# break
319+
# end
320+
# end
321+
# end
322+
323+
# if mle_configured && !Constants::AUTH_TYPE_JWT.eql?(@authenticationType.upcase)
324+
# err = StandardError.new(Constants::ERROR_PREFIX + "MLE can only be used with JWT authentication")
325+
# @log_obj.logger.error(ExceptionHandler.new.new_api_exception err)
326+
# raise err
327+
# end
282328
end
283329

284330
def logAllProperties(merchantPropertyObj)
@@ -299,6 +345,35 @@ def logAllProperties(merchantPropertyObj)
299345
@log_obj.logger.info('Merchant Configuration :\n' + propertyObj.to_s)
300346
end
301347

348+
def check_key_file
349+
# Directory exists?
350+
unless Dir.exist?(@keysDirectory)
351+
@log_obj.logger.error("Keys Directory not found. Entered directory : #{@keysDirectory}")
352+
return false
353+
end
354+
355+
key_file_pathname = File.join(@keysDirectory, @keyFilename + ".p12")
356+
357+
# File exists?
358+
unless File.exist?(key_file_pathname)
359+
@log_obj.logger.error("Key File not found. Check path/filename entered. Entered path/filename : #{key_file_pathname}")
360+
return false
361+
end
362+
363+
@log_obj.logger.info("Entered value for Key File Path : #{key_file_pathname}")
364+
365+
# Can file be opened for reading?
366+
begin
367+
File.open(key_file_pathname, 'rb') do |f|
368+
# Just open and close
369+
end
370+
return true
371+
rescue => e
372+
@log_obj.logger.info("File cannot be accessed. Permission denied : #{key_file_pathname}")
373+
return false
374+
end
375+
end
376+
302377
# getter and setter methods
303378
attr_accessor :merchantId
304379
attr_accessor :merchantSecretKey
@@ -337,6 +412,10 @@ def logAllProperties(merchantPropertyObj)
337412
attr_accessor :defaultCustomHeaders
338413
attr_accessor :pemFileDirectory
339414
attr_accessor :useMLEGlobally
415+
attr_accessor :enableRequestMLEForOptionalApisGlobally
416+
attr_accessor :disableRequestMLEForMandatoryApisGlobally
417+
attr_accessor :mleForRequestPublicCertPath
340418
attr_accessor :mapToControlMLEonAPI
341419
attr_accessor :mleKeyAlias
420+
attr_accessor :p12KeyFilePath
342421
end

0 commit comments

Comments
 (0)