selfsdk 0.0.197 → 0.0.198

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88cd6714a5c258e5777b5c7ce465c998b9215fb16459e1687282e3695961fb2a
4
- data.tar.gz: b05a49bc266ba95b6a957bea27b31edd5216afeea30dc159ca134b65ecf59261
3
+ metadata.gz: '07875d6dd8a8786b62dbb757976d48e6254e553f1baba1f6e804ac75c20984a3'
4
+ data.tar.gz: f21a0a69b9342b7f8b68b4c288337413183f57ca662e8bf8bd7ea4f2230e9c28
5
5
  SHA512:
6
- metadata.gz: e9bea262aad81d5125dce00ce7afebb05b454eec6e7c26ab8a147ac5177894add5ae827158418100944bd729f8378c59d0296b57a792e688658edf917f2f7b01
7
- data.tar.gz: 2aafa0e3b09648a67e95d662e19694faa9fb56fe90161cb44f045108f5aee004611390e9e702e0b198a0d9516d21e90ec61170a9b79d70f4b66c1a8694b87a3b
6
+ metadata.gz: ce37ab428a4e072f413f635114051c5d8b4fb31b96d27f019a8315d09adaec78f3a1a254bdb9511d4feca73c71287f7eb94bdbc521925ec818f51c948fc1de37
7
+ data.tar.gz: 6140b82f58d59acf6a087f8560d182f82f8e925deb6067688c05133807447ece403871fbd62bf045263e70dcda945731440b5c53ed82e68f6f0d6ebcc7d25f46
@@ -24,7 +24,16 @@ module SelfSDK
24
24
  @expected_value = payload[:expected_value]
25
25
  @operator = payload[:operator]
26
26
  @fact_name = name.to_s
27
- unless payload[name].nil?
27
+ if payload[name].nil?
28
+ return if payload[:facts].nil?
29
+
30
+ payload[:facts].each do |f|
31
+ if f[:key] == name.to_s
32
+ @value = f[:value]
33
+ break
34
+ end
35
+ end
36
+ else
28
37
  @value = payload[name]
29
38
  end
30
39
  end
