ruby-safenet 0.0.5 → 0.2.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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +35 -14
  3. data/lib/safenet/version.rb +1 -1
  4. data/lib/safenet.rb +509 -454
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 90946ad95b2eb47e5f0e1d89e46e3b826fcb76f5
4
- data.tar.gz: b660969da66807f9cbe228d4a4150bf152b2441b
3
+ metadata.gz: 3da1bbf0bd8ef4702c376d3a285b747da5ebe8d6
4
+ data.tar.gz: 0a2c39886bf3935eb36f7bcd11ddffeabcb4e223
5
5
  SHA512:
6
- metadata.gz: 171fcca1cb7dcf7f31d2760c70f19b3de3cba2d4d96abd4ecf47d1257419e1c4dad809b6886181c8796ab55d6d309cb60251b1a1a760ae5f9c42cd4efecde472
7
- data.tar.gz: b7034064dd83d23b485634306cdc89ace5713796e163d24d3a024026bab323736aaa27459f4f9186732665780b21eae855247309594d9c4b4282232c28d76113
6
+ metadata.gz: ed01af92d3c24a9330a176339af88f1519da6dc4b5aa5be1fc84b23aaff6c558a329bee7ec2259f9132ffadfb1bff74deaf76935b117f5321a53cd20b09f1f0b
7
+ data.tar.gz: 5c672bfa76fec86cb219b619f0f315f259c99c47cd74d9386291963f45296fb1db72a07f22dabca81748d73019659708c30d03fb4ede0e0bef714b0242c9b980
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ruby-Safenet
2
2
 
3
- A Ruby library for accessing the SAFE network
3
+ A simple SAFE API wrapper written in Ruby.
4
4
 
5
5
  ## Installation
6
6
  ```
@@ -12,37 +12,58 @@ A Ruby library for accessing the SAFE network
12
12
  ```ruby
13
13
  require "safenet"
14
14
 
