yoti 1.6.4 → 1.10.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.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/README.md +22 -224
  4. data/lib/yoti.rb +63 -1
  5. data/lib/yoti/activity_details.rb +3 -5
  6. data/lib/yoti/client.rb +13 -4
  7. data/lib/yoti/configuration.rb +8 -3
  8. data/lib/yoti/data_type/age_verification.rb +1 -1
  9. data/lib/yoti/data_type/base_profile.rb +1 -1
  10. data/lib/yoti/data_type/document_details.rb +1 -1
  11. data/lib/yoti/data_type/image.rb +4 -12
  12. data/lib/yoti/data_type/image_jpeg.rb +2 -0
  13. data/lib/yoti/data_type/image_png.rb +2 -0
  14. data/lib/yoti/data_type/media.rb +22 -0
  15. data/lib/yoti/data_type/signed_time_stamp.rb +1 -1
  16. data/lib/yoti/doc_scan/client.rb +191 -0
  17. data/lib/yoti/doc_scan/constants.rb +35 -0
  18. data/lib/yoti/doc_scan/errors.rb +81 -0
  19. data/lib/yoti/doc_scan/session/create/create_session_result.rb +50 -0
  20. data/lib/yoti/doc_scan/session/create/document_filter.rb +31 -0
  21. data/lib/yoti/doc_scan/session/create/document_restrictions_filter.rb +140 -0
  22. data/lib/yoti/doc_scan/session/create/notification_config.rb +142 -0
  23. data/lib/yoti/doc_scan/session/create/objective/objective.rb +31 -0
  24. data/lib/yoti/doc_scan/session/create/objective/proof_of_address_objective.rb +31 -0
  25. data/lib/yoti/doc_scan/session/create/orthogonal_restrictions_filter.rb +150 -0
  26. data/lib/yoti/doc_scan/session/create/requested_check.rb +39 -0
  27. data/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb +95 -0
  28. data/lib/yoti/doc_scan/session/create/requested_face_match_check.rb +95 -0
  29. data/lib/yoti/doc_scan/session/create/requested_id_document_comparison_check.rb +53 -0
  30. data/lib/yoti/doc_scan/session/create/requested_liveness_check.rb +108 -0
  31. data/lib/yoti/doc_scan/session/create/requested_supplementary_doc_text_extraction_task.rb +94 -0
  32. data/lib/yoti/doc_scan/session/create/requested_task.rb +39 -0
  33. data/lib/yoti/doc_scan/session/create/requested_text_extraction_task.rb +116 -0
  34. data/lib/yoti/doc_scan/session/create/required_document.rb +31 -0
  35. data/lib/yoti/doc_scan/session/create/required_id_document.rb +53 -0
  36. data/lib/yoti/doc_scan/session/create/required_supplementary_document.rb +90 -0
  37. data/lib/yoti/doc_scan/session/create/sdk_config.rb +221 -0
  38. data/lib/yoti/doc_scan/session/create/session_specification.rb +221 -0
  39. data/lib/yoti/doc_scan/session/retrieve/authenticity_check_response.rb +12 -0
  40. data/lib/yoti/doc_scan/session/retrieve/breakdown_response.rb +38 -0
  41. data/lib/yoti/doc_scan/session/retrieve/check_response.rb +63 -0
  42. data/lib/yoti/doc_scan/session/retrieve/details_response.rb +28 -0
  43. data/lib/yoti/doc_scan/session/retrieve/document_fields_response.rb +21 -0
  44. data/lib/yoti/doc_scan/session/retrieve/document_id_photo_response.rb +21 -0
  45. data/lib/yoti/doc_scan/session/retrieve/face_map_response.rb +21 -0
  46. data/lib/yoti/doc_scan/session/retrieve/face_match_check_response.rb +12 -0
  47. data/lib/yoti/doc_scan/session/retrieve/file_response.rb +21 -0
  48. data/lib/yoti/doc_scan/session/retrieve/frame_response.rb +21 -0
  49. data/lib/yoti/doc_scan/session/retrieve/generated_check_response.rb +28 -0
  50. data/lib/yoti/doc_scan/session/retrieve/generated_media.rb +28 -0
  51. data/lib/yoti/doc_scan/session/retrieve/generated_supplementary_document_text_data_check_response.rb +12 -0
  52. data/lib/yoti/doc_scan/session/retrieve/generated_text_data_check_response.rb +12 -0
  53. data/lib/yoti/doc_scan/session/retrieve/get_session_result.rb +145 -0
  54. data/lib/yoti/doc_scan/session/retrieve/id_document_comparison_check_response.rb +12 -0
  55. data/lib/yoti/doc_scan/session/retrieve/id_document_resource_response.rb +57 -0
  56. data/lib/yoti/doc_scan/session/retrieve/liveness_check_response.rb +12 -0
  57. data/lib/yoti/doc_scan/session/retrieve/liveness_resource_response.rb +24 -0
  58. data/lib/yoti/doc_scan/session/retrieve/media_response.rb +38 -0
  59. data/lib/yoti/doc_scan/session/retrieve/page_response.rb +37 -0
  60. data/lib/yoti/doc_scan/session/retrieve/recommendation_response.rb +34 -0
  61. data/lib/yoti/doc_scan/session/retrieve/report_response.rb +31 -0
  62. data/lib/yoti/doc_scan/session/retrieve/resource_container.rb +69 -0
  63. data/lib/yoti/doc_scan/session/retrieve/resource_response.rb +41 -0
  64. data/lib/yoti/doc_scan/session/retrieve/supplementary_document_resource_response.rb +57 -0
  65. data/lib/yoti/doc_scan/session/retrieve/supplementary_document_text_data_check_response.rb +12 -0
  66. data/lib/yoti/doc_scan/session/retrieve/supplementary_document_text_extraction_task_response.rb +18 -0
  67. data/lib/yoti/doc_scan/session/retrieve/task_response.rb +89 -0
  68. data/lib/yoti/doc_scan/session/retrieve/text_data_check_response.rb +12 -0
  69. data/lib/yoti/doc_scan/session/retrieve/text_extraction_task_response.rb +18 -0
  70. data/lib/yoti/doc_scan/session/retrieve/zoom_liveness_resource_response.rb +33 -0
  71. data/lib/yoti/doc_scan/support/supported_documents.rb +60 -0
  72. data/lib/yoti/dynamic_share_service/dynamic_scenario.rb +5 -0
  73. data/lib/yoti/dynamic_share_service/extension/extension.rb +3 -0
  74. data/lib/yoti/dynamic_share_service/extension/location_constraint_extension.rb +3 -0
  75. data/lib/yoti/dynamic_share_service/extension/thirdparty_attribute_extension.rb +1 -1
  76. data/lib/yoti/dynamic_share_service/extension/transactional_flow_extension.rb +4 -0
  77. data/lib/yoti/dynamic_share_service/policy/dynamic_policy.rb +74 -9
  78. data/lib/yoti/dynamic_share_service/policy/wanted_anchor.rb +3 -0
  79. data/lib/yoti/dynamic_share_service/policy/wanted_attribute.rb +5 -0
  80. data/lib/yoti/dynamic_share_service/share_url.rb +26 -33
  81. data/lib/yoti/errors.rb +15 -2
  82. data/lib/yoti/http/aml_check_request.rb +12 -6
  83. data/lib/yoti/http/payloads/aml_address.rb +4 -0
  84. data/lib/yoti/http/payloads/aml_profile.rb +7 -1
  85. data/lib/yoti/http/profile_request.rb +11 -6
  86. data/lib/yoti/http/request.rb +219 -18
  87. data/lib/yoti/http/signed_request.rb +13 -4
  88. data/lib/yoti/protobuf/main.rb +8 -8
  89. data/lib/yoti/share/attribute_issuance_details.rb +6 -0
  90. data/lib/yoti/ssl.rb +4 -4
  91. data/lib/yoti/util/age_processor.rb +1 -1
  92. data/lib/yoti/util/anchor_processor.rb +2 -2
  93. data/lib/yoti/util/log.rb +1 -1
  94. data/lib/yoti/util/validation.rb +41 -0
  95. data/lib/yoti/version.rb +1 -1
  96. data/yoti.gemspec +18 -6
  97. metadata +69 -33
  98. data/.github/ISSUE_TEMPLATE.md +0 -17
  99. data/.gitignore +0 -39
  100. data/CONTRIBUTING.md +0 -98
  101. data/Guardfile +0 -11
  102. data/Rakefile +0 -54
  103. data/login_flow.png +0 -0
  104. data/rubocop.yml +0 -57
  105. data/yardstick.yml +0 -9