@@ -0,0 +1,64 @@
1
+ # Copyright 2020 Self Group Ltd. All Rights Reserved.
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'self_msgproto'
6
+ require_relative 'base'
7
+ require_relative '../ntptime'
8
+
9
+ module SelfSDK
10
+ module Messages
11
+ class ConnectionRequest < Base
12
+ MSG_TYPE = "identities.connections.req"
13
+ DEFAULT_EXP_TIMEOUT = 9000
14
+
15
+ attr_accessor :facts, :options, :auth
16
+
17
+ def initialize(messaging)
18
+ @typ = MSG_TYPE
19
+ super
20
+ end
21
+
22
+ def populate(selfid, opts)
23
+ @id = SecureRandom.uuid
24
+ @from = @client.jwt.id
25
+ @to = selfid
26
+ @exp_timeout = opts.fetch(:exp_timeout, DEFAULT_EXP_TIMEOUT)
27
+ end
28
+
29
+ def parse(input, envelope=nil)
30
+ super
31
+ @typ = MSG_TYPE
32
+ @body = @payload[:msg]
33
+ end
34
+
35
+ def body
36
+ {
37
+ typ: MSG_TYPE,
38
+ iss: @jwt.id,
39
+ aud: @to,
40
+ sub: @to,
41
+ iat: SelfSDK::Time.now.strftime('%FT%TZ'),
42
+ exp: (SelfSDK::Time.now + @exp_timeout).strftime('%FT%TZ'),
43
+ jti: SecureRandom.uuid,
44
+ require_confirmation: true,
45
+ }
46
+ end
47
+
48
+ protected
49
+
50
+ def proto(to_device)
51
+ @to_device = to_device
52
+ recipient = "#{@to}:#{@to_device}"
53
+ ciphertext = encrypt_message(@jwt.prepare(body), [{id: @to, device_id: @to_device}])
54
+
55
+ m = SelfMsg::Message.new
56
+ m.id = @id
57
+ m.sender = "#{@jwt.id}:#{@messaging.device_id}"
58
+ m.recipient = recipient
59
+ m.ciphertext = ciphertext
60
+ m
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright 2020 Self Group Ltd. All Rights Reserved.
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'self_msgproto'
6
+ require_relative 'base'
7
+ require_relative '../ntptime'
8
+
9
+ module SelfSDK
10
+ module Messages
11
+ class ConnectionResponse < Base
12
+ MSG_TYPE = "identities.connections.resp"
13
+ DEFAULT_EXP_TIMEOUT = 900
14
+
15
+ attr_accessor :facts, :options, :auth
16
+
17
+ def initialize(messaging)
18
+ @typ = MSG_TYPE
19
+ super
20
+ end
21
+
22
+ def populate(selfid)
23
+ @id = SecureRandom.uuid
24
+ @from = @client.jwt.id
25
+ @to = selfid
26
+ end
27
+
28
+ def parse(input, envelope=nil)
29
+ @typ = MSG_TYPE
30
+ @payload = get_payload input
31
+ @body = @payload[:msg]
32
+ @status = @payload[:status]
33
+ end
34
+
35
+ def get_payload(input)
36
+ body = if input.is_a? String
37
+ input
38
+ else
39
+ input.ciphertext
40
+ end
41
+
42
+ jwt = JSON.parse(body, symbolize_names: true)
43
+ payload = JSON.parse(@jwt.decode(jwt[:payload]), symbolize_names: true)
44
+ header = JSON.parse(@jwt.decode(jwt[:protected]), symbolize_names: true)
45
+ @from = payload[:iss]
46
+ verify! jwt, header[:kid]
47
+ payload
48
+ end
49
+
50
+ end
51
+ end
52
+ end
data/lib/messages/fact.rb CHANGED
@@ -14,11 +14,15 @@ module SelfSDK
14
14
  end
15
15
 
16
16
  def parse(fact)
17
- @name = @messaging.source.normalize_fact_name! fact[:fact]
17
+ @name = @messaging.source.normalize_fact_name fact[:fact]
18
18
  @operator = @messaging.source.normalize_operator!(fact[:operator])
19
19
  @sources = []
20
20
  fact[:sources]&.each do |s|
21
- @sources << @messaging.source.normalize_source!(s)
21
+ @sources << s.to_s
22
+ end
23
+ @issuers = []
24
+ fact[:issuers]&.each do |i|
25
+ @issuers << i.to_s
22
26
  end
23
27
 
24
28
  @expected_value = fact[:expected_value] || ""
@@ -39,6 +43,7 @@ module SelfSDK
39
43
 
40
44
  def to_hash
41
45
  h = { fact: @name }
46
+ h[:issuers] = @issuers if @issuers.length > 0
42
47
  unless @sources.nil?
43
48
  h[:sources] = @sources if @sources.length > 0
44
49
  end