15
- SafeNet.set_app_info({
15
+ my_client = SafeNet::Client.new
16
+ my_client.nfs.create_directory("/mydir", is_private: false)
17
+ my_client.nfs.file("/mydir/index.html", is_private: false)
18
+ my_client.nfs.update_file_content("/mydir/index.html", "Hello world!<br>I'm a webpage :D")
19
+ my_client.dns.register_service("my-wonderful-app", "www", "/mydir")
20
+ my_client.dns.get_file("/mydir/index.html")
21
+
22
+ # Then, open http://www.my-wonderful-app.safenet/
23
+ ```
24
+
25
+ You can also set a more detailed App info:
26
+ ```
27
+ my_client = SafeNet::Client.new({
16
28
  name: "Ruby Demo App",
17
29
  version: "0.0.1",
18
30
  vendor: "Vendor's Name",
19
31
  id: "org.thevendor.demo",
20
32
  })
21
-
22
- SafeNet.create_directory("/mydir", is_private: false)
23
- SafeNet.file("/mydir/index.html", is_private: false)
24
- SafeNet.update_file_content("/mydir/index.html", "Hello world!<br>I'm a webpage :D")
25
- SafeNet.register_service("my-wonderful-app", "www", "/mydir")
26
- SafeNet.get_file("/mydir/index.html")
27
-
28
- # Then, open http://www.my-wonderful-app.safenet/
29
33
  ```
30
34
 
31
35
  *File Upload / Download:*
32
36
  ```ruby
33
37
  # upload
34
- SafeNet.file("/mydir/dog.jpg")
35
- SafeNet.update_file_content("/mydir/dog.jpg", File.read("/home/daniel/Pictures/dog.jpg"))
38
+ my_client.nfs.file("/mydir/dog.jpg")
39
+ my_client.nfs.update_file_content("/mydir/dog.jpg", File.read("/home/daniel/Pictures/dog.jpg"))
36
40
 
37
41
  # download
38
42
  File.open("/home/daniel/Pictures/dog-new.jpg", "w") do |file|
39
- file.write(SafeNet.get_file("/mydir/dog.jpg"))
43
+ file.write(my_client.nfs.get_file("/mydir/dog.jpg"))
40
44
  end
41
45
  ```
42
46
 
43
47
  *Directory's file list:*
44
48
  ```ruby
45
- SafeNet.get_directory("/mydir")["files"].each do |file|
49
+ my_client.nfs.get_directory("/mydir")["files"].each do |file|
46
50
  puts file["name"]
47
51
  end
48
52
  ```
53
+
54
+ ## Supported methods:
55
+ |Module|Method|Arguments|Optional|Doc|
56
+ |------|------|---------|--------|---|
57
+ |nfs|create_directory|dir_path|is_private, is_versioned, is_path_shared|Reference: https://maidsafe.readme.io/docs/nfs-create-directory|
58
+ |nfs|get_directory|dir_path|is_path_shared|https://maidsafe.readme.io/docs/nfs-get-directory|
59
+ |nfs|delete_directory|dir_path|is_path_shared|https://maidsafe.readme.io/docs/nfs-create-directory|
60
+ |nfs|file|file_path|is_private, is_versioned, is_path_shared|https://maidsafe.readme.io/docs/nfsfile|
61
+ |nfs|get_file|file_path|is_path_shared, offset, length|https://maidsafe.readme.io/docs/nfs-get-file|
62
+ |nfs|update_file_content|file_path, contents|is_path_shared, offset|https://maidsafe.readme.io/docs/nfs-update-file-content|
63
+ |nfs|delete_file|file_path|is_path_shared|https://maidsafe.readme.io/docs/nfs-delete-file|
64
+ |dns|create_long_name|long_name||https://maidsafe.readme.io/docs/dns-create-long-name|
65
+ |dns|register_service|long_name, service_name, service_home_dir_path|is_path_shared, metadata|https://maidsafe.readme.io/docs/dns-register-service|
66
+ |dns|list_long_names||is_path_shared, metadata|https://maidsafe.readme.io/docs/dns-list-long-names|
67
+ |dns|list_services|long_name||https://maidsafe.readme.io/docs/dns-list-services|
68
+ |dns|get_home_dir|long_name, service_name||https://maidsafe.readme.io/docs/dns-get-home-dir|
69
+ |dns|get_file_unauth|long_name, service_name, file_path|offset, length|https://maidsafe.readme.io/docs/dns-get-file-unauth|
@@ -1,3 +1,3 @@
1
1
  module Safenet
2
- VERSION = "0.0.5"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/safenet.rb CHANGED
@@ -1,514 +1,569 @@
1
1
  require "safenet/version"
2
-
2
+ require "net/http"
3
+ require "rbnacl"
4
+ require "base64"
5
+ require "json"
6
+ require "cgi" # CGI.escape method
7
+
8
+ # usage:
9
+ # my_client = SafeNet::Client.new(name: 'My App')
10
+ # my_client.nfs.file('x.txt')
11
+ # my_client.nfs.update_file_content('x.txt', 'Hello World!')
12
+ # my_client.nfs.get_file('x.txt')
3
13
  module SafeNet
4
- require "net/http"
5
- require "rbnacl"
6
- require "base64"
7
- require "json"
8
- require "cgi" # CGI.escape method
9
-
10
- # default values
11
- @@NAME = "Ruby Demo App"
12
- @@VERSION = "0.0.1"
13
- @@VENDOR = "Vendor's Name"
14
- @@ID = "org.thevendor.demo"
15
- @@LAUNCHER_SERVER = "http://localhost:8100/"
16
- @@CONF_PATH = File.join(File.expand_path('..', __FILE__), "conf.json")
17
-
18
- def self.set_app_info(options)
19
- @@NAME = options[:name] if options.has_key?(:name)
20
- @@VERSION = options[:version] if options.has_key?(:version)
21
- @@VENDOR = options[:vendor] if options.has_key?(:vendor)
22
- @@ID = options[:id] if options.has_key?(:id)
23
- @@LAUNCHER_SERVER = options[:server] if options.has_key?(:server)
24
- @@CONF_PATH = options[:conf_file] if options.has_key?(:conf_file)
25
- end
26
14
 
27
- #
28
- # An application exchanges data with the SAFE Launcher using symmetric key
29
- # encryption. The symmetric key is session based and is securely transferred
30
- # from the SAFE Launcher to the application using ECDH Key Exchange.
31
- # Applications will generate an asymmetric key pair and a nonce for ECDH Key
32
- # Exchange with the SAFE Launcher.
33
- #
34
- # The application will initiate the authorisation request with the generated
35
- # nonce and public key, along with information about the application and the
36
- # required permissions.
37
- #
38
- # The SAFE Launcher will prompt to the user with the application information
39
- # along with the requested permissions. Once the user authorises the
40
- # request, the symmetric keys for encryption are received. If the user
41
- # denies the request then the SAFE Launcher sends an unauthorised error
42
- # response.
43
- #
44
- # Usage: SafeNet.auth()
45
- # Fail: nil
46
- # Success: {token: "1222", encryptedKey: "232", "publicKey": "4323", "permissions": []}
47
- #
48
- # Reference: https://maidsafe.readme.io/docs/auth
49
- #
50
- def self.auth
51
- # entry point
52
- url = "#{@@LAUNCHER_SERVER}auth"
53
-
54
- # new random key
55
- private_key = RbNaCl::PrivateKey.generate
56
- nonce = RbNaCl::Random.random_bytes(24)
57
-
58
- # payload
59
- payload = {
60
- app: {
61
- name: @@NAME,
62
- version: @@VERSION,
63
- vendor: @@VENDOR,
64
- id: @@ID
65
- },
66
- publicKey: Base64.strict_encode64(private_key.public_key),
67
- nonce: Base64.strict_encode64(nonce),
68
- permissions: []
69
- }
70
-
71
- # api call
72
- uri = URI(url)
73
- http = Net::HTTP.new(uri.host, uri.port)
74
- req = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/json'})
75
- req.body = payload.to_json
76
- res = http.request(req)
77
-
78
- # return's parser
79
- if res.code == "200"
80
- response = JSON.parse(res.body)
81
-
82
- # save it in conf.json
83
- conf = response.dup
84
- conf["nonce"] = Base64.strict_encode64(nonce)
85
- conf["privateKey"] = Base64.strict_encode64(private_key)
86
- File.open(@@CONF_PATH, "w") { |f| f << JSON.pretty_generate(conf) }
87
-
88
- # invalidates @@keys
89
- @@keys = {}
90
- else
91
- # puts "ERROR #{res.code}: #{res.message}"
92
- response = nil
15
+ class Client
16
+ attr_reader :auth, :nfs, :dns, :app_info, :key_helper
17
+
18
+ def initialize(options = {})
19
+ @app_info = defaults()
20
+ set_app_info(options) if options.any?
21
+ @key_helper = SafeNet::KeyHelper.new(self)
22
+ @auth = SafeNet::Auth.new(self)
23
+ @nfs = SafeNet::NFS.new(self)
24
+ @dns = SafeNet::DNS.new(self)
93
25
  end
94
26
 
95
- # return
96
- response
97
- end
27
+ def set_app_info(options = {})
28
+ @app_info[:name] = options[:name] if options.has_key?(:name)
29
+ @app_info[:version] = options[:version] if options.has_key?(:version)
30
+ @app_info[:vendor] = options[:vendor] if options.has_key?(:vendor)
31
+ @app_info[:id] = options[:id] if options.has_key?(:id)
32
+ @app_info[:launcher_server] = options[:server] if options.has_key?(:server)
33
+ @app_info[:conf_path] = options[:conf_file] if options.has_key?(:conf_file)
34
+ end
98
35
 
36
+ private
99
37
 
100
- #
101
- # To check whether the authorisation token obtained is valid.
102
- # The Authorization header must be present in the request.
103
- #
104
- # Usage: SafeNet.is_token_valid()
105
- # Fail: false
106
- # Success: true
107
- #
108
- # Reference: https://maidsafe.readme.io/docs/is-token-valid
109
- #
110
- def self.is_token_valid
111
- # entry point
112
- url = "#{@@LAUNCHER_SERVER}auth"
113
-
114
- # api call
115
- uri = URI(url)
116
- http = Net::HTTP.new(uri.host, uri.port)
117
- req = Net::HTTP::Get.new(uri.path, {
118
- 'Authorization' => "Bearer #{self.get_token()}"
119
- })
120
- res = http.request(req)
121
- res.code == "200"
38
+ # default values
39
+ def defaults
40
+ {
41
+ name: "Ruby Demo App",
42
+ version: "0.0.1",
43
+ vendor: "Vendor's Name",
44
+ id: "org.thevendor.demo",
45
+ launcher_server: "http://localhost:8100/",
46
+ conf_path: File.join(File.expand_path('..', __FILE__), "conf.json")
47
+ }
48
+ end
122
49
  end
123
50
 
51
+ class KeyHelper
124
52
 
125
- #
126
- # Removes the token from the SAFE Launcher.
127
- #
128
- # Usage: SafeNet.revoke_token()
129
- # Fail: false
130
- # Success: true
131
- #
132
- # Reference: https://maidsafe.readme.io/docs/revoke-token
133
- #
134
- def self.revoke_token
135
- # entry point
136
- url = "#{@@LAUNCHER_SERVER}auth"
137
-
138
- # api call
139
- uri = URI(url)
140
- http = Net::HTTP.new(uri.host, uri.port)
141
- req = Net::HTTP::Delete.new(uri.path, {
142
- 'Authorization' => "Bearer #{self.get_valid_token()}"
143
- })
144
- res = http.request(req)
145
- res.code == "200"
146
- end
53
+ def initialize(client_obj)
54
+ @client = client_obj
55
+ @keys = {}
56
+ @conf = {}
57
+ end
147
58
 
59
+ def get_token
60
+ @conf = File.exists?(@client.app_info[:conf_path]) ? JSON.parse(File.read(@client.app_info[:conf_path])) : (@client.auth.auth() || {})
61
+ @conf["token"]
62
+ end
148
63
 
149
- #
150
- # Create a directory using the NFS API.
151
- # Only authorised requests can create a directory.
152
- #
153
- # Usage: SafeNet.get_directory("/photos", is_path_shared: false)
154
- # Fail: {"errorCode"=>-1502, "description"=>"FfiError::PathNotFound"}
155
- # Success: {"info"=> {"name"=> "Ruby Demo App-Root-Dir", ...}, ...}
156
- #
157
- # Reference: https://maidsafe.readme.io/docs/nfs-create-directory
158
- #
159
- def self.get_directory(dir_path, options = {})
160
- # default values
161
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
162
-
163
- # entry point
164
- url = "#{@@LAUNCHER_SERVER}nfs/directory/#{CGI.escape(dir_path)}/#{options[:is_path_shared]}"
165
-
166
- # api call
167
- uri = URI(url)
168
- http = Net::HTTP.new(uri.host, uri.port)
169
- req = Net::HTTP::Get.new(uri.path, {
170
- 'Authorization' => "Bearer #{self.get_valid_token()}",
171
- })
172
- res = http.request(req)
173
- JSON.parse(self.decrypt(res.body))
174
- end
64
+ def get_valid_token
65
+ @last_conf = File.exists?(@client.app_info[:conf_path]) ? JSON.parse(File.read(@client.app_info[:conf_path])) : {}
66
+ @client.auth.auth() unless File.exists?(@client.app_info[:conf_path]) && @client.auth.is_token_valid()
67
+ @conf = JSON.parse(File.read(@client.app_info[:conf_path]))
68
+ @conf["token"]
69
+ end
175
70
 
71
+ def decrypt(message_base64)
72
+ keys = get_keys()
73
+ keys["secret_box"].decrypt(keys["symmetric_nonce"], Base64.strict_decode64(message_base64))
74
+ end
176
75
 
177
- #
178
- # Create a File using the NFS API.
179
- # Only authorised requests can invoke the API.
180
- #
181
- # Usage: SafeNet.file("/photos/cat.jpg")
182
- # Adv.Usage: SafeNet.file("/photos/cat.jpg", is_private: true, metadata: "some meta", is_path_shared: false, is_versioned: false)
183
- # Fail: {"errorCode"=>-505, "description"=>"NfsError::FileAlreadyExistsWithSameName"}
184
- # Success: true
185
- #
186
- # Reference: https://maidsafe.readme.io/docs/nfsfile
187
- #
188
- def self.file(file_path, options = {})
189
- url = "#{@@LAUNCHER_SERVER}nfs/file"
76
+ def encrypt(message)
77
+ keys = get_keys()
78
+ res = keys["secret_box"].encrypt(keys["symmetric_nonce"], message)
79
+ Base64.strict_encode64(res)
80
+ end
190
81
 
191
- # default values
192
- options[:is_private] = true if ! options.has_key?(:is_private)
193
- options[:is_versioned] = false if ! options.has_key?(:is_versioned)
194
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
195
-
196
- # payload
197
- payload = {
198
- filePath: file_path,
199
- isPrivate: options[:is_private],
200
- isVersioned: options[:is_versioned],
201
- isPathShared: options[:is_path_shared]
202
- }
203
-
204
- # optional
205
- payload["metadata"] = Base64.strict_encode64(options[:metadata]) if options.has_key?(:metadata)
206
-
207
- # api call
208
- uri = URI(url)
209
- http = Net::HTTP.new(uri.host, uri.port)
210
- req = Net::HTTP::Post.new(uri.path, {
211
- 'Authorization' => "Bearer #{self.get_valid_token()}",
212
- 'Content-Type' => 'text/plain'
213
- })
214
- req.body = self.encrypt(payload.to_json)
215
- res = http.request(req)
216
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
217
- end
82
+ def invalidate
83
+ @keys = {}
84
+ end
218
85
 
86
+ private
219
87
 
220
- #
221
- # Create a directory using the NFS API.
222
- # Only authorised requests can create a directory.
223
- #
224
- # Usage: SafeNet.create_directory("/photos")
225
- # Adv.Usage: SafeNet.create_directory("/photos", is_private: true, metadata: "some meta", is_path_shared: false, is_versioned: false)
226
- # Fail: {"errorCode"=>-505, "description"=>"NfsError::FileAlreadyExistsWithSameName"}
227
- # Success: true
228
- #
229
- # Reference: https://maidsafe.readme.io/docs/nfs-create-directory
230
- #
231
- def self.create_directory(dir_path, options = {})
232
- # entry point
233
- url = "#{@@LAUNCHER_SERVER}nfs/directory"
88
+ def get_keys
89
+ # not loaded yet?
90
+ if @keys.empty?
91
+ get_valid_token() if @conf.empty?
234
92
 
235
- # default values
236
- options[:is_private] = true if ! options.has_key?(:is_private)
237
- options[:is_versioned] = false if ! options.has_key?(:is_versioned)
238
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
239
-
240
- # payload
241
- payload = {
242
- dirPath: dir_path,
243
- isPrivate: options[:is_private],
244
- isVersioned: options[:is_versioned],
245
- isPathShared: options[:is_path_shared]
246
- }
247
-
248
- # optional
249
- payload["metadata"] = Base64.strict_encode64(options[:metadata]) if options.has_key?(:metadata)
250
-
251
- # api call
252
- uri = URI(url)
253
- http = Net::HTTP.new(uri.host, uri.port)
254
- req = Net::HTTP::Post.new(uri.path, {
255
- 'Authorization' => "Bearer #{self.get_valid_token()}",
256
- 'Content-Type' => 'text/plain'
257
- })
258
- req.body = self.encrypt(payload.to_json)
259
- res = http.request(req)
260
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
261
- end
93
+ # extract keys
94
+ cipher_text = Base64.strict_decode64(@conf["encryptedKey"])
95
+ nonce = Base64.strict_decode64(@conf["nonce"])
96
+ private_key = Base64.strict_decode64(@conf["privateKey"])
97
+ public_key = Base64.strict_decode64(@conf["publicKey"])
262
98
 
99
+ box = RbNaCl::Box.new(public_key, private_key)
100
+ data = box.decrypt(nonce, cipher_text)
263
101
 
264
- # ex.: delete_directory("/photos")
265
- def self.delete_directory(dir_path, options = {})
266
- # default values
267
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
268
-
269
- # entry point
270
- url = "#{@@LAUNCHER_SERVER}nfs/directory/#{CGI.escape(dir_path)}/#{options[:is_path_shared]}"
271
-
272
- # api call
273
- uri = URI(url)
274
- http = Net::HTTP.new(uri.host, uri.port)
275
- req = Net::HTTP::Delete.new(uri.path, {
276
- 'Authorization' => "Bearer #{self.get_valid_token()}",
277
- })
278
- res = http.request(req)
279
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
280
- end
102
+ # The first segment of the data will have the symmetric key
103
+ @keys["symmetric_key"] = data.slice(0, RbNaCl::SecretBox.key_bytes)
281
104
 
282
- # options: offset, length, is_path_shared
283
- def self.get_file(file_path, options = {})
284
- # default values
285
- options[:offset] = 0 if ! options.has_key?(:offset)
286
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
287
-
288
- # entry point
289
- url = "#{@@LAUNCHER_SERVER}nfs/file/#{CGI.escape(file_path)}/#{options[:is_path_shared]}?"
290
-
291
- # query params are encrypted
292
- query = []
293
- query << "offset=#{options[:offset]}"
294
- query << "length=#{options[:length]}" if options.has_key?(:length) # length is optional
295
- url = "#{url}#{self.encrypt(query.join('&'))}"
296
-
297
- # api call
298
- uri = URI(url)
299
- http = Net::HTTP.new(uri.host, uri.port)
300
- req = Net::HTTP::Get.new(uri.path, {
301
- 'Authorization' => "Bearer #{self.get_valid_token()}",
302
- })
303
- res = http.request(req)
304
- res.code == "200" ? self.decrypt(res.body) : JSON.parse(self.decrypt(res.body))
305
- end
105
+ # The second segment of the data will have the nonce to be used
106
+ @keys["symmetric_nonce"] = data.slice(RbNaCl::SecretBox.key_bytes, RbNaCl::SecretBox.key_bytes)
306
107
 
307
- def self.update_file_content(file_path, contents, options = {})
308
- # default values
309
- options[:offset] = 0 if ! options.has_key?(:offset)
310
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
311
-
312
- # entry point
313
- url = "#{@@LAUNCHER_SERVER}nfs/file/#{CGI.escape(file_path)}/#{options[:is_path_shared]}?offset=#{options[:offset]}"
314
-
315
- # api call
316
- uri = URI(url)
317
- http = Net::HTTP.new(uri.host, uri.port)
318
- req = Net::HTTP::Put.new(uri.path, {
319
- 'Authorization' => "Bearer #{self.get_valid_token()}",
320
- 'Content-Type' => 'text/plain'
321
- })
322
- req.body = self.encrypt(contents)
323
- res = http.request(req)
324
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
325
- end
108
+ # keep the box object in cache
109
+ @keys["secret_box"] = RbNaCl::SecretBox.new(@keys["symmetric_key"])
110
+ end
111
+
112
+ @keys
113
+ end
326
114
 
327
- def self.delete_file(file_path, options = {})
328
- # default values
329
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
330
-
331
- # entry point
332
- url = "#{@@LAUNCHER_SERVER}nfs/file/#{CGI.escape(file_path)}/#{options[:is_path_shared]}"
333
-
334
- # api call
335
- uri = URI(url)
336
- http = Net::HTTP.new(uri.host, uri.port)
337
- req = Net::HTTP::Delete.new(uri.path, {
338
- 'Authorization' => "Bearer #{self.get_valid_token()}",
339
- })
340
- res = http.request(req)
341
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
342
115
  end
343
116
 
117
+ class Auth
118
+ def initialize(client_obj)
119
+ @client = client_obj
120
+ end
344
121
 
345
- def self.create_long_name(long_name)
346
- # entry point
347
- url = "#{@@LAUNCHER_SERVER}dns/#{CGI.escape(long_name)}"
122
+ #
123
+ # An application exchanges data with the SAFE Launcher using symmetric key
124
+ # encryption. The symmetric key is session based and is securely transferred
125
+ # from the SAFE Launcher to the application using ECDH Key Exchange.
126
+ # Applications will generate an asymmetric key pair and a nonce for ECDH Key
127
+ # Exchange with the SAFE Launcher.
128
+ #
129
+ # The application will initiate the authorisation request with the generated
130
+ # nonce and public key, along with information about the application and the
131
+ # required permissions.
132
+ #
133
+ # The SAFE Launcher will prompt to the user with the application information
134
+ # along with the requested permissions. Once the user authorises the
135
+ # request, the symmetric keys for encryption are received. If the user
136
+ # denies the request then the SAFE Launcher sends an unauthorised error
137
+ # response.
138
+ #
139
+ # Usage: my_client.auth()
140
+ # Fail: nil
141
+ # Success: {token: "1222", encryptedKey: "232", "publicKey": "4323", "permissions": []}
142
+ #
143
+ # Reference: https://maidsafe.readme.io/docs/auth
144
+ #
145
+ def auth
146
+ # entry point
147
+ url = "#{@client.app_info[:launcher_server]}auth"
148
+
149
+ # new random key
150
+ private_key = RbNaCl::PrivateKey.generate
151
+ nonce = RbNaCl::Random.random_bytes(24)
152
+
153
+ # payload
154
+ payload = {
155
+ app: {
156
+ name: @client.app_info[:name],
157
+ version: @client.app_info[:version],
158
+ vendor: @client.app_info[:vendor],
159
+ id: @client.app_info[:id]
160
+ },
161
+ publicKey: Base64.strict_encode64(private_key.public_key),
162
+ nonce: Base64.strict_encode64(nonce),
163
+ permissions: []
164
+ }
165
+
166
+ # api call
167
+ uri = URI(url)
168
+ http = Net::HTTP.new(uri.host, uri.port)
169
+ req = Net::HTTP::Post.new(uri.path, {'Content-Type' => 'application/json'})
170
+ req.body = payload.to_json
171
+ res = http.request(req)
172
+
173
+ # return's parser
174
+ if res.code == "200"
175
+ response = JSON.parse(res.body)
176
+
177
+ # save it in conf.json
178
+ conf = response.dup
179
+ conf["nonce"] = Base64.strict_encode64(nonce)
180
+ conf["privateKey"] = Base64.strict_encode64(private_key)
181
+ File.open(@client.app_info[:conf_path], "w") { |f| f << JSON.pretty_generate(conf) }
182
+
183
+ # invalidates @keys
184
+ @client.key_helper.invalidate()
185
+ else
186
+ # puts "ERROR #{res.code}: #{res.message}"
187
+ response = nil
188
+ end
189
+
190
+ # return
191
+ response
192
+ end
348
193
 
349
- # api call
350
- uri = URI(url)
351
- http = Net::HTTP.new(uri.host, uri.port)
352
- req = Net::HTTP::Post.new(uri.path, {
353
- 'Authorization' => "Bearer #{self.get_valid_token()}",
354
- 'Content-Type' => 'text/plain'
355
- })
356
- res = http.request(req)
357
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
358
- end
359
194
 
195
+ #
196
+ # To check whether the authorisation token obtained is valid.
197
+ # The Authorization header must be present in the request.
198
+ #
199
+ # Usage: SafeNet.is_token_valid()
200
+ # Fail: false
201
+ # Success: true
202
+ #
203
+ # Reference: https://maidsafe.readme.io/docs/is-token-valid
204
+ #
205
+ def is_token_valid
206
+ # entry point
207
+ url = "#{@client.app_info[:launcher_server]}auth"
208
+
209
+ # api call
210
+ uri = URI(url)
211
+ http = Net::HTTP.new(uri.host, uri.port)
212
+ req = Net::HTTP::Get.new(uri.path, {
213
+ 'Authorization' => "Bearer #{@client.key_helper.get_token()}"
214
+ })
215
+ res = http.request(req)
216
+ res.code == "200"
217
+ end
360
218
 
361
- # ex.: register_service("thegoogle", "www", "/www")
362
- def self.register_service(long_name, service_name, service_home_dir_path, options = {})
363
- # entry point
364
- url = "#{@@LAUNCHER_SERVER}dns"
365
219
 
366
- # default values
367
- options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
368
-
369
- # payload
370
- payload = {
371
- longName: long_name,
372
- serviceName: service_name,
373
- serviceHomeDirPath: service_home_dir_path,
374
- isPathShared: options[:is_path_shared]
375
- }
376
-
377
- # optional
378
- payload["metadata"] = options[:metadata] if options.has_key?(:metadata)
379
-
380
- # api call
381
- uri = URI(url)
382
- http = Net::HTTP.new(uri.host, uri.port)
383
- req = Net::HTTP::Post.new(uri.path, {
384
- 'Authorization' => "Bearer #{self.get_valid_token()}",
385
- 'Content-Type' => 'text/plain'
386
- })
387
- req.body = self.encrypt(payload.to_json)
388
- res = http.request(req)
389
- res.code == "200" ? true : JSON.parse(self.decrypt(res.body))
220
+ #
221
+ # Removes the token from the SAFE Launcher.
222
+ #
223
+ # Usage: SafeNet.revoke_token()
224
+ # Fail: false
225
+ # Success: true
226
+ #
227
+ # Reference: https://maidsafe.readme.io/docs/revoke-token
228
+ #
229
+ def revoke_token
230
+ # entry point
231
+ url = "#{@client.app_info[:launcher_server]}auth"
232
+
233
+ # api call
234
+ uri = URI(url)
235
+ http = Net::HTTP.new(uri.host, uri.port)
236
+ req = Net::HTTP::Delete.new(uri.path, {
237
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}"
238
+ })
239
+ res = http.request(req)
240
+ res.code == "200"
241
+ end
390
242
  end
391
243
 
392
244
 
393
- def self.list_long_names
394
- # entry point
395
- url = "#{@@LAUNCHER_SERVER}dns"
245
+ class NFS
246
+ def initialize(client_obj)
247
+ @client = client_obj
248
+ end
396
249
 
397
- # api call
398
- uri = URI(url)
399
- http = Net::HTTP.new(uri.host, uri.port)
400
- req = Net::HTTP::Get.new(uri.path, {
401
- 'Authorization' => "Bearer #{self.get_valid_token()}",
402
- })
403
- res = http.request(req)
404
- JSON.parse(self.decrypt(res.body))
405
- end
250
+ #
251
+ # Create a directory using the NFS API.
252
+ # Only authorised requests can create a directory.
253
+ #
254
+ # Usage: SafeNet.get_directory("/photos", is_path_shared: false)
255
+ # Fail: {"errorCode"=>-1502, "description"=>"FfiError::PathNotFound"}
256
+ # Success: {"info"=> {"name"=> "Ruby Demo App-Root-Dir", ...}, ...}
257
+ #
258
+ # Reference: https://maidsafe.readme.io/docs/nfs-get-directory
259
+ #
260
+ def get_directory(dir_path, options = {})
261
+ # default values
262
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
263
+
264
+ # entry point
265
+ url = "#{@client.app_info[:launcher_server]}nfs/directory/#{CGI.escape(dir_path)}/#{options[:is_path_shared]}"
266
+
267
+ # api call
268
+ uri = URI(url)
269
+ http = Net::HTTP.new(uri.host, uri.port)
270
+ req = Net::HTTP::Get.new(uri.path, {
271
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
272
+ })
273
+ res = http.request(req)
274
+ JSON.parse(@client.key_helper.decrypt(res.body))
275
+ end
406
276
 
407
277
 
408
- def self.list_services(long_name)
409
- # entry point
410
- url = "#{@@LAUNCHER_SERVER}dns/#{CGI.escape(long_name)}"
278
+ #
279
+ # Create a File using the NFS API.
280
+ # Only authorised requests can invoke the API.
281
+ #
282
+ # Usage: SafeNet.file("/photos/cat.jpg")
283
+ # Adv.Usage: SafeNet.file("/photos/cat.jpg", is_private: true, metadata: "some meta", is_path_shared: false, is_versioned: false)
284
+ # Fail: {"errorCode"=>-505, "description"=>"NfsError::FileAlreadyExistsWithSameName"}
285
+ # Success: true
286
+ #
287
+ # Reference: https://maidsafe.readme.io/docs/nfsfile
288
+ #
289
+ def file(file_path, options = {})
290
+ url = "#{@client.app_info[:launcher_server]}nfs/file"
291
+
292
+ # default values
293
+ options[:is_private] = true if ! options.has_key?(:is_private)
294
+ options[:is_versioned] = false if ! options.has_key?(:is_versioned)
295
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
296
+
297
+ # payload
298
+ payload = {
299
+ filePath: file_path,
300
+ isPrivate: options[:is_private],
301
+ isVersioned: options[:is_versioned],
302
+ isPathShared: options[:is_path_shared]
303
+ }
304
+
305
+ # optional
306
+ payload["metadata"] = Base64.strict_encode64(options[:metadata]) if options.has_key?(:metadata)
307
+
308
+ # api call
309
+ uri = URI(url)
310
+ http = Net::HTTP.new(uri.host, uri.port)
311
+ req = Net::HTTP::Post.new(uri.path, {
312
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
313
+ 'Content-Type' => 'text/plain'
314
+ })
315
+ req.body = @client.key_helper.encrypt(payload.to_json)
316
+ res = http.request(req)
317
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
318
+ end
411
319
 
412
- # api call
413
- uri = URI(url)
414
- http = Net::HTTP.new(uri.host, uri.port)
415
- req = Net::HTTP::Get.new(uri.path, {
416
- 'Authorization' => "Bearer #{self.get_valid_token()}",
417
- })
418
- res = http.request(req)
419
- JSON.parse(self.decrypt(res.body))
420
- end
421
320
 
321
+ #
322
+ # Create a directory using the NFS API.
323
+ # Only authorised requests can create a directory.
324
+ #
325
+ # Usage: SafeNet.create_directory("/photos")
326
+ # Adv.Usage: SafeNet.create_directory("/photos", is_private: true, metadata: "some meta", is_path_shared: false, is_versioned: false)
327
+ # Fail: {"errorCode"=>-505, "description"=>"NfsError::FileAlreadyExistsWithSameName"}
328
+ # Success: true
329
+ #
330
+ # Reference: https://maidsafe.readme.io/docs/nfs-create-directory
331
+ #
332
+ def create_directory(dir_path, options = {})
333
+ # entry point
334
+ url = "#{@client.app_info[:launcher_server]}nfs/directory"
335
+
336
+ # default values
337
+ options[:is_private] = true if ! options.has_key?(:is_private)
338
+ options[:is_versioned] = false if ! options.has_key?(:is_versioned)
339
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
340
+
341
+ # payload
342
+ payload = {
343
+ dirPath: dir_path,
344
+ isPrivate: options[:is_private],
345
+ isVersioned: options[:is_versioned],
346
+ isPathShared: options[:is_path_shared]
347
+ }
348
+
349
+ # optional
350
+ payload["metadata"] = Base64.strict_encode64(options[:metadata]) if options.has_key?(:metadata)
351
+
352
+ # api call
353
+ uri = URI(url)
354
+ http = Net::HTTP.new(uri.host, uri.port)
355
+ req = Net::HTTP::Post.new(uri.path, {
356
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
357
+ 'Content-Type' => 'text/plain'
358
+ })
359
+ req.body = @client.key_helper.encrypt(payload.to_json)
360
+ res = http.request(req)
361
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
362
+ end
422
363
 
423
- def self.get_home_dir(long_name, service_name)
424
- # entry point
425
- url = "#{@@LAUNCHER_SERVER}dns/#{CGI.escape(service_name)}/#{CGI.escape(long_name)}"
426
364
 
427
- # api call
428
- uri = URI(url)
429
- http = Net::HTTP.new(uri.host, uri.port)
430
- req = Net::HTTP::Get.new(uri.path)
431
- res = http.request(req)
432
- JSON.parse(res.body)
433
- end
365
+ # ex.: delete_directory("/photos")
366
+ def delete_directory(dir_path, options = {})
367
+ # default values
368
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
434
369
 
370
+ # entry point
371
+ url = "#{@client.app_info[:launcher_server]}nfs/directory/#{CGI.escape(dir_path)}/#{options[:is_path_shared]}"
435
372
 
436
- # get_file_unauth("thegoogle", "www", "index.html", offset: 3, length: 5)
437
- def self.get_file_unauth(long_name, service_name, file_path, options = {})
438
- # default values
439
- options[:offset] = 0 if ! options.has_key?(:offset)
440
-
441
- # entry point
442
- url = "#{@@LAUNCHER_SERVER}dns/#{CGI.escape(service_name)}/#{CGI.escape(long_name)}/#{CGI.escape(file_path)}?"
443
-
444
- # query params are encrypted
445
- query = []
446
- query << "offset=#{options[:offset]}"
447
- query << "length=#{options[:length]}" if options.has_key?(:length) # length is optional
448
- url = "#{url}#{self.encrypt(query.join('&'))}"
449
-
450
- # api call
451
- uri = URI(url)
452
- http = Net::HTTP.new(uri.host, uri.port)
453
- req = Net::HTTP::Get.new(uri.path)
454
- res = http.request(req)
455
- res.code == "200" ? res.body : JSON.parse(res.body)
456
- end
373
+ # api call
374
+ uri = URI(url)
375
+ http = Net::HTTP.new(uri.host, uri.port)
376
+ req = Net::HTTP::Delete.new(uri.path, {
377
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
378
+ })
379
+ res = http.request(req)
380
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
381
+ end
457
382
 
458
- private
383
+ # options: offset, length, is_path_shared
384
+ def get_file(file_path, options = {})
385
+ # default values
386
+ options[:offset] = 0 if ! options.has_key?(:offset)
387
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
388
+
389
+ # entry point
390
+ url = "#{@client.app_info[:launcher_server]}nfs/file/#{CGI.escape(file_path)}/#{options[:is_path_shared]}?"
391
+
392
+ # query params are encrypted
393
+ query = []
394
+ query << "offset=#{options[:offset]}"
395
+ query << "length=#{options[:length]}" if options.has_key?(:length) # length is optional
396
+ url = "#{url}#{@client.key_helper.encrypt(query.join('&'))}"
397
+
398
+ # api call
399
+ uri = URI(url)
400
+ http = Net::HTTP.new(uri.host, uri.port)
401
+ req = Net::HTTP::Get.new(uri.path, {
402
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
403
+ })
404
+ res = http.request(req)
405
+ res.code == "200" ? @client.key_helper.decrypt(res.body) : JSON.parse(@client.key_helper.decrypt(res.body))
406
+ end
459
407
 
460
- def self.get_token
461
- @@conf = File.exists?(@@CONF_PATH) ? JSON.parse(File.read(@@CONF_PATH)) : (self.auth() || {})
462
- @@conf["token"]
463
- end
408
+ def update_file_content(file_path, contents, options = {})
409
+ # default values
410
+ options[:offset] = 0 if ! options.has_key?(:offset)
411
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
412
+
413
+ # entry point
414
+ url = "#{@client.app_info[:launcher_server]}nfs/file/#{CGI.escape(file_path)}/#{options[:is_path_shared]}?offset=#{options[:offset]}"
415
+
416
+ # api call
417
+ uri = URI(url)
418
+ http = Net::HTTP.new(uri.host, uri.port)
419
+ req = Net::HTTP::Put.new(uri.path, {
420
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
421
+ 'Content-Type' => 'text/plain'
422
+ })
423
+ req.body = @client.key_helper.encrypt(contents)
424
+ res = http.request(req)
425
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
426
+ end
464
427
 
465
- def self.get_valid_token
466
- @last_conf = File.exists?(@@CONF_PATH) ? JSON.parse(File.read(@@CONF_PATH)) : {}
467
- self.auth() unless File.exists?(@@CONF_PATH) && self.is_token_valid()
468
- @@conf = JSON.parse(File.read(@@CONF_PATH))
469
- @@conf["token"]
428
+ def delete_file(file_path, options = {})
429
+ # default values
430
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
431
+
432
+ # entry point
433
+ url = "#{@client.app_info[:launcher_server]}nfs/file/#{CGI.escape(file_path)}/#{options[:is_path_shared]}"
434
+
435
+ # api call
436
+ uri = URI(url)
437
+ http = Net::HTTP.new(uri.host, uri.port)
438
+ req = Net::HTTP::Delete.new(uri.path, {
439
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
440
+ })
441
+ res = http.request(req)
442
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
443
+ end
470
444
  end
471
445
 
472
446
 
473
- def self.get_keys
474
- @@keys ||= {}
447
+ class DNS
448
+ def initialize(client_obj)
449
+ @client = client_obj
450
+ end
451
+
452
+ # https://maidsafe.readme.io/docs/dns-create-long-name
453
+ def create_long_name(long_name)
454
+ # entry point
455
+ url = "#{@client.app_info[:launcher_server]}dns/#{CGI.escape(long_name)}"
456
+
457
+ # api call
458
+ uri = URI(url)
459
+ http = Net::HTTP.new(uri.host, uri.port)
460
+ req = Net::HTTP::Post.new(uri.path, {
461
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
462
+ 'Content-Type' => 'text/plain'
463
+ })
464
+ res = http.request(req)
465
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
466
+ end
475
467
 
476
- # not loaded yet?
477
- if @@keys.empty?
478
- @@conf ||= {}
479
- self.get_valid_token if @@conf.empty?
480
468
 
481
- # extract keys
482
- cipher_text = Base64.strict_decode64(@@conf["encryptedKey"])
483
- nonce = Base64.strict_decode64(@@conf["nonce"])
484
- private_key = Base64.strict_decode64(@@conf["privateKey"])
485
- public_key = Base64.strict_decode64(@@conf["publicKey"])
469
+ # ex.: register_service("thegoogle", "www", "/www")
470
+ # https://maidsafe.readme.io/docs/dns-register-service
471
+ def register_service(long_name, service_name, service_home_dir_path, options = {})
472
+ # entry point
473
+ url = "#{@client.app_info[:launcher_server]}dns"
474
+
475
+ # default values
476
+ options[:is_path_shared] = false if ! options.has_key?(:is_path_shared)
477
+
478
+ # payload
479
+ payload = {
480
+ longName: long_name,
481
+ serviceName: service_name,
482
+ serviceHomeDirPath: service_home_dir_path,
483
+ isPathShared: options[:is_path_shared]
484
+ }
485
+
486
+ # optional
487
+ payload["metadata"] = options[:metadata] if options.has_key?(:metadata)
488
+
489
+ # api call
490
+ uri = URI(url)
491
+ http = Net::HTTP.new(uri.host, uri.port)
492
+ req = Net::HTTP::Post.new(uri.path, {
493
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
494
+ 'Content-Type' => 'text/plain'
495
+ })
496
+ req.body = @client.key_helper.encrypt(payload.to_json)
497
+ res = http.request(req)
498
+ res.code == "200" ? true : JSON.parse(@client.key_helper.decrypt(res.body))
499
+ end
486
500
 
487
- box = RbNaCl::Box.new(public_key, private_key)
488
- data = box.decrypt(nonce, cipher_text)
501
+ # https://maidsafe.readme.io/docs/dns-list-long-names
502
+ def list_long_names
503
+ # entry point
504
+ url = "#{@client.app_info[:launcher_server]}dns"
505
+
506
+ # api call
507
+ uri = URI(url)
508
+ http = Net::HTTP.new(uri.host, uri.port)
509
+ req = Net::HTTP::Get.new(uri.path, {
510
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
511
+ })
512
+ res = http.request(req)
513
+ JSON.parse(@client.key_helper.decrypt(res.body))
514
+ end
489
515
 
490
- # The first segment of the data will have the symmetric key
491
- @@keys["symmetric_key"] = data.slice(0, RbNaCl::SecretBox.key_bytes)
516
+ # https://maidsafe.readme.io/docs/dns-list-services
517
+ def list_services(long_name)
518
+ # entry point
519
+ url = "#{@client.app_info[:launcher_server]}dns/#{CGI.escape(long_name)}"
520
+
521
+ # api call
522
+ uri = URI(url)
523
+ http = Net::HTTP.new(uri.host, uri.port)
524
+ req = Net::HTTP::Get.new(uri.path, {
525
+ 'Authorization' => "Bearer #{@client.key_helper.get_valid_token()}",
526
+ })
527
+ res = http.request(req)
528
+ JSON.parse(@client.key_helper.decrypt(res.body))
529
+ end
492
530
 
493
- # The second segment of the data will have the nonce to be used
494
- @@keys["symmetric_nonce"] = data.slice(RbNaCl::SecretBox.key_bytes, RbNaCl::SecretBox.key_bytes)
495
531
 
496
- # keep the box object in cache
497
- @@keys["secret_box"] = RbNaCl::SecretBox.new(@@keys["symmetric_key"])
532
+ # https://maidsafe.readme.io/docs/dns-get-home-dir
533
+ def get_home_dir(long_name, service_name)
534
+ # entry point
535
+ url = "#{@client.app_info[:launcher_server]}dns/#{CGI.escape(service_name)}/#{CGI.escape(long_name)}"
536
+
537
+ # api call
538
+ uri = URI(url)
539
+ http = Net::HTTP.new(uri.host, uri.port)
540
+ req = Net::HTTP::Get.new(uri.path)
541
+ res = http.request(req)
542
+ JSON.parse(res.body)
498
543
  end
499
544
 
500
- @@keys
501
- end
502
545
 
503
- def self.decrypt(message_base64)
504
- keys = self.get_keys
505
- keys["secret_box"].decrypt(keys["symmetric_nonce"], Base64.strict_decode64(message_base64))
506
- end
546
+ # https://maidsafe.readme.io/docs/dns-get-file-unauth
547
+ # get_file_unauth("thegoogle", "www", "index.html", offset: 3, length: 5)
548
+ def self.get_file_unauth(long_name, service_name, file_path, options = {})
549
+ # default values
550
+ options[:offset] = 0 if ! options.has_key?(:offset)
507
551
 
508
- def self.encrypt(message)
509
- keys = self.get_keys
510
- res = keys["secret_box"].encrypt(keys["symmetric_nonce"], message)
511
- Base64.strict_encode64(res)
512
- end
552
+ # entry point
553
+ url = "#{@client.app_info[:launcher_server]}dns/#{CGI.escape(service_name)}/#{CGI.escape(long_name)}/#{CGI.escape(file_path)}?"
513
554
 
555
+ # query params are encrypted
556
+ query = []
557
+ query << "offset=#{options[:offset]}"
558
+ query << "length=#{options[:length]}" if options.has_key?(:length) # length is optional
559
+ url = "#{url}#{@client.key_helper.encrypt(query.join('&'))}"
560
+
561
+ # api call
562
+ uri = URI(url)
563
+ http = Net::HTTP.new(uri.host, uri.port)
564
+ req = Net::HTTP::Get.new(uri.path)
565
+ res = http.request(req)
566
+ res.code == "200" ? res.body : JSON.parse(res.body)
567
+ end
568
+ end
514
569
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-safenet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Loureiro
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-03-26 00:00:00.000000000 Z
11
+ date: 2016-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler