yoti 1.5.0 → 1.6.4

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE.md +17 -0
  3. data/CONTRIBUTING.md +0 -29
  4. data/Gemfile +0 -2
  5. data/README.md +1 -1
  6. data/Rakefile +16 -3
  7. data/lib/yoti.rb +16 -0
  8. data/lib/yoti/activity_details.rb +21 -2
  9. data/lib/yoti/client.rb +2 -1
  10. data/lib/yoti/data_type/age_verification.rb +54 -0
  11. data/lib/yoti/data_type/attribute.rb +3 -0
  12. data/lib/yoti/data_type/base_profile.rb +13 -0
  13. data/lib/yoti/data_type/document_details.rb +88 -0
  14. data/lib/yoti/data_type/profile.rb +76 -0
  15. data/lib/yoti/dynamic_share_service/dynamic_scenario.rb +67 -0
  16. data/lib/yoti/dynamic_share_service/extension/extension.rb +45 -0
  17. data/lib/yoti/dynamic_share_service/extension/location_constraint_extension.rb +88 -0
  18. data/lib/yoti/dynamic_share_service/extension/thirdparty_attribute_extension.rb +119 -0
  19. data/lib/yoti/dynamic_share_service/extension/transactional_flow_extension.rb +47 -0
  20. data/lib/yoti/dynamic_share_service/policy/dynamic_policy.rb +184 -0
  21. data/lib/yoti/dynamic_share_service/policy/source_constraint.rb +88 -0
  22. data/lib/yoti/dynamic_share_service/policy/wanted_anchor.rb +53 -0
  23. data/lib/yoti/dynamic_share_service/policy/wanted_attribute.rb +85 -0
  24. data/lib/yoti/dynamic_share_service/share_url.rb +82 -0
  25. data/lib/yoti/http/profile_request.rb +1 -0
  26. data/lib/yoti/http/request.rb +15 -0
  27. data/lib/yoti/http/signed_request.rb +0 -3
  28. data/lib/yoti/protobuf/main.rb +25 -4
  29. data/lib/yoti/protobuf/sharepubapi/DataEntry_pb.rb +29 -0
  30. data/lib/yoti/protobuf/sharepubapi/ExtraData_pb.rb +19 -0
  31. data/lib/yoti/protobuf/sharepubapi/IssuingAttributes_pb.rb +23 -0
  32. data/lib/yoti/protobuf/sharepubapi/ThirdPartyAttribute_pb.rb +20 -0
  33. data/lib/yoti/share/attribute_issuance_details.rb +43 -0
  34. data/lib/yoti/share/extra_data.rb +25 -0
  35. data/lib/yoti/ssl.rb +8 -0
  36. data/lib/yoti/util/age_processor.rb +4 -0
  37. data/lib/yoti/version.rb +1 -1
  38. data/rubocop.yml +4 -0
  39. data/yoti.gemspec +3 -3
  40. metadata +31 -14
  41. data/.travis.yml +0 -17
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoti
4
+ module DynamicSharingService
5
+ # A list of anchors to require for a dynamic share
6
+ class SourceConstraint
7
+ DRIVING_LICENCE = 'DRIVING_LICENCE'
8
+ PASSPORT = 'PASSPORT'
9
+ NATIONAL_ID = 'NATIONAL_ID'
10
+ PASS_CARD = 'PASS_CARD'
11
+
12
+ SOURCE_CONSTRAINT = 'SOURCE'
13
+
14
+ attr_reader :anchors
15
+
16
+ def soft_preference
17
+ return @soft_preference if @soft_preference
18
+
19
+ false
20
+ end
21
+
22
+ def to_json(*_args)
23
+ as_json.to_json
24
+ end
25
+
26
+ def as_json(*_args)
27
+ obj = {
28
+ type: SOURCE_CONSTRAINT,
29
+ preferred_sources: {
30
+ anchors: @anchors.map(&:as_json)
31
+ }
32
+ }
33
+ obj[:preferred_sources][:soft_preference] = @soft_preference unless @soft_preference.nil?
34
+ obj
35
+ end
36
+
37
+ def initialize
38
+ @anchors = []
39
+ end
40
+
41
+ def self.builder
42
+ SourceConstraintBuilder.new
43
+ end
44
+ end
45
+
46
+ # Builder for SourceConstraint
47
+ class SourceConstraintBuilder
48
+ def initialize
49
+ @constraint = SourceConstraint.new
50
+ end
51
+
52
+ def with_anchor_by_value(value, sub_type)
53
+ anchor = WantedAnchor.builder.with_value(value).with_sub_type(sub_type).build
54
+ with_anchor(anchor)
55
+ end
56
+
57
+ def with_anchor(anchor)
58
+ @constraint.anchors.push(anchor)
59
+ self
60
+ end
61
+
62
+ def with_passport(sub_type = nil)
63
+ with_anchor_by_value(SourceConstraint::PASSPORT, sub_type)
64
+ end
65
+
66
+ def with_driving_licence(sub_type = nil)
67
+ with_anchor_by_value(SourceConstraint::DRIVING_LICENCE, sub_type)
68
+ end
69
+
70
+ def with_national_id(sub_type = nil)
71
+ with_anchor_by_value(SourceConstraint::NATIONAL_ID, sub_type)
72
+ end
73
+
74
+ def with_passcard(sub_type = nil)
75
+ with_anchor_by_value(SourceConstraint::PASS_CARD, sub_type)
76
+ end
77
+
78
+ def with_soft_preference(preference = true)
79
+ @constraint.instance_variable_set(:@soft_preference, preference)
80
+ self
81
+ end
82
+
83
+ def build
84
+ Marshal.load Marshal.dump @constraint
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoti
4
+ module DynamicSharingService
5
+ # A wanted anchor for a source based constraint
6
+ class WantedAnchor
7
+ attr_reader :value
8
+ attr_reader :sub_type
9
+
10
+ def initialize
11
+ @value = ''
12
+ @sub_type = nil
13
+ end
14
+
15
+ def to_json(*_args)
16
+ as_json.to_json
17
+ end
18
+
19
+ def as_json
20
+ obj = {
21
+ name: @value
22
+ }
23
+ obj[:sub_type] = @sub_type if @sub_type
24
+ obj
25
+ end
26
+
27
+ def self.builder
28
+ WantedAnchorBuilder.new
29
+ end
30
+ end
31
+
32
+ # Builder for WantedAnchor
33
+ class WantedAnchorBuilder
34
+ def initialize
35
+ @anchor = WantedAnchor.new
36
+ end
37
+
38
+ def with_value(value)
39
+ @anchor.instance_variable_set(:@value, value)
40
+ self
41
+ end
42
+
43
+ def with_sub_type(sub_type = nil)
44
+ @anchor.instance_variable_set(:@sub_type, sub_type)
45
+ self
46
+ end
47
+
48
+ def build
49
+ Marshal.load Marshal.dump @anchor
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoti
4
+ module DynamicSharingService
5
+ # Describes a wanted attribute in a dynamic sharing policy
6
+ class WantedAttribute
7
+ attr_reader :name
8
+ attr_reader :derivation
9
+ attr_reader :constraints
10
+
11
+ def initialize
12
+ @constraints = []
13
+ end
14
+
15
+ def accept_self_asserted
16
+ return true if @accept_self_asserted
17
+
18
+ false
19
+ end
20
+
21
+ def to_json(*_args)
22
+ as_json.to_json
23
+ end
24
+
25
+ def as_json(*_args)
26
+ obj = {
27
+ name: @name
28
+ }
29
+ obj[:derivation] = @derivation if derivation
30
+ obj[:accept_self_asserted] = @accept_self_asserted if accept_self_asserted
31
+ obj[:constraints] = @constraints.map(&:as_json) unless constraints.empty?
32
+ obj
33
+ end
34
+
35
+ def self.builder
36
+ WantedAttributeBuilder.new
37
+ end
38
+ end
39
+
40
+ # Builder for WantedAttribute
41
+ class WantedAttributeBuilder
42
+ def initialize
43
+ @attribute = WantedAttribute.new
44
+ end
45
+
46
+ #
47
+ # @param [String] name
48
+ #
49
+ def with_name(name)
50
+ @attribute.instance_variable_set(:@name, name)
51
+ self
52
+ end
53
+
54
+ #
55
+ # @param [String] derivation
56
+ #
57
+ def with_derivation(derivation)
58
+ @attribute.instance_variable_set(:@derivation, derivation)
59
+ self
60
+ end
61
+
62
+ #
63
+ # @param constraint Constraint to apply to the requested attribute
64
+ #
65
+ def with_constraint(constraint)
66
+ @attribute.constraints.push(constraint)
67
+ self
68
+ end
69
+
70
+ #
71
+ # @param [Bool] accept
72
+ #
73
+ def with_accept_self_asserted(accept = true)
74
+ @attribute.instance_variable_set(:@accept_self_asserted, accept)
75
+ self
76
+ end
77
+
78
+ def build
79
+ raise 'Attribute name missing' if @attribute.name.nil? || @attribute.name == ''
80
+
81
+ Marshal.load Marshal.dump @attribute
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Yoti
6
+ module DynamicSharingService
7
+ class Share
8
+ attr_reader :share_url, :ref_id
9
+
10
+ def initialize(data)
11
+ @share_url = data['qrcode']
12
+ @ref_id = data['ref_id']
13
+ end
14
+ end
15
+
16
+ def self.create_share_url_endpoint
17
+ "/qrcodes/apps/#{Yoti.configuration.client_sdk_id}"
18
+ end
19
+
20
+ def self.create_share_url_query
21
+ "?nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
22
+ end
23
+
24
+ def self.create_share_url(scenario)
25
+ endpoint = "#{create_share_url_endpoint}#{create_share_url_query}"
26
+ uri = URI("#{Yoti.configuration.api_endpoint}#{endpoint}")
27
+
28
+ unsigned = Net::HTTP::Post.new uri
29
+ unsigned.body = scenario.to_json
30
+
31
+ signed_request = Yoti::SignedRequest.new(
32
+ unsigned,
33
+ endpoint,
34
+ scenario
35
+ ).sign
36
+
37
+ response = Net::HTTP.start(
38
+ uri.hostname,
39
+ uri.port,
40
+ use_ssl: true
41
+ ) do |http|
42
+ http.request signed_request
43
+ end
44
+
45
+ create_share_url_parse_response response
46
+ end
47
+
48
+ def self.create_share_url_parse_response(response)
49
+ if response.code.to_i < 200 || response.code.to_i >= 300
50
+ case response.code
51
+ when '400'
52
+ raise InvalidDataError
53
+ when '404'
54
+ raise ApplicationNotFoundError
55
+ else
56
+ raise UnknownHTTPError, response.code
57
+ end
58
+ end
59
+
60
+ Share.new JSON.parse response.body
61
+ end
62
+
63
+ class InvalidDataError < StandardError
64
+ def initialize(msg = 'JSON is incorrect, contains invalid data')
65
+ super
66
+ end
67
+ end
68
+
69
+ class ApplicationNotFoundError < StandardError
70
+ def initialize(msg = 'Application was not found')
71
+ super
72
+ end
73
+ end
74
+
75
+ class UnknownHTTPError < StandardError
76
+ def initialize(code = nil, msg = 'Unknown HTTP Error')
77
+ msg = "#{msg}: #{code}" unless code.nil?
78
+ super msg
79
+ end
80
+ end
81
+ end
82
+ end
@@ -15,6 +15,7 @@ module Yoti
15
15
 