@@ -0,0 +1,94 @@
1
+ # Copyright 2020 Self Group Ltd. All Rights Reserved.
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'self_msgproto'
6
+ require_relative 'base'
7
+ require_relative '../ntptime'
8
+
9
+ module SelfSDK
10
+ module Messages
11
+ class FactIssue < Base
12
+ MSG_TYPE = "identities.facts.issue"
13
+ DEFAULT_EXP_TIMEOUT = 900
14
+
15
+ def initialize(messaging)
16
+ @typ = MSG_TYPE
17
+ super
18
+ end
19
+
20
+ def populate(selfid, facts, opts)
21
+ @id = opts.fetch(:cid, SecureRandom.uuid)
22
+ @exp_timeout = opts.fetch(:exp_timeout, DEFAULT_EXP_TIMEOUT)
23
+ @viewers = opts.fetch(:viewers, nil)
24
+
25
+ @from = @jwt.id
26
+ @to = selfid
27
+ @attestations = build_attestations!(facts)
28
+ end
29
+
30
+ def body
31
+ b = {
32
+ typ: MSG_TYPE,
33
+ iss: @jwt.id,
34
+ aud: @to,
35
+ sub: @to,
36
+ iat: SelfSDK::Time.now.strftime('%FT%TZ'),
37
+ exp: (SelfSDK::Time.now + @exp_timeout).strftime('%FT%TZ'),
38
+ cid: @id,
39
+ jti: SecureRandom.uuid,
40
+ status: 'verified',
41
+ attestations: @attestations
42
+ }
43
+ # viewers
44
+ b[:viewers] = @viewers unless @viewers.nil?
45
+ b
46
+ end
47
+
48
+ protected
49
+
50
+ def proto(to_device)
51
+ @to_device = to_device
52
+ recipient = "#{@to}:#{@to_device}"
53
+ ciphertext = encrypt_message(@jwt.prepare(body), [{id: @to, device_id: @to_device}])
54
+
55
+ m = SelfMsg::Message.new
56
+ m.id = @id
57
+ m.sender = "#{@jwt.id}:#{@messaging.device_id}"
58
+ m.recipient = recipient
59
+ m.ciphertext = ciphertext
60
+ m
61
+ end
62
+
63
+ private
64
+
65
+ def build_attestations!(facts)
66
+ raise 'facts must be provided in the form of an array' unless facts.kind_of?(Array)
67
+
68
+ attestations = []
69
+ facts.each do |fact|
70
+ att = fact.transform_keys(&:to_sym)
71
+ raise 'invalid attestation : does not provide a key' if !att.has_key?(:key) || att[:key].empty?
72
+
73
+ raise 'invalid attestation : does not provide a value' if !att.has_key?(:value) || att[:value].empty?
74
+ att.delete(:source)
75
+
76
+ attestations << sign(fact[:source], att)
77
+ end
78
+
79
+ attestations
80
+ end
81
+
82
+ def sign(source, facts)
83
+ fact = { jti: SecureRandom.uuid,
84
+ sub: @to,
85
+ iss: @from,
86
+ iat: SelfSDK::Time.now.strftime('%FT%TZ'),
87
+ source: source,
88
+ verified: true,
89
+ facts: [ facts ] }
90
+ @client.jwt.signed(fact)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -48,7 +48,7 @@ module SelfSDK
48
48
  end
49
49
 
50
50
  def fact(name)
51
- name = @messaging.source.normalize_fact_name!(name)
51
+ name = @messaging.source.normalize_fact_name(name)
52
52
  @facts.select{|f| f.name == name}.first
53
53
  end
54
54
 
@@ -11,6 +11,7 @@ require_relative "chat_invite"
11
11
  require_relative "chat_join"
12
12
  require_relative "chat_remove"
13
13
  require_relative "document_sign_resp"
14
+ require_relative "connection_response"
14
15
 
15
16
  module SelfSDK
16
17
  module Messages
@@ -55,6 +56,9 @@ module SelfSDK
55
56
  when SelfSDK::Messages::DocumentSignResponse::MSG_TYPE
56
57
  m = DocumentSignResponse.new(messaging)
57
58
  m.parse(body, envelope)
59
+ when SelfSDK::Messages::ConnectionResponse::MSG_TYPE
60
+ m = ConnectionResponse.new(messaging)
61
+ m.parse(body, envelope)
58
62
  else
59
63
  raise StandardError.new("Invalid message type #{payload[:typ]}.")
60
64
  end
data/lib/messaging.rb CHANGED
@@ -430,9 +430,8 @@ module SelfSDK
430
430
  message.validate! @uuid_observer[message.id][:original_message] if @uuid_observer.include? message.id
431
431
  notify_observer(message)
432
432
  end
433
-
434
433
  rescue StandardError => e
435
- p "Error processing incoming message #{input.to_json}"
434
+ p "Error processing incoming message #{e.message}"
436
435
  SelfSDK.logger.info e