@@ -1,11 +1,19 @@
1
1
  require 'securerandom'
2
+ require 'cgi'
2
3
 
3
4
  module Yoti
4
5
  # Manage the API's HTTPS requests
5
6
  class Request
7
+ # @deprecated will be removed in 2.0.0 - token is now provided with the endpoint
6
8
  # @return [String] the URL token received from Yoti Connect
7
9
  attr_accessor :encrypted_connect_token
8
10
 
11
+ # @return [String] the base URL
12
+ attr_writer :base_url
13
+
14
+ # @return [Hash] query params to add to the request
15
+ attr_accessor :query_params
16
+
9
17
  # @return [String] the HTTP method used for the request
10
18
  # The allowed methods are: GET, DELETE, POST, PUT, PATCH
11
19
  attr_accessor :http_method
@@ -13,36 +21,93 @@ module Yoti
13
21
  # @return [String] the API endpoint for the request
14
22
  attr_accessor :endpoint
15
23
 
16
- # @return [Hash] the body sent with the request
24
+ # @return [#to_json,String] the body sent with the request
17
25
  attr_accessor :payload
18
26
 
19
27
  def initialize
20
28
  @headers = {}
21
29
  end
22
30
 
31
+ #
32
+ # @return [RequestBuilder]
33
+ #
34
+ def self.builder
35
+ RequestBuilder.new
36
+ end
37
+
38
+ #
23
39
  # Adds a HTTP header to the request
