smile-identity-core 1.2.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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