437
436
  # p e.backtrace
438
437
  nil
data/lib/services/chat.rb CHANGED
@@ -6,6 +6,8 @@ require 'self_crypto'
6
6
  require_relative '../chat/file_object'
7
7
  require_relative '../chat/group'
8
8
  require_relative '../chat/message'
9
+ require_relative "../messages/connection_request"
10
+
9
11
  module SelfSDK
10
12
  module Services
11
13
  class Chat
@@ -160,6 +162,46 @@ module SelfSDK
160
162
  send(members, typ: "chat.remove", gid: gid )
161
163
  end
162
164
 
165
+ # Generates a connection request in form of QR
166
+ #
167
+ # @opts opts [Integer] :exp_timeout timeout in seconds to expire the request.
168
+ def generate_connection_qr(opts = {})
169
+ req = SelfSDK::Messages::ConnectionRequest.new(@messaging)
170
+ req.populate(@jwt.id, opts)
171
+ body = @jwt.prepare(req.body)
172
+
173
+ ::RQRCode::QRCode.new(body, level: 'l')
174
+ end
175
+
176
+ # Generates a connection request in form of deep link
177
+ #
178
+ # @param callback [String] the url you'll be redirected if the app is not installed.
179
+ # @opts opts [Integer] :exp_timeout timeout in seconds to expire the request.
180
+ def generate_connection_deep_link(callback, opts = {})
181
+ req = SelfSDK::Messages::ConnectionRequest.new(@messaging)
182
+ req.populate(@jwt.id, opts)
183
+ body = @jwt.prepare(req.body)
184
+ body = @jwt.encode(body)
185
+
186
+ env = @messaging.client.client.env
187
+ if env.empty?
188
+ return "https://links.joinself.com/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app"
189
+ elsif env == 'development'
190
+ return "https://links.joinself.com/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.dev"
191
+ end
192
+
193
+ "https://#{env}.links.joinself.com/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.#{env}"
194
+ end
195
+
196
+ # Subscribes to a connection response
197
+ #
198
+ # @yield [request] Invokes the block with a connection response message.
199
+ def on_connection(&block)
200
+ @messaging.subscribe :connection_response do |msg|
201
+ block.call(msg)
202
+ end
203
+ end
204
+
163
205
  private
164
206
 
165
207
  # sends a confirmation for a list of messages to a list of recipients.
@@ -1,13 +1,13 @@
1
1
  # Copyright 2020 Self Group Ltd. All Rights Reserved.
2
2
 
3
3
  # frozen_string_literal: true
4
+ require_relative '../messages/fact_issue.rb'
4
5
 
5
6
  # Namespace for classes and modules that handle SelfSDK gem
6
7
  module SelfSDK
7
8
  # Namespace for classes and modules that handle selfsdk-gem public ui
8
9
  module Services
9
10
  # Self provides this self-hosted verified intermediary.
10
- DEFAULT_INTERMEDIARY = "self_intermediary"
11
11
  # Input class to handle fact requests on self network.
12
12
  class Facts
13
13
  # Creates a new facts service.
@@ -93,6 +93,57 @@ module SelfSDK
93
93
  opts[:auth] = false
94
94
  @requester.generate_deep_link(facts, callback, opts)
95
95
  end