40
+ #
41
+ # @param [String] header
42
+ # @param [String] value
43
+ #
24
44
  def add_header(header, value)
25
45
  @headers[header] = value
26
46
  end
27
47
 
48
+ #
28
49
  # Makes a HTTP request after signing the headers
29
- # @return [Hash] the body from the HTTP request
30
- def body
50
+ #
51
+ # @return [HTTPResponse]
52
+ #
53
+ def execute
31
54
  raise RequestError, 'The request requires a HTTP method.' unless @http_method
32
- raise RequestError, 'The payload needs to be a hash.' unless @payload.to_s.empty? || @payload.is_a?(Hash)
33
55
 
34
- res = Net::HTTP.start(uri.hostname, Yoti.configuration.api_port, use_ssl: https_uri?) do |http|
56
+ http_res = Net::HTTP.start(uri.hostname, Yoti.configuration.api_port, use_ssl: https_uri?) do |http|
35
57
  signed_request = SignedRequest.new(unsigned_request, path, @payload).sign
36
58
  http.request(signed_request)
37
59
  end
38
60
 
39
- raise RequestError, "Unsuccessful Yoti API call: #{res.message}" unless res.code == '200'
61
+ raise RequestError.new("Unsuccessful Yoti API call: #{http_res.message}", http_res) unless response_is_success(http_res)
40
62
 
41
- res.body
63
+ http_res
64
+ end
65
+
66
+ #
67
+ # Makes a HTTP request and returns the body after signing the headers
68
+ #
69
+ # @return [String]
70
+ #
71
+ def body
72
+ execute.body
73
+ end
74
+
75
+ #
76
+ # @return [String] the base URL
77
+ #
78
+ def base_url
79
+ @base_url ||= Yoti.configuration.api_endpoint
42
80
  end
43
81
 
44
82
  private
45
83
 
84
+ #
85
+ # @param [Net::HTTPResponse] http_res
86
+ #
87
+ # @return [Boolean]
88
+ #
89
+ def response_is_success(http_res)
90
+ http_res.code.to_i >= 200 && http_res.code.to_i < 300
91
+ end
92
+
93
+ #
94
+ # Adds payload to provided HTTP request
95
+ #
96
+ # @param [Net::HTTPRequest] http_req
97
+ #
98
+ def add_payload(http_req)
99
+ return if @payload.to_s.empty?
100
+
101
+ if @payload.is_a?(String)
102
+ http_req.body = @payload
103
+ elsif @payload.respond_to?(:to_json)
104
+ http_req.body = @payload.to_json
105
+ end
106
+ end
107
+
108
+ #
109
+ # @return [Net::HTTPRequest] the unsigned HTTP request
110
+ #
46
111
  def unsigned_request
