yoti 1.6.3 → 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +22 -224
  3. data/lib/yoti.rb +54 -1
  4. data/lib/yoti/client.rb +13 -4
  5. data/lib/yoti/configuration.rb +7 -2
  6. data/lib/yoti/data_type/age_verification.rb +1 -1
  7. data/lib/yoti/data_type/base_profile.rb +1 -1
  8. data/lib/yoti/data_type/image.rb +4 -12
  9. data/lib/yoti/data_type/image_jpeg.rb +2 -0
  10. data/lib/yoti/data_type/image_png.rb +2 -0
  11. data/lib/yoti/data_type/media.rb +19 -0
  12. data/lib/yoti/data_type/signed_time_stamp.rb +1 -1
  13. data/lib/yoti/doc_scan/client.rb +187 -0
  14. data/lib/yoti/doc_scan/constants.rb +31 -0
  15. data/lib/yoti/doc_scan/errors.rb +81 -0
  16. data/lib/yoti/doc_scan/session/create/create_session_result.rb +50 -0
  17. data/lib/yoti/doc_scan/session/create/document_filter.rb +31 -0
  18. data/lib/yoti/doc_scan/session/create/document_restrictions_filter.rb +140 -0
  19. data/lib/yoti/doc_scan/session/create/notification_config.rb +142 -0
  20. data/lib/yoti/doc_scan/session/create/orthogonal_restrictions_filter.rb +150 -0
  21. data/lib/yoti/doc_scan/session/create/requested_check.rb +39 -0
  22. data/lib/yoti/doc_scan/session/create/requested_document_authenticity_check.rb +95 -0
  23. data/lib/yoti/doc_scan/session/create/requested_face_match_check.rb +95 -0
  24. data/lib/yoti/doc_scan/session/create/requested_id_document_comparison_check.rb +53 -0
  25. data/lib/yoti/doc_scan/session/create/requested_liveness_check.rb +108 -0
  26. data/lib/yoti/doc_scan/session/create/requested_task.rb +39 -0
  27. data/lib/yoti/doc_scan/session/create/requested_text_extraction_task.rb +116 -0
  28. data/lib/yoti/doc_scan/session/create/required_document.rb +31 -0
  29. data/lib/yoti/doc_scan/session/create/required_id_document.rb +53 -0
  30. data/lib/yoti/doc_scan/session/create/sdk_config.rb +221 -0
  31. data/lib/yoti/doc_scan/session/create/session_specification.rb +221 -0
  32. data/lib/yoti/doc_scan/session/retrieve/authenticity_check_response.rb +12 -0
  33. data/lib/yoti/doc_scan/session/retrieve/breakdown_response.rb +38 -0
  34. data/lib/yoti/doc_scan/session/retrieve/check_response.rb +63 -0
  35. data/lib/yoti/doc_scan/session/retrieve/details_response.rb +28 -0
  36. data/lib/yoti/doc_scan/session/retrieve/document_fields_response.rb +21 -0
  37. data/lib/yoti/doc_scan/session/retrieve/document_id_photo_response.rb +21 -0
  38. data/lib/yoti/doc_scan/session/retrieve/face_map_response.rb +21 -0
  39. data/lib/yoti/doc_scan/session/retrieve/face_match_check_response.rb +12 -0
  40. data/lib/yoti/doc_scan/session/retrieve/frame_response.rb +21 -0
  41. data/lib/yoti/doc_scan/session/retrieve/generated_check_response.rb +28 -0
  42. data/lib/yoti/doc_scan/session/retrieve/generated_media.rb +28 -0
  43. data/lib/yoti/doc_scan/session/retrieve/generated_text_data_check_response.rb +12 -0
  44. data/lib/yoti/doc_scan/session/retrieve/get_session_result.rb +127 -0
  45. data/lib/yoti/doc_scan/session/retrieve/id_document_comparison_check_response.rb +12 -0
  46. data/lib/yoti/doc_scan/session/retrieve/id_document_resource_response.rb +57 -0
  47. data/lib/yoti/doc_scan/session/retrieve/liveness_check_response.rb +12 -0
  48. data/lib/yoti/doc_scan/session/retrieve/liveness_resource_response.rb +24 -0
  49. data/lib/yoti/doc_scan/session/retrieve/media_response.rb +38 -0
  50. data/lib/yoti/doc_scan/session/retrieve/page_response.rb +27 -0
  51. data/lib/yoti/doc_scan/session/retrieve/recommendation_response.rb +34 -0
  52. data/lib/yoti/doc_scan/session/retrieve/report_response.rb +31 -0
  53. data/lib/yoti/doc_scan/session/retrieve/resource_container.rb +50 -0
  54. data/lib/yoti/doc_scan/session/retrieve/resource_response.rb +39 -0
  55. data/lib/yoti/doc_scan/session/retrieve/task_response.rb +87 -0
  56. data/lib/yoti/doc_scan/session/retrieve/text_data_check_response.rb +12 -0
  57. data/lib/yoti/doc_scan/session/retrieve/text_extraction_task_response.rb +18 -0
  58. data/lib/yoti/doc_scan/session/retrieve/zoom_liveness_resource_response.rb +33 -0
  59. data/lib/yoti/doc_scan/support/supported_documents.rb +60 -0
  60. data/lib/yoti/dynamic_share_service/extension/thirdparty_attribute_extension.rb +62 -11
  61. data/lib/yoti/dynamic_share_service/policy/dynamic_policy.rb +84 -22
  62. data/lib/yoti/dynamic_share_service/share_url.rb +28 -33
  63. data/lib/yoti/errors.rb +15 -2
  64. data/lib/yoti/http/aml_check_request.rb +12 -6
  65. data/lib/yoti/http/payloads/aml_address.rb +4 -0
  66. data/lib/yoti/http/payloads/aml_profile.rb +7 -1
  67. data/lib/yoti/http/profile_request.rb +11 -6
  68. data/lib/yoti/http/request.rb +221 -18
  69. data/lib/yoti/http/signed_request.rb +13 -4
  70. data/lib/yoti/protobuf/main.rb +1 -1
  71. data/lib/yoti/ssl.rb +2 -2
  72. data/lib/yoti/util/anchor_processor.rb +1 -1
  73. data/lib/yoti/util/validation.rb +41 -0
  74. data/lib/yoti/version.rb +1 -1
  75. data/yoti.gemspec +19 -9
  76. metadata +60 -67
  77. data/.github/ISSUE_TEMPLATE.md +0 -17
  78. data/.gitignore +0 -39
  79. data/CONTRIBUTING.md +0 -127
  80. data/Guardfile +0 -11
  81. data/Rakefile +0 -49
  82. data/login_flow.png +0 -0
  83. data/rubocop.yml +0 -57
  84. data/yardstick.yml +0 -9
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoti
4
+ module DocScan
5
+ module Session
6
+ module Retrieve
7
+ class ZoomLivenessResourceResponse < LivenessResourceResponse
8
+ # @return [FaceMapResponse]
9
+ attr_reader :facemap
10
+
11
+ # @return [Array<FrameResponse>]
12
+ attr_reader :frames
13
+
14
+ #
15
+ # @param [Hash] resource
16
+ #
17
+ def initialize(resource)
18
+ super(resource)
19
+
20
+ @facemap = FaceMapResponse.new(resource['facemap']) unless resource['facemap'].nil?
21
+
22
+ if resource['frames'].nil?
23
+ @frames = []
24
+ else
25
+ Validation.assert_is_a(Array, resource['frames'], 'frames')
26
+ @frames = resource['frames'].map { |frame| FrameResponse.new(frame) }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoti
4
+ module DocScan
5
+ module Support
6
+ class SupportedDocumentsResponse
7
+ # @return [Array<SupportedCountry>]
8
+ attr_reader :supported_countries
9
+
10
+ #
11
+ # @param [Hash] response
12
+ #
13
+ def initialize(response)
14
+ if response['supported_countries'].nil?
15
+ @supported_countries = []
16
+ else
17
+ Validation.assert_is_a(Array, response['supported_countries'], 'supported_countries')
18
+ @supported_countries = response['supported_countries'].map { |country| SupportedCountry.new(country) }
19
+ end
20
+ end
21
+ end
22
+
23
+ class SupportedCountry
24
+ # @return [String]
25
+ attr_reader :code
26
+
27
+ # @return [Array<SupportedDocument>]
28
+ attr_reader :supported_documents
29
+
30
+ #
31
+ # @param [Hash] country
32
+ #
33
+ def initialize(country)
34
+ Validation.assert_is_a(String, country['code'], 'code', true)
35
+ @code = country['code']
36
+
37
+ if country['supported_documents'].nil?
38
+ @supported_documents = []
39
+ else
40
+ Validation.assert_is_a(Array, country['supported_documents'], 'supported_documents')
41
+ @supported_documents = country['supported_documents'].map { |document| SupportedDocument.new(document) }
42
+ end
43
+ end
44
+ end
45
+
46
+ class SupportedDocument
47
+ # @return [String]
48
+ attr_reader :type
49
+
50
+ #
51
+ # @param [Hash] document
52
+ #
53
+ def initialize(document)
54
+ Validation.assert_is_a(String, document['type'], 'type', true)
55
+ @type = document['type']
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,14 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'time'
4
+
3
5
  module Yoti
