yoti 1.5.0 → 1.6.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 (45) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +0 -2
  3. data/README.md +1 -1
  4. data/Rakefile +8 -0
  5. data/lib/yoti.rb +16 -0
  6. data/lib/yoti/activity_details.rb +17 -2
  7. data/lib/yoti/client.rb +2 -1
  8. data/lib/yoti/data_type/age_verification.rb +54 -0
  9. data/lib/yoti/data_type/attribute.rb +3 -0
  10. data/lib/yoti/data_type/base_profile.rb +13 -0
  11. data/lib/yoti/data_type/document_details.rb +96 -0
  12. data/lib/yoti/data_type/profile.rb +76 -0
  13. data/lib/yoti/dynamic_share_service/dynamic_scenario.rb +67 -0
  14. data/lib/yoti/dynamic_share_service/extension/extension.rb +45 -0
  15. data/lib/yoti/dynamic_share_service/extension/location_constraint_extension.rb +88 -0
  16. data/lib/yoti/dynamic_share_service/extension/thirdparty_attribute_extension.rb +58 -0
  17. data/lib/yoti/dynamic_share_service/extension/transactional_flow_extension.rb +47 -0
  18. data/lib/yoti/dynamic_share_service/policy/dynamic_policy.rb +184 -0
  19. data/lib/yoti/dynamic_share_service/policy/source_constraint.rb +88 -0
  20. data/lib/yoti/dynamic_share_service/policy/wanted_anchor.rb +53 -0
  21. data/lib/yoti/dynamic_share_service/policy/wanted_attribute.rb +85 -0
  22. data/lib/yoti/dynamic_share_service/share_url.rb +80 -0
  23. data/lib/yoti/http/profile_request.rb +1 -0
  24. data/lib/yoti/http/request.rb +13 -0
  25. data/lib/yoti/http/signed_request.rb +0 -3
  26. data/lib/yoti/protobuf/main.rb +11 -0
  27. data/lib/yoti/protobuf/sharepubapi/DataEntry_pb.rb +29 -0
  28. data/lib/yoti/protobuf/sharepubapi/ExtraData_pb.rb +19 -0
  29. data/lib/yoti/protobuf/sharepubapi/IssuingAttributes_pb.rb +23 -0
  30. data/lib/yoti/protobuf/sharepubapi/ThirdPartyAttribute_pb.rb +20 -0
  31. data/lib/yoti/sandbox.rb +5 -0
  32. data/lib/yoti/sandbox/anchor.rb +49 -0
  33. data/lib/yoti/sandbox/attribute.rb +52 -0
  34. data/lib/yoti/sandbox/profile.rb +171 -0
  35. data/lib/yoti/sandbox/sandbox.rb +105 -0
  36. data/lib/yoti/sandbox/sandbox_client.rb +45 -0
  37. data/lib/yoti/share/attribute_issuance_details.rb +43 -0
  38. data/lib/yoti/share/extra_data.rb +25 -0
  39. data/lib/yoti/ssl.rb +7 -0
  40. data/lib/yoti/util/age_processor.rb +4 -0
  41. data/lib/yoti/version.rb +1 -1
  42. data/rubocop.yml +4 -0
  43. data/yoti.gemspec +4 -2
  44. metadata +61 -4
  45. data/.travis.yml +0 -17
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sandbox
4
+ # Attribute describes an attribute on a sandbox profile
5
+ class Attribute
6
+ attr_accessor :name
7
+ attr_accessor :value
8
+ attr_accessor :derivation
9
+ attr_accessor :optional
10
+ attr_reader :anchors
11
+
12
+ def initialize(
13
+ name: '',
14
+ value: '',
15
+ derivation: '',
16
+ optional: false,
17
+ anchors: []
18
+ )
19
+ @name = name
20
+ @value = value
21
+ @derivation = derivation
22
+ @optional = optional
23
+ @anchors = anchors
24
+ end
25
+
26
+ def as_json(*_args)
27
+ {
28
+ name: name,
29
+ value: value,
30
+ derivation: derivation,
31
+ optional: optional,
32
+ anchors: anchors
33
+ }
34
+ end
35
+
36
+ def with_anchor(anchor)
37
+ @anchors.push anchor
38
+ self
39
+ end
40
+ end
41
+
42
+ # Helper functions for building derivation strings
43
+ module Derivation
44
+ def self.age_over(age)
45
+ "age_over:#{age}"
46
+ end
47
+
48
+ def self.age_under(age)
49
+ "age_under:#{age}"
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yoti/data_type/attribute'
4
+
5
+ require 'base64'
6
+
7
+ require_relative 'attribute'
8
+
9
+ module Sandbox
10
+ # Builds a user profile on the sandbox instance
11
+ class Profile
12
+ attr_reader :remember_me_id
13
+ attr_reader :attributes
14
+
15
+ def as_json(*_args)
16
+ {
17
+ remember_me_id: remember_me_id,
18
+ profile_attributes: attributes.map(&:as_json)
19
+ }
20
+ end
21
+
22
+ def to_json(*_args)
23
+ as_json.to_json
24
+ end
25
+
26
+ def initialize
27
+ @remember_me_id = ''
28
+ @attributes = []
29
+ end
30
+
31
+ def with_remember_me_id(id)
32
+ @remember_me_id = id
33
+ self
34
+ end
35
+
36
+ def with_attribute(
37
+ name:,
38
+ value:,
39
+ derivation: '',
40
+ optional: false,
41
+ anchors: []
42
+ )
43
+ @attributes.push Sandbox::Attribute.new(
44
+ name: name,
45
+ value: value,
46
+ derivation: derivation,
47
+ optional: optional,
48
+ anchors: anchors
49
+ )
50
+ self
51
+ end
52
+
53
+ def with_given_names(value, optional: false, anchors: [])
54
+ with_attribute(
55
+ name: Yoti::Attribute::GIVEN_NAMES,
56
+ value: value,
57
+ optional: optional,
58
+ anchors: anchors
59
+ )
60
+ end
61
+
62
+ def with_family_name(value, optional: false, anchors: [])
63
+ with_attribute(
64
+ name: Yoti::Attribute::FAMILY_NAME,
65
+ value: value,
66
+ optional: optional,
67
+ anchors: anchors
68
+ )
69
+ end
70
+
71
+ def with_full_name(value, optional: false, anchors: [])
72
+ with_attribute(
73
+ name: Yoti::Attribute::FULL_NAME,
74
+ value: value,
75
+ optional: optional,
76
+ anchors: anchors
77
+ )
78
+ end
79
+
80
+ def with_date_of_birth(date, optional: false, anchors: [])
81
+ with_attribute(
82
+ name: Yoti::Attribute::DATE_OF_BIRTH,
83
+ value: date.to_s,
84
+ optional: optional,
85
+ anchors: anchors
86
+ )
87
+ end
88
+
89
+ def with_age_verification(dob:, derivation:, optional: false, anchors: [])
90
+ with_attribute(
91
+ name: Yoti::Attribute::DATE_OF_BIRTH,
92
+ value: dob.to_s,
93
+ derivation: derivation,
94
+ optional: optional,
95
+ anchors: anchors
96
+ )
97
+ end
98
+
99
+ def with_gender(value, optional: false, anchors: [])
100
+ with_attribute(
101
+ name: Yoti::Attribute::GENDER,
102
+ value: value,
103
+ optional: optional,
104
+ anchors: anchors
105
+ )
106
+ end
107
+
108
+ def with_phone_number(value, optional: false, anchors: [])
109
+ with_attribute(
110
+ name: Yoti::Attribute::PHONE_NUMBER,
111
+ value: value,
112
+ optional: optional,
113
+ anchors: anchors
114
+ )
115
+ end
116
+
117
+ def with_nationality(value, optional: false, anchors: [])
118
+ with_attribute(
119
+ name: Yoti::Attribute::NATIONALITY,
120
+ value: value,
121
+ optional: optional,
122
+ anchors: anchors
123
+ )
124
+ end
125
+
126
+ def with_postal_address(value, optional: false, anchors: [])
127
+ with_attribute(
128
+ name: Yoti::Attribute::POSTAL_ADDRESS,
129
+ value: value,
130
+ optional: optional,
131
+ anchors: anchors
132
+ )
133
+ end
134
+
135
+ def with_structured_postal_address(data, optional: false, anchors: [])
136
+ with_attribute(
137
+ name: Yoti::Attribute::STRUCTURED_POSTAL_ADDRESS,
138
+ value: data.to_json,
139
+ optional: optional,
140
+ anchors: anchors
141
+ )
142
+ end
143
+
144
+ def with_selfie(data, optional: false, anchors: [])
145
+ with_attribute(
146
+ name: Yoti::Attribute::SELFIE,
147
+ value: Base64.strict_encode64(data),
148
+ optional: optional,
149
+ anchors: anchors
150
+ )
151
+ end
152
+
153
+ def with_email_address(value, optional: false, anchors: [])
154
+ with_attribute(
155
+ name: Yoti::Attribute::EMAIL_ADDRESS,
156
+ value: value,
157
+ optional: optional,
158
+ anchors: anchors
159
+ )
160
+ end
161
+
162
+ def with_document_details(value, optional: false, anchors: [])
163
+ with_attribute(
164
+ name: Yoti::Attribute::DOCUMENT_DETAILS,
165
+ value: value,
166
+ optional: optional,
167
+ anchors: anchors
168
+ )
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yoti'
4
+ require 'yoti/configuration'
5
+ require 'yoti/http/signed_request'
6
+
7
+ require_relative 'sandbox_client'
8
+ require_relative 'profile'
9
+ require_relative 'anchor'
10
+ require_relative 'attribute'
11
+
12
+ require 'openssl'
13
+ require 'net/http'
14
+ require 'date'
15
+ require 'securerandom'
16
+
17
+ require 'dotenv'
18
+ Dotenv.load
19
+
20
+ # Singleton for sandbox test resources
21
+ module Sandbox
22
+ class << self
23
+ attr_accessor :sandbox_client
24
+ attr_accessor :dev_key
25
+ attr_accessor :application
26
+ end
27
+
28
+ def self.setup!
29
+ return if application
30
+
31
+ read_dev_key!
32
+ create_application!
33
+ self.sandbox_client = Client.new(
34
+ app_id: application['id'],
35
+ private_key: application['private_key'],
36
+ base_url: ENV['SANDBOX_BASE_URL']
37
+ )
38
+ configure_yoti(
39
+ app_id: sandbox_client.app_id,
40
+ pem: sandbox_client.key.to_pem
41
+ )
42
+ nil
43
+ end
44
+
45
+ def self.configure_yoti(app_id:, pem:)
46
+ Yoti.configuration = Yoti::Configuration.new
47
+ Yoti.configuration.client_sdk_id = app_id
48
+ Yoti.configuration.key = pem
49
+ Yoti.configuration.key_file_path = ''
50
+ Yoti.configuration.api_endpoint = "#{ENV['SANDBOX_BASE_URL']}/"
51
+ Yoti::SSL.reload!
52
+ end
53
+
54
+ def self.create_application_uri
55
+ uri = URI(
56
+ "#{ENV['SANDBOX_BASE_URL']}/#{ENV['SANDBOX_ENDPOINT']}?\
57
+ nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
58
+ )
59
+ uri.port = 11_443
60
+ uri
61
+ end
62
+
63
+ def self.create_application!
64
+ Yoti.configure do |config|
65
+ config.key_file_path = ENV['SANDBOX_KEY']
66
+ config.client_sdk_id = 'DUMMY'
67
+ end
68
+
69
+ uri = create_application_uri
70
+ payload = { name: "Test Run #{DateTime.now.rfc3339}" }
71
+
72
+ response = Net::HTTP.start(
73
+ uri.hostname,
74
+ uri.port,
75
+ use_ssl: true,
76
+ verify_mode: OpenSSL::SSL::VERIFY_NONE
77
+ ) do |http|
78
+ unsigned = Net::HTTP::Post.new uri
79
+ unsigned.body = payload.to_json
80
+ signed_request = Yoti::SignedRequest.new(
81
+ unsigned,
82
+ "#{ENV['SANDBOX_ENDPOINT']}?#{uri.query}",
83
+ payload
84
+ ).sign
85
+ Yoti::Log.logger.info("Creating application #{signed_request.body}")
86
+ http.request signed_request
87
+ end
88
+
89
+ raise "Create application failed #{response.code}: #{response.body}" unless response.code == '201'
90
+
91
+ self.application = JSON.parse(response.body)
92
+ nil
93
+ end
94
+
95
+ def self.read_dev_key!
96
+ self.dev_key = OpenSSL::PKey::RSA.new(
97
+ File.read(ENV['SANDBOX_KEY'], encoding: 'utf-8')
98
+ )
99
+ nil
100
+ end
101
+
102
+ def self.share(profile)
103
+ sandbox_client.setup_sharing_profile profile
104
+ end
105
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sandbox
4
+ # Client is responsible for setting up test data in the sandbox instance
5
+ class Client
6
+ attr_accessor :app_id
7
+ attr_accessor :key
8
+ attr_accessor :base_url
9
+
10
+ def initialize(app_id:, private_key:, base_url:)
11
+ @app_id = app_id
12
+ @base_url = base_url
13
+ @key = OpenSSL::PKey::RSA.new(Base64.decode64(private_key))
14
+ end
15
+
16
+ def setup_sharing_profile(profile)
17
+ endpoint = "/apps/#{app_id}/tokens?\
18
+ nonce=#{SecureRandom.uuid}&timestamp=#{Time.now.to_i}"
19
+ uri = URI(
20
+ "#{@base_url}/#{endpoint}"
21
+ )
22
+
23
+ response = Net::HTTP.start(
24
+ uri.hostname,
25
+ uri.port,
26
+ use_ssl: true,
27
+ verify_mode: OpenSSL::SSL::VERIFY_NONE
28
+ ) do |http|
29
+ unsigned = Net::HTTP::Post.new uri
30
+ unsigned.body = profile.to_json
31
+ signed_request = Yoti::SignedRequest.new(
32
+ unsigned,
33
+ endpoint,
34
+ profile
35
+ ).sign
36
+ http.request signed_request
37
+ end
38
+
39
+ raise "Failed to share profile #{response.code}: #{response.body}" unless response.code == '201'
40
+
41
+ token = JSON.parse(response.body)['token']
42
+ token
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Yoti
6
+ module Share
7
+ class Definition
8
+ attr_reader :name
9
+
10
+ #
11
+ # Constructor
12
+ #
13
+ # @param [String] name
14
+ #
15
+ def initialize(name)
16
+ @name = name
17
+ end
18
+ end
19
+
20
+ class AttributeIssuanceDetails
21
+ attr_reader :token
22
+ attr_reader :attributes
23
+ attr_reader :expiry_date
24
+
25
+ #
26
+ # Constructor
27
+ #
28
+ # @param [Yoti::Protobuf::Sharepubapi::ThirdPartyAttribute] data_entry
29
+ #
30
+ def initialize(data_entry)
31
+ @token = Base64.encode64(data_entry.issuance_token)
32
+ begin
33
+ @expiry_date = DateTime.parse(data_entry.issuing_attributes.expiry_date)
34
+ rescue ArgumentError
35
+ @expiry_date = nil
36
+ end
37
+ @attributes = data_entry.issuing_attributes.definitions.map do |defn|
38
+ Definition.new(defn.name)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yoti
4
+ module Share
5
+ class ExtraData
6
+ attr_reader :attribute_issuance_details
7
+
8
+ #
9
+ # Constructor
10
+ #
11
+ # @param [Yoti::Protobuf::Sharepubapi::ExtraData] proto
12
+ #
13
+ def initialize(proto)
14
+ @attribute_issuance_details = nil
15
+
16
+ proto.list.each do |data_entry|
17
+ if data_entry.type == :THIRD_PARTY_ATTRIBUTE && @attribute_issuance_details.nil?
18
+ attribute = Yoti::Protobuf::Sharepubapi::ThirdPartyAttribute.decode(data_entry.value)
19
+ @attribute_issuance_details = Yoti::Share::AttributeIssuanceDetails.new(attribute)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end