47
112
  case @http_method
48
113
  when 'GET'
@@ -51,13 +116,13 @@ module Yoti
51
116
  http_req = Net::HTTP::Delete.new(uri)
52
117
  when 'POST'
53
118
  http_req = Net::HTTP::Post.new(uri)
54
- http_req.body = @payload.to_json unless @payload.to_s.empty?
119
+ add_payload(http_req)
55
120
  when 'PUT'
56
121
  http_req = Net::HTTP::Put.new(uri)
57
- http_req.body = @payload.to_json unless @payload.to_s.empty?
122
+ add_payload(http_req)
58
123
  when 'PATCH'
59
124
  http_req = Net::HTTP::Patch.new(uri)
60
- http_req.body = @payload.to_json unless @payload.to_s.empty?
125
+ add_payload(http_req)
61
126
  else
62
127
  raise RequestError, "Request method not allowed: #{@http_method}"
63
128
  end
@@ -69,22 +134,27 @@ module Yoti
69
134
  http_req
70
135
  end
71
136
 
137
+ #
138
+ # @return [URI] the full request URI
139
+ #
72
140
  def uri
73
- @uri ||= URI(Yoti.configuration.api_endpoint + path)
141
+ @uri ||= URI(base_url + path)
74
142
  end
75
143
 
144
+ #
145
+ # @return [String] the path with query string
146
+ #
76
147
  def path
77
148
  @path ||= begin
78
- nonce = SecureRandom.uuid
79
- timestamp = Time.now.to_i
80
-
81
- "/#{@endpoint}/#{token}"\
82
- "?nonce=#{nonce}"\
83
- "&timestamp=#{timestamp}"\
84
- "&appId=#{Yoti.configuration.client_sdk_id}"
149
+ "/#{@endpoint}/#{token}".chomp('/') + "?#{query_string}"
85
150
  end
86
151
  end
87
152
 
153
+ #
154
+ # @deprecated will be removed in 2.0.0 - token is now provided with the endpoint
155
+ #
156
+ # @return [String] the decrypted connect token
157
+ #
88
158
  def token
89
159
  return '' unless @encrypted_connect_token
90
160
 
@@ -94,5 +164,136 @@ module Yoti
94
164
  def https_uri?
95
165
  uri.scheme == 'https'
96
166
  end