4
6
  module DynamicSharingService
5
7
  class ThirdPartyAttributeDefinition
8
+ #
9
+ # @param [String] name
10
+ #
6
11
  def initialize(name)
7
12
  @name = name
8
13
  end
9
14
 
15
+ def as_json(*_args)
16
+ { name: @name }
17
+ end
18
+
10
19
  def to_json(*_args)
11
- { name: @name }.to_json
20
+ as_json.to_json
12
21
  end
13
22
  end
14
23
 
@@ -18,11 +27,21 @@ module Yoti
18
27
  @definitions = []
19
28
  end
20
29
 
30
+ #
31
+ # @param [DateTime,Time] expiry_date
32
+ #
33
+ # @return [self]
34
+ #
21
35
  def with_expiry_date(expiry_date)
22
36
  @expiry_date = expiry_date
23
37
  self
24
38
  end
25
39
 
40
+ #
41
+ # @param [String] names
42
+ #
43
+ # @return [self]
44
+ #
26
45
  def with_definitions(*names)
27
46
  @definitions += names.map do |name|
28
47
  ThirdPartyAttributeDefinition.new(name)
@@ -30,39 +49,71 @@ module Yoti
30
49
  self
31
50
  end
32
51
 
52
+ #
53
+ # @return [ThirdPartyAttributeExtension]
54
+ #
33
55
  def build
