selfsdk 0.0.197 → 0.0.198

Sign up to get free protection for your applications and to get access to all the features.
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