16
16
  def request
17
17
  yoti_request = Yoti::Request.new
18
+ yoti_request.add_header('X-Yoti-Auth-Key', Yoti::SSL.auth_key_from_pem)
18
19
  yoti_request.encrypted_connect_token = @encrypted_connect_token
19
20
  yoti_request.http_method = 'GET'
20
21
  yoti_request.endpoint = 'profile'
@@ -1,3 +1,5 @@
1
+ require 'securerandom'
2
+
1
3
  module Yoti
2
4
  # Manage the API's HTTPS requests
3
5
  class Request
@@ -14,6 +16,15 @@ module Yoti
14
16
  # @return [Hash] the body sent with the request
15
17
  attr_accessor :payload
16
18
 
19
+ def initialize
20
+ @headers = {}
21
+ end
22
+
23
+ # Adds a HTTP header to the request
24
+ def add_header(header, value)
25
+ @headers[header] = value
26
+ end
27
+
17
28
  # Makes a HTTP request after signing the headers
18
29
  # @return [Hash] the body from the HTTP request
19
30
  def body
@@ -51,6 +62,10 @@ module Yoti
51
62
  raise RequestError, "Request method not allowed: #{@http_method}"
52
63
  end
53
64
 
65
+ @headers.each do |header, value|
66
+ http_req[header] = value
67
+ end
68
+
54
69
  http_req
55
70
  end
56
71
 
@@ -11,12 +11,9 @@ module Yoti
11
11
  end
12
12
 
13
13
  def sign
14
- @http_req['X-Yoti-Auth-Key'] = @auth_key
15
14
  @http_req['X-Yoti-Auth-Digest'] = message_signature
16
15
  @http_req['X-Yoti-SDK'] = Yoti.configuration.sdk_identifier
17
16
  @http_req['X-Yoti-SDK-Version'] = "#{Yoti.configuration.sdk_identifier}-#{Yoti::VERSION}"
18
- @http_req['Content-Type'] = 'application/json'
19
- @http_req['Accept'] = 'application/json'
20
17
  @http_req
21
18
  end
22
19
 
@@ -6,6 +6,9 @@ require 'json'
6
6
  require_relative 'attrpubapi/List_pb.rb'
7
7
  require_relative 'compubapi/EncryptedData_pb.rb'
8
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'
9
12
 
10
13
  module Yoti
11
14
  module Protobuf
@@ -40,12 +43,20 @@ module Yoti
40
43
  decipher_profile(receipt['profile_content'], receipt['wrapped_receipt_key'])
41
44
  end
42
45
 
46
+ def extra_data(receipt)
47
+ return nil if receipt['extra_data_content'].nil? || receipt['extra_data_content'] == ''
48
+
49
+ decipher_extra_data(receipt['extra_data_content'], receipt['wrapped_receipt_key'])
50
+ end
51
+
43
52
  def attribute_list(data)