34
- extension = ThirdPartyAttributeExtension.new
35
- extension.instance_variable_get(:@content)[:expiry_date] = @expiry_date
36
- extension.instance_variable_get(:@content)[:definitions] = @definitions
37
- extension
56
+ content = ThirdPartyAttributeExtensionContent.new(@expiry_date, @definitions)
57
+ ThirdPartyAttributeExtension.new(content)
38
58
  end
39
59
  end
40
60
 
41
61
  class ThirdPartyAttributeExtension
42
62
  EXTENSION_TYPE = 'THIRD_PARTY_ATTRIBUTE'
43
63
 
64
+ # @return [ThirdPartyAttributeExtensionContent]
44
65
  attr_reader :content
66
+
67
+ # @return [String]
45
68
  attr_reader :type
46
69
 
47
- def initialize
48
- @content = {}
70
+ #
71
+ # @param [ThirdPartyAttributeExtensionContent] content
72
+ #
73
+ def initialize(content = nil)
74
+ @content = content
49
75
  @type = EXTENSION_TYPE
50
76
  end
51
77
 
52
78
  def as_json(*_args)
53
- {
54
- type: @type,
55
- content: @content
56
- }
79
+ json = {}
80
+ json[:type] = @type
81
+ json[:content] = @content.as_json unless @content.nil?
82
+ json
57
83
  end
58
84
 
59
85
  def to_json(*_args)
60
86
  as_json.to_json
61
87
  end
62
88
 
89
+ #
90
+ # @return [ThirdPartyAttributeExtensionBuilder]
91
+ #
63
92
  def self.builder
64
93
  ThirdPartyAttributeExtensionBuilder.new
65
94
  end
66
95
  end
96
+
97
+ class ThirdPartyAttributeExtensionContent
98
+ #
99
+ # @param [DateTime,Time] expiry_date
100
+ # @param [Array<ThirdPartyAttributeDefinition>] definitions
101
+ #
102
+ def initialize(expiry_date, definitions)
103
+ @expiry_date = expiry_date
104
+ @definitions = definitions
105
+ end
106
+
107
+ def as_json(*_args)
108
+ json = {}
109
+ json[:expiry_date] = @expiry_date.to_time.utc.strftime('%FT%T.%3NZ') unless @expiry_date.nil?
110
+ json[:definitions] = @definitions.map(&:as_json)
111
+ json
112
+ end
113
+
114
+ def to_json(*_args)
115
+ as_json.to_json
116
+ end
117
+ end
67
118
  end
68
119
  end
@@ -16,8 +16,8 @@ module Yoti
16
16
  false
17
17
  end
18
18
 
19
- def to_json(*args)
20
- as_json.to_json(*args)
19
+ def to_json(*_args)
20
+ as_json.to_json
21
21
  end
22
22
 
23
23
  def as_json(*_args)
@@ -93,36 +93,54 @@ module Yoti
93
93
 
94
94
  #
95
95
  # @param [String] name
96
- # @param [Hash] constraints
96
+ # @param [Array<SourceConstraint>] constraints
97
+ # @param [Bool] accept_self_asserted
97
98
  #
98
- def with_wanted_attribute_by_name(name, constraints: nil)
99
+ def with_wanted_attribute_by_name(name, constraints: nil, accept_self_asserted: nil)
99
100
  attribute_builder = WantedAttribute.builder.with_name(name)
100
101
  constraints&.each do |constraint|
101
102
  attribute_builder.with_constraint constraint
102
103
  end
104
+ attribute_builder.with_accept_self_asserted(accept_self_asserted) unless accept_self_asserted.nil?
103
105
  attribute = attribute_builder.build
104
106
  with_wanted_attribute attribute
105
107
  end
106
108
 
109
+ #
110
+ # @option options [Array<SourceConstraint>] :constraints
111
+ # @option options [Bool] :accept_self_asserted
112
+ #
107
113
  def with_family_name(options = {})
108
- with_wanted_attribute_by_name Attribute::FAMILY_NAME, options
114
+ with_wanted_attribute_by_name Attribute::FAMILY_NAME, **options
109
115
  end
110
116
 
117
+ #
118
+ # @option options [Array<SourceConstraint>] :constraints
119
+ # @option options [Bool] :accept_self_asserted
120
+ #
111
121
  def with_given_names(options = {})
112
- with_wanted_attribute_by_name Attribute::GIVEN_NAMES, options
122
+ with_wanted_attribute_by_name Attribute::GIVEN_NAMES, **options
113
123
  end
114
124
 
125
+ #
126
+ # @option options [Array<SourceConstraint>] :constraints
127
+ # @option options [Bool] :accept_self_asserted
128
+ #
115
129
  def with_full_name(options = {})
116
- with_wanted_attribute_by_name Attribute::FULL_NAME, options
130
+ with_wanted_attribute_by_name Attribute::FULL_NAME, **options
117
131
  end
118
132
 
133
+ #
134
+ # @option options [Array<SourceConstraint>] :constraints
135
+ # @option options [Bool] :accept_self_asserted
136
+ #
119
137
  def with_date_of_birth(options = {})
120
- with_wanted_attribute_by_name Attribute::DATE_OF_BIRTH, options
138
+ with_wanted_attribute_by_name Attribute::DATE_OF_BIRTH, **options
121
139
  end