167
+
168
+ #
169
+ # Builds query string including nonce and timestamp
170
+ #
171
+ # @return [String]
172
+ #
173
+ def query_string
174
+ params = {
175
+ nonce: SecureRandom.uuid,
176
+ timestamp: Time.now.to_i
177
+ }
178
+
179
+ if @query_params.nil?
180
+ # @deprecated this default will be removed in 2.0.0
181
+ # Append appId when no custom query params are provided.
182
+ params.merge!(appId: Yoti.configuration.client_sdk_id)
183
+ else
184
+ Validation.assert_is_a(Hash, @query_params, 'query_params')
185
+ params.merge!(@query_params)
186
+ end
187
+
188
+ params.map do |k, v|
189
+ "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
190
+ end.join('&')
191
+ end
192
+ end
193
+
194
+ #
195
+ # Builder for {Request}
196
+ #
197
+ class RequestBuilder
198
+ def initialize
199
+ @headers = {}
200
+ @query_params = {}
201
+ end
202
+
203
+ #
204
+ # Sets the base URL
205
+ #
206
+ # @param [String] base_url
207
+ #
208
+ # @return [self]
209
+ #
210
+ def with_base_url(base_url)
211
+ Validation.assert_is_a(String, base_url, 'base_url')
212
+ @base_url = base_url
213
+ self
214
+ end
215
+
216
+ #
217
+ # Adds a HTTP header to the request
218
+ #
219
+ # @param [String] header
220
+ # @param [String] value
221
+ #
222
+ # @return [self]
223
+ #
224
+ def with_header(header, value)
225
+ Validation.assert_is_a(String, header, 'header')
226
+ Validation.assert_is_a(String, value, 'value')
227
+ @headers[header] = value
228
+ self
229
+ end
230
+
231
+ #
232
+ # Adds a query parameter to the request
233
+ #
234
+ # @param [String] key
235
+ # @param [String] value
236
+ #
237
+ # @return [self]
238
+ #
239
+ def with_query_param(key, value)
240
+ Validation.assert_is_a(String, key, 'key')
241
+ Validation.assert_is_a(String, value, 'value')
242
+ @query_params[key] = value
243
+ self
244
+ end
245
+
246
+ #
247
+ # Sets the HTTP method
248
+ #
249
+ # @param [String] http_method
250
+ #
251
+ # @return [self]
252
+ #
253
+ def with_http_method(http_method)
254
+ Validation.assert_is_a(String, http_method, 'http_method')
255
+ @http_method = http_method
256
+ self
257
+ end
258
+
259
+ #
260
+ # Sets the API endpoint for the request
261
+ #
262
+ # @param [String] endpoint
263
+ #
264
+ # @return [self]
265
+ #
266
+ def with_endpoint(endpoint)
267
+ Validation.assert_is_a(String, endpoint, 'endpoint')
268
+ @endpoint = endpoint
269
+ self
270
+ end
271
+
272
+ #
273
+ # Sets the body sent with the request
274
+ #
275
+ # @param [#to_json,String] payload
276
+ #
277
+ # @return [self]
278
+ #
279
+ def with_payload(payload)
280
+ Validation.assert_respond_to(:to_json, payload, 'payload') unless payload.is_a?(String)
281
+ @payload = payload
282
+ self
283
+ end
284
+
285
+ #
286
+ # @return [Request]
287
+ #
288
+ def build
289
+ request = Request.new
290
+ request.base_url = @base_url
291
+ request.endpoint = @endpoint
292
+ request.query_params = @query_params
293
+ request.http_method = @http_method
294
+ request.payload = @payload
295
+ @headers.map { |k, v| request.add_header(k, v) }
296
+ request
297
+ end
97
298
  end
98
299
  end
@@ -3,6 +3,11 @@ require 'base64'
3
3
  module Yoti
4
4
  # Converts a basic Net::HTTP request into a Yoti Signed Request
5
5
  class SignedRequest
6
+ #
7
+ # @param [Net::HTTPRequest] unsigned_request
8
+ # @param [String] path
9
+ # @param [#to_json,String] payload
10
+ #
6
11
  def initialize(unsigned_request, path, payload = {})
7
12
  @http_req = unsigned_request
8
13
  @path = path
@@ -10,6 +15,9 @@ module Yoti
10
15
  @auth_key = Yoti::SSL.auth_key_from_pem
11
16
  end
12
17
 
18
+ #
19
+ # @return [Net::HTTPRequest]
20
+ #
13
21
  def sign
14
22
  @http_req['X-Yoti-Auth-Digest'] = message_signature
15
23
  @http_req['X-Yoti-SDK'] = Yoti.configuration.sdk_identifier
@@ -20,18 +28,19 @@ module Yoti
20
28
  private
21
29
 
22
30
  def message_signature
23
- @message_signature ||= Yoti::SSL.get_secure_signature("#{http_method}&#{@path}#{payload_string}")
31
+ @message_signature ||= Yoti::SSL.get_secure_signature("#{http_method}&#{@path}#{base64_payload}")
24
32
  end
25
33
 
26
34
  def http_method
27
35
  @http_req.method
28
36
  end
29
37
 
30
- # Create the base64 encoded request body
31
- def payload_string
38
+ def base64_payload
32
39
  return '' unless @payload
33
40
 
34
- '&' + Base64.strict_encode64(@payload.to_json)
41
+ payload_string = @payload.is_a?(String) ? @payload : @payload.to_json
42
+
43
+ "&#{Base64.strict_encode64(payload_string)}"
35
44
  end
36
45
  end
37
46
  end
@@ -3,12 +3,12 @@ $LOAD_PATH.unshift File.expand_path('./attrpubapi/', __dir__)
3
3
  require 'google/protobuf'
4
4
  require 'json'
5
5
 
6
- require_relative 'attrpubapi/List_pb.rb'
7
- require_relative 'compubapi/EncryptedData_pb.rb'
8
- require_relative 'compubapi/SignedTimestamp_pb.rb'
9
- require_relative 'sharepubapi/ExtraData_pb.rb'
10
- require_relative 'sharepubapi/IssuingAttributes_pb.rb'
11
- require_relative 'sharepubapi/ThirdPartyAttribute_pb.rb'
6
+ require_relative 'attrpubapi/List_pb'
7
+ require_relative 'compubapi/EncryptedData_pb'
8
+ require_relative 'compubapi/SignedTimestamp_pb'
9
+ require_relative 'sharepubapi/ExtraData_pb'
10
+ require_relative 'sharepubapi/IssuingAttributes_pb'
11
+ require_relative 'sharepubapi/ThirdPartyAttribute_pb'
12
12
 
13
13
  module Yoti
14
14
  module Protobuf
@@ -91,9 +91,9 @@ module Yoti
91
91
  private
92
92
 
93
93
  def convert_multi_value(value)
94
- proto_multi_value = Yoti::Protobuf::Attrpubapi::MultiValue.decode(value)
94
+ proto_multi_values = Yoti::Protobuf::Attrpubapi::MultiValue.decode(value).values
95
95
  items = []
96
- proto_multi_value.values.each do |item|
96
+ proto_multi_values.each do |item|
97
97
  items << value_based_on_content_type(item.data, item.content_type)
98
98
  end
99
99
  MultiValue.new(items)
@@ -5,6 +5,7 @@ require 'base64'
5
5
  module Yoti
6
6
  module Share
7
7
  class Definition
8
+ # @return [String]
8
9
  attr_reader :name
9
10
 
10
11
  #
@@ -18,8 +19,13 @@ module Yoti
18
19
  end
19
20
 
20
21
  class AttributeIssuanceDetails
22
+ # @return [String]
21
23
  attr_reader :token
24
+
25
+ # @return [Array<Yoti::Share::Definition>]
22
26
  attr_reader :attributes
27
+
28
+ # @return [DateTime|nil]
23
29
  attr_reader :expiry_date
24
30
 
25
31
  #
@@ -32,7 +32,7 @@ module Yoti
32
32
  end
33
33
 
34
34
  # Extracts the public key from pem key, converts it to a DER base 64 encoded value
35
- # @return [String] base 64 encoded anthentication key
35
+ # @return [String] base 64 encoded authentication key
36
36
  def auth_key_from_pem
37
37
  public_key = private_key.public_key
38
38
  Base64.strict_encode64(public_key.to_der)
@@ -42,13 +42,13 @@ module Yoti
42
42
  # @param message [String] message to be signed
43
43
  # @return [String] signed message encoded in base 64
44
44
  def get_secure_signature(message)
45
- digest = OpenSSL::Digest::SHA256.new
45
+ digest = OpenSSL::Digest.new('SHA256')
46
46
  Base64.strict_encode64(private_key.sign(digest, message))
47
47
  end
48
48
 
49
49
  # Uses the decrypted receipt key and the current user's iv to decode the text
50
50
  # @param key [String] base 64 decoded key
51
- # @param iv [String] base 64 decoded iv
51
+ # @param user_iv [String] base 64 decoded iv
52
52
  # @param text [String] base 64 decoded cyphered text
53
53
  # @return [String] base 64 decoded deciphered text
54
54
  def decipher(key, user_iv, text)
@@ -60,7 +60,7 @@ module Yoti
60
60
  end
61
61
 
62
62
  # Reset and reload the Private Key used for SSL functions
63
- # @deprecated 2.0.0
63
+ # @deprecated will be removed in 2.0.0
64
64
  def reload!
65
65
  @private_key = nil
66
66
  @pem = nil
@@ -2,7 +2,7 @@ module Yoti
2
2
  #
3
3
  # Process age attribute
4
4
  #
5
- # @deprecated 2.0.0 - replaced by Yoti::AgeVerification
5
+ # @deprecated will be removed in 2.0.0 - replaced by Yoti::AgeVerification
6
6
  #
7
7
  class AgeProcessor
8
8
  AGE_PATTERN = 'age_(over|under):[1-9][0-9]?[0-9]?'