smile-identity-core 1.2.1 → 2.1.0

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.
@@ -1,82 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'validations'
4
+
1
5
  module SmileIdentityCore
6
+ # Allows you to query the Identity Information for an individual using their ID number
2
7
  class IDApi
8
+ include Validations
9
+
10
+ REQUIRED_ID_INFO_FIELD = %i[country id_type id_number].freeze
3
11
 
4
12
  def initialize(partner_id, api_key, sid_server)
5
13
  @partner_id = partner_id.to_s
6
14
  @api_key = api_key
7
15
 
8
- @sid_server = sid_server
9
- if !(sid_server =~ URI::regexp)
10
- sid_server_mapping = {
11
- 0 => 'https://testapi.smileidentity.com/v1',
12
- 1 => 'https://api.smileidentity.com/v1',
13
- }
14
- @url = sid_server_mapping[sid_server.to_i]
15
- else
16
- @url = sid_server
17
- end
16
+ @url = if sid_server !~ URI::DEFAULT_PARSER.make_regexp
17
+ SmileIdentityCore::ENV::SID_SERVER_MAPPING[sid_server.to_s]
18
+ else
19
+ sid_server
20
+ end
18
21
  end
19
22
 
20
23
  def submit_job(partner_params, id_info, options = {})
21
- self.partner_params = symbolize_keys partner_params
22
- self.id_info = symbolize_keys id_info
23
- @use_new_signature = symbolize_keys(options || {}).fetch(:signature, false)
24
+ @partner_params = validate_partner_params(symbolize_keys(partner_params))
25
+ @id_info = validate_id_info(symbolize_keys(id_info), REQUIRED_ID_INFO_FIELD)
24
26
 
25
- if @partner_params[:job_type].to_i != 5
26
- raise ArgumentError, 'Please ensure that you are setting your job_type to 5 to query ID Api'
27
+ unless [JobType::ENHANCED_KYC, JobType::BUSINESS_VERIFICATION].include?(@partner_params[:job_type].to_i)
28
+ raise ArgumentError, 'Please ensure that you are setting your job_type to 5 or 7 to query ID Api'
27
29
  end
28
30
 
29
- setup_requests
30
- end
31
-
32
- def partner_params=(partner_params)
33
- if partner_params == nil
34
- raise ArgumentError, 'Please ensure that you send through partner params'
31
+ if partner_params[:job_type] == JobType::BUSINESS_VERIFICATION
32
+ return SmileIdentityCore::BusinessVerification
33
+ .new(@partner_id, @api_key, @url)
34
+ .submit_job(partner_params, id_info)
35
35
  end
36
36
 
37
- if !partner_params.is_a?(Hash)
38
- raise ArgumentError, 'Partner params needs to be a hash'
39
- end
37
+ options = symbolize_keys(options || {})
38
+ @use_async_endpoint = options.fetch(:async, false)
40
39
 
41
- [:user_id, :job_id, :job_type].each do |key|
42
- unless partner_params[key] && !partner_params[key].nil? && !(partner_params[key].empty? if partner_params[key].is_a?(String))
43
- raise ArgumentError, "Please make sure that #{key} is included in the partner params"
44
- end
45
- end
46
-
47
- @partner_params = partner_params
48
- end
49
-
50
- def id_info=(id_info)
51
-
52
- updated_id_info = id_info
53
-
54
- if updated_id_info.nil? || updated_id_info.keys.length == 0
55
- raise ArgumentError, 'Please make sure that id_info not empty or nil'
56
- end
57
-
58
- [:country, :id_type, :id_number].each do |key|
59
- unless updated_id_info[key] && !updated_id_info[key].nil? && !updated_id_info[key].empty?
60
- raise ArgumentError, "Please make sure that #{key} is included in the id_info"
61
- end
62
- end
63
-
64
- @id_info = updated_id_info
40
+ setup_requests
65
41
  end
66
42
 
67
43
  private
68
44
 
69
- def symbolize_keys params
70
- (params.is_a?(Hash)) ? Hash[params.map{ |k, v| [k.to_sym, v] }] : params
45
+ def symbolize_keys(params)
46
+ params.is_a?(Hash) ? params.transform_keys(&:to_sym) : params
71
47
  end
72
48
 
73
49
  def setup_requests
74
- url = "#{@url}/id_verification"
75
-
76
50
  request = Typhoeus::Request.new(
77
- url,
51
+ "#{@url}/#{endpoint}",
78
52
  method: 'POST',
79
- headers: {'Content-Type'=> "application/json"},
53
+ headers: { 'Content-Type' => 'application/json' },
80
54
  body: configure_json
81
55
  )
82
56
 
@@ -88,39 +62,24 @@ module SmileIdentityCore
88
62
  request.run
89
63
  end
90
64
 
65
+ def endpoint
66
+ @use_async_endpoint ? 'async_id_verification' : 'id_verification'
67
+ end
68
+
91
69
  def configure_json