122
140
 
123
141
  #
124
142
  # @param [String] derivation
125
- # @param [Hash] constraints
143
+ # @param [Array<SourceConstraint>] constraints
126
144
  #
127
145
  def with_age_derived_attribute(derivation, constraints: nil)
128
146
  attribute_builder = WantedAttribute.builder
@@ -135,49 +153,93 @@ module Yoti
135
153
  end
136
154
 
137
155
  #
138
- # @param [Integer] derivation
156
+ # @param [Integer] age
157
+ # @option options [Array<SourceConstraint>] :constraints
158
+ # @option options [Bool] :accept_self_asserted
139
159
  #
140
160
  def with_age_over(age, options = {})
141
- with_age_derived_attribute(Attribute::AGE_OVER + age.to_s, options)
161
+ with_age_derived_attribute(Attribute::AGE_OVER + age.to_s, **options)
142
162
  end
143
163
 
144
164
  #
145
- # @param [Integer] derivation
165
+ # @param [Integer] age
166
+ # @option options [Array<SourceConstraint>] :constraints
167
+ # @option options [Bool] :accept_self_asserted
146
168
  #
147
169
  def with_age_under(age, options = {})
148
- with_age_derived_attribute(Attribute::AGE_UNDER + age.to_s, options)
170
+ with_age_derived_attribute(Attribute::AGE_UNDER + age.to_s, **options)
149
171
  end
150
172
 
173
+ #
174
+ # @option options [Array<SourceConstraint>] :constraints
175
+ # @option options [Bool] :accept_self_asserted
176
+ #
151
177
  def with_gender(options = {})
152
- with_wanted_attribute_by_name Attribute::GENDER, options
178
+ with_wanted_attribute_by_name Attribute::GENDER, **options
153
179
  end
154
180
 
181
+ #
182
+ # @option options [Array<SourceConstraint>] :constraints
183
+ # @option options [Bool] :accept_self_asserted
184
+ #
155
185
  def with_postal_address(options = {})
156
- with_wanted_attribute_by_name(Attribute::POSTAL_ADDRESS, options)
186
+ with_wanted_attribute_by_name(Attribute::POSTAL_ADDRESS, **options)
157
187
  end
158
188
 
189
+ #
190
+ # @option options [Array<SourceConstraint>] :constraints
191
+ # @option options [Bool] :accept_self_asserted
192
+ #
159
193
  def with_structured_postal_address(options = {})
160
- with_wanted_attribute_by_name(Attribute::STRUCTURED_POSTAL_ADDRESS, options)
194
+ with_wanted_attribute_by_name(Attribute::STRUCTURED_POSTAL_ADDRESS, **options)
161
195
  end
162
196
 
197
+ #
198
+ # @option options [Array<SourceConstraint>] :constraints
199
+ # @option options [Bool] :accept_self_asserted
200
+ #
163
201
  def with_nationality(options = {})
164
- with_wanted_attribute_by_name(Attribute::NATIONALITY, options)
202
+ with_wanted_attribute_by_name(Attribute::NATIONALITY, **options)
165
203
  end
166
204
 
205
+ #
206
+ # @option options [Array<SourceConstraint>] :constraints
207
+ # @option options [Bool] :accept_self_asserted
208
+ #
167
209
  def with_phone_number(options = {})
168
- with_wanted_attribute_by_name(Attribute::PHONE_NUMBER, options)
210
+ with_wanted_attribute_by_name(Attribute::PHONE_NUMBER, **options)
169
211
  end
170
212
 
213
+ #
214
+ # @option options [Array<SourceConstraint>] :constraints
215
+ # @option options [Bool] :accept_self_asserted
216
+ #
171
217
  def with_selfie(options = {})
172
- with_wanted_attribute_by_name(Attribute::SELFIE, options)
218
+ with_wanted_attribute_by_name(Attribute::SELFIE, **options)
173
219
  end
174
220
 
221
+ #
222
+ # @option options [Array<SourceConstraint>] :constraints
223
+ # @option options [Bool] :accept_self_asserted
224
+ #
175
225
  def with_email(options = {})