96
+
97
+ # Issues a custom fact and sends it to the user.
98
+ #
99
+ # @param selfid [String] self identifier for the message recipient.
100
+ # @param facts [Array<Fact>] facts to be sent to the user
101
+ # @option opts [String] :viewers list of self identifiers for the user that will have access to this facts.
102
+ def issue(selfid, facts, opts = {})
103
+ hased_facts = []
104
+ facts.each do |f|
105
+ hased_facts << f.to_hash
106
+ end
107
+
108
+ SelfSDK.logger.info "issuing facts for #{selfid}"
109
+ msg = SelfSDK::Messages::FactIssue.new(@requester.messaging)
110
+ msg.populate(selfid, hased_facts, opts)
111
+
112
+ msg.send_message
113
+ end
114
+
115
+ # Facts to be issued
116
+ class Fact
117
+ attr_accessor :key, :value, :group
118
+
119
+ def initialize(key, value, source, group = nil)
120
+ @key = key
121
+ @value = value
122
+ @source = source
123
+ @group = group
124
+ end
125
+
126
+ def to_hash
127
+ b = { key: @key, value: @value, source: @source }
128
+ b[:group] = @group.to_hash unless @group.nil?
129
+ b
130
+ end
131
+ end
132
+
133
+ class Group
134
+ attr_accessor :name, :icon
135
+
136
+ def initialize(name, icon = "")
137
+ @name = name
138
+ @icon = icon
139
+ end
140
+
141
+ def to_hash
142
+ b = { name: @name }
143
+ b[:icon] = @icon unless @icon.empty?
144
+ b
145
+ end
146
+ end
96
147
  end
97
148
  end
98
149
  end
@@ -10,6 +10,8 @@ module SelfSDK
10
10
  DEFAULT_INTERMEDIARY = "self_intermediary"
11
11
  # Input class to handle fact requests on self network.
12
12
  class Requester
13
+ attr_reader :messaging
14
+
13
15
  # Creates a new facts service.
14
16
  # Facts service mainly manages fact requests against self users wanting
15
17
  # to share their verified facts with your app.
@@ -155,7 +157,7 @@ module SelfSDK
155
157
  else
156
158
  { fact: f }
157
159
  end
158
- # validate_fact!(fact)
160
+ validate_fact!(fact) unless fact.key?('issuers')
159
161
  fs << fact
160
162
  end
161
163
  fs
@@ -167,74 +169,14 @@ module SelfSDK
167
169
 
168
170
  raise 'provided fact does not specify a name' if f[:fact].empty?
169
171
  return unless f.has_key? :sources
172
+ return if f.has_key? :issuers # skip the validation if is a custom fact
170
173
 
171
- valid_sources = [SOURCE_USER_SPECIFIED,
172
- SOURCE_PASSPORT,
173
- SOURCE_DRIVING_LICENSE,
174
- SOURCE_IDENTITY_CARD,
175
- SOURCE_TWITTER,
176
- SOURCE_LINKEDIN,
177
- SOURCE_FACEBOK]
178
- fact_for_passport = [FACT_DOCUMENT_NUMBER,
179
- FACT_SURNAME,
180
- FACT_GIVEN_NAMES,
181
- FACT_DATE_OF_BIRTH,
182
- FACT_DATE_OF_EXPIRATION,
183
- FACT_SEX,
184
- FACT_NATIONALITY,
185
- FACT_COUNTRY_OF_ISSUANCE]
186
-
187
- facts_for_dl = [FACT_DOCUMENT_NUMBER,
188
- FACT_SURNAME,
189
- FACT_GIVEN_NAMES,
190
- FACT_DATE_OF_BIRTH,
191
- FACT_DATE_OF_ISSUANCE,
192
- FACT_DATE_OF_EXPIRATION,
193
- FACT_ADDRESS,
194
- FACT_ISSUING_AUTHORITY,
195
- FACT_PLACE_OF_BIRTH,
196
- FACT_COUNTRY_OF_ISSUANCE]
197
-
198
- facts_for_user = [FACT_DOCUMENT_NUMBER,
199
- FACT_DISPLAY_NAME,
200
- FACT_EMAIL,
201
- FACT_PHONE]
202
-
203
- facts_for_twitter = [FACT_ACCOUNT_ID, FACT_NICKNAME]
204
- facts_for_linkedin = [FACT_ACCOUNT_ID, FACT_NICKNAME]
205
- facts_for_facebook = [FACT_ACCOUNT_ID, FACT_NICKNAME]
206
- facts_for_live = [FACT_SELFIE]
174
+ raise "invalid fact '#{f[:fact]}'" unless @messaging.facts.include?(f[:fact])
207
175
 