92
- request_security(use_new_signature: @use_new_signature)
93
- .merge(@id_info)
94
- .merge(
95
- partner_id: @partner_id,
96
- partner_params: @partner_params)
97
- .to_json
70
+ signature_generator.generate_signature(Time.now.to_s)
71
+ .merge(@id_info)
72
+ .merge(
73
+ partner_id: @partner_id,
74
+ partner_params: @partner_params,
75
+ source_sdk: SmileIdentityCore::SOURCE_SDK,
76
+ source_sdk_version: SmileIdentityCore::VERSION
77
+ )
78
+ .to_json
98
79
  end
99
80
 
100
81
  def signature_generator
101
82
  SmileIdentityCore::Signature.new(@partner_id, @api_key)
102
83
  end
103
-
104
- def signature(timestamp: Time.now.to_s)
105
- signature = signature_generator.generate_signature(timestamp)[:signature]
106
- {
107
- signature: signature,
108
- timestamp: timestamp
109
- }
110
- end
111
-
112
- def sec_key(timestamp: Time.now.to_s)
113
- sec_key = signature_generator.generate_sec_key(timestamp)[:sec_key]
114
- {
115
- sec_key: sec_key,
116
- timestamp: timestamp
117
- }
118
- end
119
-
120
- def request_security(use_new_signature: true)
121
- return signature if use_new_signature
122
-
123
- sec_key
124
- end
125
84
  end
126
85
  end
@@ -1,54 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SmileIdentityCore
4
+ # Contains handy methods to generate and confirm signature for authentication
2
5
  class Signature
3
-
4
6
  def initialize(partner_id, api_key)
5
7
  @api_key = api_key
6
8
  @partner_id = partner_id
7
9
  end
8
10
 
9
- def generate_sec_key(timestamp=Time.now.to_i)
10
- begin
11
- @timestamp = timestamp
12
-
13
- hash_signature = Digest::SHA256.hexdigest([@partner_id.to_i, @timestamp].join(":"))
14
- public_key = OpenSSL::PKey::RSA.new(Base64.decode64(@api_key))
15
- @sec_key = [Base64.encode64(public_key.public_encrypt(hash_signature)), hash_signature].join('|')
16
-
17
- {
18
- sec_key: @sec_key,
19
- timestamp: @timestamp
20
- }
21
- rescue => e
22
- raise e
23
- end
24
- end
25
-
26
- def confirm_sec_key(timestamp, sec_key)
27
- begin
28
- hash_signature = Digest::SHA256.hexdigest([@partner_id.to_i, timestamp].join(":"))
29
- encrypted = sec_key.split('|')[0]
30
-
31
- public_key = OpenSSL::PKey::RSA.new(Base64.decode64(@api_key))
32
- decrypted = public_key.public_decrypt(Base64.decode64(encrypted))
33
-
34
- decrypted == hash_signature
35
- rescue => e
36
- raise e
37
- end
38
- end
39
-
40
- def generate_signature(timestamp=Time.now.to_s)
11
+ # Generates a signature based on the specified timestamp (uses the current time by default)
12
+ #
13
+ # @return [Hash] containing both the signature and related timestamp
14
+ def generate_signature(timestamp = Time.now.to_s)
41
15
  hmac = OpenSSL::HMAC.new(@api_key, 'sha256')
42
16
  hmac.update(timestamp.to_s)
43
17
  hmac.update(@partner_id)
44
- hmac.update("sid_request")
45
- signature = Base64.strict_encode64(hmac.digest())
18
+ hmac.update('sid_request')
19
+ @signature = Base64.strict_encode64(hmac.digest)
46
20
  {
47
- signature: signature,
21
+ signature: @signature,
48
22
  timestamp: timestamp.to_s
49
23
  }
50
24
  end
51
25
 
26
+ # Confirms the signature against a newly generated signature based on the same timestamp
27
+ #
28
+ # @param [String] timestamp the timestamp to generate the signature from
29
+ # @param [String] msg_signature a previously generated signature, to be confirmed
30
+ # @return [Boolean] TRUE or FALSE
52
31
  def confirm_signature(timestamp, msg_signature)
53
32
  generate_signature(timestamp)[:signature] == msg_signature
54
33
  end
@@ -1,22 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SmileIdentityCore
4
+ # A utility class to query job status
2
5
  class Utilities
3
-
4
6
  def initialize(partner_id, api_key, sid_server)
5
7
  @partner_id = partner_id.to_s
6
8
  @api_key = api_key
7
9
 
8
- if !(sid_server =~ URI::regexp)
9
- sid_server_mapping = {
10
- 0 => 'https://testapi.smileidentity.com/v1',
11
- 1 => 'https://api.smileidentity.com/v1',
12
- }
13
- @url = sid_server_mapping[sid_server.to_i]
14
- else
15
- @url = sid_server
16
- end
10
+ @url = if sid_server !~ URI::DEFAULT_PARSER.make_regexp
11
+ SmileIdentityCore::ENV::SID_SERVER_MAPPING[sid_server.to_s]
12
+ else
13
+ sid_server
14
+ end
17
15
 
18
16
  @signature_connection = SmileIdentityCore::Signature.new(@partner_id, @api_key)
19
-
20
17
  end
21
18
 