176
- with_wanted_attribute_by_name(Attribute::EMAIL_ADDRESS, options)
226
+ with_wanted_attribute_by_name(Attribute::EMAIL_ADDRESS, **options)
177
227
  end
178
228
 
179
- def with_document_details
180
- with_wanted_attribute_by_name(Attribute::DOCUMENT_DETAILS)
229
+ #
230
+ # @option options [Array<SourceConstraint>] :constraints
231
+ # @option options [Bool] :accept_self_asserted
232
+ #
233
+ def with_document_details(options = {})
234
+ with_wanted_attribute_by_name(Attribute::DOCUMENT_DETAILS, **options)
235
+ end
236
+
237
+ #
238
+ # @option options [Array<SourceConstraint>] :constraints
239
+ # @option options [Bool] :accept_self_asserted
240
+ #
241
+ def with_document_images(options = {})
242
+ with_wanted_attribute_by_name(Attribute::DOCUMENT_IMAGES, **options)
181
243
  end
182
244
  end
183
245
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'securerandom'
4
+
3
5
  module Yoti
4
6
  module DynamicSharingService
5
7
  class Share
@@ -11,50 +13,33 @@ module Yoti
11
13
  end
12
14
  end
13
15
 
14
- def self.create_share_url_endpoint
15
- "/qrcodes/apps/#{Yoti.configuration.client_sdk_id}"
16
- end
17
-
18
- def self.create_share_url_query
19
- "?nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
20
- end
21
-
22
16
  def self.create_share_url(scenario)
23
- endpoint = "#{create_share_url_endpoint}#{create_share_url_query}"
24
- uri = URI("#{Yoti.configuration.api_endpoint}#{endpoint}")
25
-
26
- unsigned = Net::HTTP::Post.new uri
27
- unsigned.body = scenario.to_json
17
+ yoti_request = Yoti::Request
18
+ .builder
19
+ .with_http_method('POST')
20
+ .with_base_url(Yoti.configuration.api_endpoint)
21
+ .with_endpoint("qrcodes/apps/#{Yoti.configuration.client_sdk_id}")
22
+ .with_query_param('appId', Yoti.configuration.client_sdk_id)
23
+ .with_payload(scenario)
24
+ .build
28
25
 
29
- signed_request = Yoti::SignedRequest.new(
30
- unsigned,
31
- endpoint,
32
- scenario
33
- ).sign
26
+ begin
27
+ create_share_url_parse_response yoti_request.execute
28
+ rescue Yoti::RequestError => e
29
+ raise if e.response.nil?
34
30
 
35
- response = Net::HTTP.start(
36
- uri.hostname,
37
- uri.port,
38
- use_ssl: true
39
- ) do |http|
40
- http.request signed_request
41
- end
42
-
43
- create_share_url_parse_response response
44
- end
45
-
46
- def self.create_share_url_parse_response(response)
47
- if response.code.to_i < 200 || response.code.to_i >= 300
48
- case response.code
31
+ case e.response.code
49
32
  when '400'
50
33
  raise InvalidDataError
51
34
  when '404'
52
35
  raise ApplicationNotFoundError
53
36
  else
54
- raise UnknownHTTPError, response.code
37
+ raise UnknownHTTPError, e.response.code
55
38
  end
56
39
  end
40
+ end
57
41
 
42
+ def self.create_share_url_parse_response(response)
58
43
  Share.new JSON.parse response.body
59
44
  end
60
45
 
@@ -76,5 +61,15 @@ module Yoti
76
61
  super msg
77
62
  end
78
63
  end
64
+
65
+ # @deprecated no longer used - will be removed in 2.0.0
66
+ def self.create_share_url_query
67
+ "?nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
68
+ end
69
+
70
+ # @deprecated no longer used - will be removed in 2.0.0
71
+ def self.create_share_url_endpoint
72
+ "/qrcodes/apps/#{Yoti.configuration.client_sdk_id}"
73
+ end
79
74
  end
80
75
  end