ruby-safenet 0.0.5 → 0.2.0

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