176
+ spec = @messaging.sources
208
177
  f[:sources].each do |s|
209
- raise errInvalidSource unless valid_sources.include? s.to_s
210
-
211
- if s.to_s == SOURCE_PASSPORT || s.to_s == SOURCE_IDENTITY_CARD
212
- raise errInvalidFactToSource unless fact_for_passport.include? f[:fact]
213
- end
214
-
215
- if s.to_s == SOURCE_DRIVING_LICENSE
216
- raise errInvalidFactToSource unless facts_for_dl.include? f[:fact]
217
- end
218
-
219
- if s.to_s == SOURCE_USER_SPECIFIED
220
- raise errInvalidFactToSource unless facts_for_user.include? f[:fact].to_s
221
- end
222
-
223
- if s.to_s == SOURCE_TWITTER
224
- raise errInvalidFactToSource unless facts_for_twitter.include? f[:fact].to_s
225
- end
226
-
227
- if s.to_s == SOURCE_LINKEDIN
228
- raise errInvalidFactToSource unless facts_for_linkedin.include? f[:fact].to_s
229
- end
230
-
231
- if s.to_s == SOURCE_FACEBOOK
232
- raise errInvalidFactToSource unless facts_for_facebook.include? f[:fact].to_s
233
- end
234
-
235
- if s.to_s == SOURCE_LIVE
236
- raise errInvalidFactToSource unless facts_for_live.include? f[:fact].to_s
237
- end
178
+ raise errInvalidSource unless spec.key?(s)
179
+ raise errInvalidFactToSource unless spec[s].include? f[:fact]
238
180
  end
239
181
  end
240
182
  end
data/lib/sources.rb CHANGED
@@ -6,6 +6,7 @@ require "json"
6
6
  require_relative "source_definition.rb"
7
7
 
8
8
  module SelfSDK
9
+ attr_reader :sources, :facts
9
10
  class Sources
10
11
  def initialize(sources_file)
11
12
  @sources = SOURCE_DATA["sources"]
@@ -15,10 +16,12 @@ module SelfSDK
15
16
  end
16
17
  end
17
18
 
18
- def normalize_fact_name!(fact)
19
- fact = fact.to_s
20
- raise "invalid fact '#{fact}'" unless @facts.include?(fact)
21
- fact
19
+ def normalize_fact_name(fact)
20
+ fact.to_s
21
+ end
22
+
23
+ def normalize_source(source)
24
+ source.to_s
22
25
  end
23
26
 
24
27
  def validate_source!(source)
@@ -47,11 +50,13 @@ module SelfSDK
47
50
  chat_join: SelfSDK::Messages::ChatJoin::MSG_TYPE,
48
51
  chat_remove: SelfSDK::Messages::ChatRemove::MSG_TYPE,
49
52
  document_sign_response: SelfSDK::Messages::DocumentSignResponse::MSG_TYPE,
53
+ connection_response: SelfSDK::Messages::ConnectionResponse::MSG_TYPE,
50
54
  }
51
55
  raise "invalid message type '#{s}'" unless types.key? s
52
56
  return types[s]
53
57
  end
54
58
 
59
+
55
60
  private
56
61
 
57
62
  def get(options, input, option_type)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: selfsdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.197
4
+ version: 0.0.198
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aldgate Ventures
@@ -342,8 +342,11 @@ files:
342
342
  - lib/messages/chat_message_delivered.rb
343
343
  - lib/messages/chat_message_read.rb
344
344
  - lib/messages/chat_remove.rb
345
+ - lib/messages/connection_request.rb
346
+ - lib/messages/connection_response.rb
345
347
  - lib/messages/document_sign_resp.rb
346
348
  - lib/messages/fact.rb
349
+ - lib/messages/fact_issue.rb
347
350
  - lib/messages/fact_request.rb
348
351
  - lib/messages/fact_response.rb
349
352
  - lib/messages/message.rb