22
19
  def get_job_status(user_id, job_id, options = {})
@@ -24,77 +21,53 @@ module SmileIdentityCore
24
21
  options[:return_history] ||= false
25
22
  options[:return_image_links] ||= false
26
23
 
27
- security = request_security(use_new_signature: options.fetch(:signature, false))
28
- query_job_status(configure_job_query(user_id, job_id, options).merge(security))
24
+ query_job_status(configure_job_query(user_id, job_id,
25
+ options).merge(@signature_connection.generate_signature(Time.now.to_s)))
29
26
  end
30
27
 
31
28
  private
32
29
 
33
- def symbolize_keys params
34
- (params.is_a?(Hash)) ? Hash[params.map{ |k, v| [k.to_sym, v] }] : params
30
+ def symbolize_keys(params)
31
+ params.is_a?(Hash) ? params.transform_keys(&:to_sym) : params
35
32
  end
36
33
 
37
34
  def query_job_status(request_json_data)
38
- url = "#{@url}/job_status"
39
-
40
35
  request = Typhoeus::Request.new(
41
- url,
42
- headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
36
+ "#{@url}/job_status",
37
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
43
38
  method: :post,
44
39
  body: request_json_data.to_json
45
40
  )
46
41
 
47
42
  request.on_complete do |response|
48
- begin
49
- body = JSON.parse(response.body)
43
+ body = JSON.parse(response.body)
50
44
 
51
- # NB: we have to trust that the server will return the right kind of
52
- # timestamp (integer or string) for the signature, and the right kind
53
- # of signature in the "signature" field. The best way to know what
54
- # kind of validation to perform is by remembering which kind of
55
- # security we started with.
56
- if request_json_data.has_key?(:sec_key)
57
- valid = @signature_connection.confirm_sec_key(body['timestamp'], body['signature'])
58
- else
59
- valid = @signature_connection.confirm_signature(body['timestamp'], body['signature'])
60
- end
45
+ # NB: we have to trust that the server will return the right kind of
46
+ # timestamp (integer or string) for the signature, and the right kind
47
+ # of signature in the "signature" field. The best way to know what
48
+ # kind of validation to perform is by remembering which kind of
49
+ # security we started with.
50
+ valid = @signature_connection.confirm_signature(body['timestamp'], body['signature'])
61
51
 
62
- if(!valid)
63
- raise "Unable to confirm validity of the job_status response"
64
- end
52
+ raise 'Unable to confirm validity of the job_status response' unless valid
65
53
 
66
- return body
67
- rescue => e
68
- raise e
69
- end
54
+ return body
55
+ rescue StandardError => e
56
+ raise e
70
57
  end
71
58
 
72
59
  request.run
73
60
  end
74
61
 
75
- def request_security(use_new_signature: false)
76
- if use_new_signature
77
- @timestamp = Time.now.to_s
78
- {
79
- signature: @signature_connection.generate_signature(@timestamp)[:signature],
80
- timestamp: @timestamp,
81
- }
82
- else
83
- @timestamp = Time.now.to_i
84
- {
85
- sec_key: @signature_connection.generate_sec_key(@timestamp)[:sec_key],
86
- timestamp: @timestamp,
87
- }
88
- end
89
- end
90
-
91
62
  def configure_job_query(user_id, job_id, options)
92
63
  {
93
64
  user_id: user_id,
94
65
  job_id: job_id,
95
66
  partner_id: @partner_id,
96
67
  image_links: options[:return_image_links],
97
- history: options[:return_history]
68
+ history: options[:return_history],
69
+ source_sdk: SmileIdentityCore::SOURCE_SDK,
70
+ source_sdk_version: SmileIdentityCore::VERSION
98
71
  }
99
72
  end
100
73
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmileIdentityCore
4
+ module Validations # :nodoc:
5
+ def validate_partner_params(partner_params)
6
+ raise ArgumentError, 'Please ensure that you send through partner params' if partner_params.nil?
7
+
8
+ raise ArgumentError, 'Partner params needs to be a hash' unless partner_params.is_a?(Hash)
9
+
10
+ %i[user_id job_id job_type].each do |key|
11
+ if partner_params[key].to_s.empty?
12
+ raise ArgumentError,
13
+ "Please make sure that #{key} is included in the partner params"
14
+ end
15
+ end
16
+
17
+ partner_params
18
+ end
19
+
20
+ def validate_id_info(id_info, required_id_info_fields)
21
+ raise ArgumentError, 'Please make sure that id_info is not empty or nil' if id_info.nil? || id_info.empty?
22
+
23
+ raise ArgumentError, 'Id info needs to be a hash' unless id_info.is_a?(Hash)
24
+
25
+ required_id_info_fields.each do |key|
26
+ raise ArgumentError, "Please make sure that #{key} is included in the id_info" if id_info[key].to_s.empty?
27
+ end
28
+
29
+ id_info
30
+ end
31
+ end
32
+ end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SmileIdentityCore
2
- VERSION = "1.2.1"
4
+ VERSION = '2.1.0'
5
+ SOURCE_SDK = 'Ruby'
3
6
  end