44
53
  Yoti::Protobuf::Attrpubapi::AttributeList.decode(data)
45
54
  end
46
55
 
47
56
  def value_based_on_attribute_name(value, attr_name)
48
57
  case attr_name
58
+ when Yoti::Attribute::DOCUMENT_DETAILS
59
+ Yoti::DocumentDetails.new(value)
49
60
  when Yoti::Attribute::DOCUMENT_IMAGES
50
61
  raise(TypeError, 'Document Images could not be decoded') unless value.is_a?(Yoti::MultiValue)
51
62
 
@@ -83,7 +94,7 @@ module Yoti
83
94
  proto_multi_value = Yoti::Protobuf::Attrpubapi::MultiValue.decode(value)
84
95
  items = []
85
96
  proto_multi_value.values.each do |item|
86
- items.append value_based_on_content_type(item.data, item.content_type)
97
+ items << value_based_on_content_type(item.data, item.content_type)
87
98
  end
88
99
  MultiValue.new(items)
89
100
  end
@@ -100,11 +111,21 @@ module Yoti
100
111
  end
101
112
 
102
113
  def decipher_profile(profile_content, wrapped_key)
103
- encrypted_data = decode_profile(profile_content)
104
- unwrapped_key = Yoti::SSL.decrypt_token(wrapped_key)
105
- decrypted_data = Yoti::SSL.decipher(unwrapped_key, encrypted_data.iv, encrypted_data.cipher_text)
114
+ decrypted_data = decipher_data(profile_content, wrapped_key)
106
115
  Protobuf.attribute_list(decrypted_data)
107
116
  end
117
+
118
+ def decipher_extra_data(extra_data_content, wrapped_key)
119
+ decrypted_data = decipher_data(extra_data_content, wrapped_key)
120
+ proto = Yoti::Protobuf::Sharepubapi::ExtraData.decode(decrypted_data)
121
+ Share::ExtraData.new(proto)
122
+ end
123
+
124
+ def decipher_data(encrypted_content, wrapped_key)
125
+ encrypted_data = decode_profile(encrypted_content)
126
+ unwrapped_key = Yoti::SSL.decrypt_token(wrapped_key)
127
+ Yoti::SSL.decipher(unwrapped_key, encrypted_data.iv, encrypted_data.cipher_text)
128
+ end
108
129
  end
109
130
  end
110
131
  end
@@ -0,0 +1,29 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: DataEntry.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ Google::Protobuf::DescriptorPool.generated_pool.build do
7
+ add_message "sharepubapi_v1.DataEntry" do
8
+ optional :type, :enum, 1, "sharepubapi_v1.DataEntry.Type"
9
+ optional :value, :bytes, 2
10
+ end
11
+ add_enum "sharepubapi_v1.DataEntry.Type" do
12
+ value :UNDEFINED, 0
13
+ value :INVOICE, 1
14
+ value :PAYMENT_TRANSACTION, 2
15
+ value :LOCATION, 3
16
+ value :TRANSACTION, 4
17
+ value :AGE_VERIFICATION_SECRET, 5
18
+ value :THIRD_PARTY_ATTRIBUTE, 6
19
+ end
20
+ end
21
+
22
+ module Yoti
23
+ module Protobuf
24
+ module Sharepubapi
25
+ DataEntry = Google::Protobuf::DescriptorPool.generated_pool.lookup("sharepubapi_v1.DataEntry").msgclass
26
+ DataEntry::Type = Google::Protobuf::DescriptorPool.generated_pool.lookup("sharepubapi_v1.DataEntry.Type").enummodule
27
+ end
28
+ end
29
+ end