midb 1.1.1 → 2.0.0

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
  SHA1:
3
- metadata.gz: d5ef9483dfada39d3e9f8bccc1c44e1e418a49b1
4
- data.tar.gz: 954831b1336e3d3402279f2d5b1cd49f99850d50
3
+ metadata.gz: 644f4e1de9aa34f81c03df7a4632ed0c809c2347
4
+ data.tar.gz: ba52f668994e64f7425ed9d001dd86083c3dec7e
5
5
  SHA512:
6
- metadata.gz: 50e11179acc6827bb9fc7ece5f5ecaf4b7938dac3838d82601d2c496a62fd5f6f4fa00fbab474c927a1c925e7aec4ede32603542256627c806e020c7d8010bbb
7
- data.tar.gz: e148bf8571b0f54652a79945f92df92ebde4ab67e3898acc4541f1c34342b93fb6fa9d20f8d006e3eb9f44a4f0a9145293e1706e227f993dcceed68d1d50aee5
6
+ metadata.gz: d05f26de94df71818137d79bef6cb23b79e9ad68d4fa3c853494959d75e670177d8b0065ae24a34fd767c145a5066df37548a666c912f58dfd54d12ff540063a
7
+ data.tar.gz: 82b54f559776bcb5a7dc5bc19a437e8a804c12703cde686bdc57f37048d85a70ea0e8ab0967ea954e96d7905f5b6270455cbf1a02641cdfa0b1f59467daf2cd6
@@ -1,6 +1,6 @@
1
1
  require 'sqlite3'
2
2
  require 'mysql2'
3
-
3
+ require 'midb/errors_view'
4
4
  module MIDB
5
5
  module API
6
6
  # @author unrar
@@ -38,14 +38,19 @@ module MIDB
38
38
  #
39
39
  # @return [SQLite3::Database, Mysql2::Client] A resource referencing to the database
40
40
  def connect()
41
- # Connect to an SQLite3 database
42
- if @engine == :sqlite3
43
- sq = SQLite3::Database.open("./db/#{@db}.db")
44
- sq.results_as_hash = true
45
- return sq
46
- # Connect to a MySQL database
47
- elsif @engine == :mysql
48
- return Mysql2::Client.new(:host => @host, :username => @uname, :password => @pwd, :database => @db)
41
+ begin
42
+ # Connect to an SQLite3 database
43
+ if @engine == :sqlite3
44
+ sq = SQLite3::Database.open("./db/#{@db}.db")
45
+ sq.results_as_hash = true
46
+ return sq
47
+ # Connect to a MySQL database
48
+ elsif @engine == :mysql
49
+ return Mysql2::Client.new(:host => @host, :username => @uname, :password => @pwd, :database => @db)
50
+ end
51
+ rescue
52
+ MIDB::Interface::Errors.exception(:database_error, $!)
53
+ return false
49
54
  end
50
55
  end
51
56
 
@@ -56,10 +61,15 @@ module MIDB
56
61
  #
57
62
  # @return [Array, Hash] Returns an array of hashes for SQLite3 or a hash for MySQL
58
63
  def query(res, query)
59
- if @engine == :sqlite3
60
- return res.execute(query)
61
- elsif @engine == :mysql
62
- return res.query(query)
64
+ begin
65
+ if @engine == :sqlite3
66
+ return res.execute(query)
67
+ elsif @engine == :mysql
68
+ return res.query(query)
69
+ end
70
+ rescue
71
+ MIDB::Interface::Errors.exception(:query_error, $!)
72
+ return false
63
73
  end
64
74
  end
65
75
 
@@ -25,6 +25,14 @@ module MIDB
25
25
  end
26
26
  abort("Fatal error: #{errmsg}")
27
27
  end
28
+ def self.exception(exc, more="")
29
+ excmsg = case exc
30
+ when :database_error then "An error occurred when trying to connect to the database. #{more}"
31
+ when :query_error then "An error occurred when trying to query the database. #{more}"
32
+ else "Unknown exception: #{exc.to_s} #{more}"
33
+ end
34
+ puts "(exception)\t#{excmsg}"
35
+ end
28
36
  end
29
37
  end
30
38
  end
@@ -31,6 +31,19 @@ module MIDB
31
31
  # @return [Object] MIDB::API::Hooks instance
32
32
  attr_accessor :args, :config, :db, :http_status, :port, :hooks
33
33
 
34
+ # Convert an on/off string to a boolean
35
+ #
36
+ # @param text [String] Text to convert to bool
37
+ def to_bool(text)
38
+ if text == "on" || text == "true" || text == "yes"
39
+ return true
40
+ elsif text == "off" || text == "false" || text == "no"
41
+ return false
42
+ else
43
+ return nil
44
+ end
45
+ end
46
+
34
47
  # Constructor for this controller.
35
48
  #
36
49
  # @param args [Array<String>] Arguments passed to the binary.
@@ -92,12 +105,20 @@ module MIDB
92
105
  @config["serves"] = []
93
106
  @config["status"] = :asleep # The server is initially asleep
94
107
  @config["apikey"] = "midb-api" # This should be changed, it's the private API key
108
+ # Optional API key for GET methods (void by default)
109
+ @config["apigetkey"] = nil
95
110
  @config["dbengine"] = :sqlite3 # SQLite is the default engine
96
111
  # Default DB configuration for MySQL and other engines
97
112
  @config["dbhost"] = "localhost"
98
113
  @config["dbport"] = 3306
99
114
  @config["dbuser"] = "nobody"
100
115
  @config["dbpassword"] = "openaccess"
116
+ # New settings - privacy
117
+ @config["privacyget"] = false
118
+ @config["privacypost"] = true
119
+ @config["privacyput"] = true
120
+ @config["privacydelete"] = true
121
+
101
122
  File.open(".midb.yaml", 'w') do |l|
102
123
  l.write @config.to_yaml
103
124
  end
@@ -155,6 +176,30 @@ module MIDB
155
176
  when "key"
156
177
  @config["apikey"] = setter if set
157
178
  MIDB::Interface::Server.out_config(:apikey, @config)
179
+ when "getkey"
180
+ if setter == "nil"
181
+ @config["apigetkey"] = nil if set
182
+ else
183
+ @config["apigetkey"] = setter if set
184
+ end
185
+ MIDB::Interface::Server.out_config(:apigetkey, @config)
186
+ end
187
+ when "privacy"
188
+ case subcmd
189
+ when "get"
190
+ @config["privacyget"] = self.to_bool(setter) if set
191
+ MIDB::Interface::Server.out_config(:privacyget, @config)
192
+ when "post"
193
+ @config["privacypost"] = self.to_bool(setter) if set
194
+ MIDB::Interface::Server.out_config(:privacypost, @config)
195
+ when "put"
196
+ @config["privacyput"] = self.to_bool(setter) if set
197
+ MIDB::Interface::Server.out_config(:privacyput, @config)
198
+ when "delete"
199
+ @config["privacydelete"] = self.to_bool(setter) if set
200
+ MIDB::Interface::Server.out_config(:privacydelete, @config)
201
+ else
202
+ MIDB::Interface::Errors.die(:syntax)
158
203
  end
159
204
  else
160
205
  MIDB::Interface::Errors.die(:syntax)
@@ -247,10 +247,12 @@ module MIDB
247
247
  dbe = MIDB::API::Dbengine.new(@engine.config, @db)
248
248
  dblink = dbe.connect()
249
249
  rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]} WHERE id=#{id};")
250
+ if rows == false
251
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
252
+ end
250
253
  if dbe.length(rows) > 0
251
254
  rows.each do |row|
252
255
  jso[row["id"]] = self.get_structure
253
-
254
256
  self.get_structure.each do |name, dbi|
255
257
  table = dbi.split("/")[0]
256
258
  field = dbi.split("/")[1]
@@ -263,6 +265,9 @@ module MIDB
263
265
  else
264
266
  query = dbe.query(dblink, "SELECT #{field} from #{table} WHERE id=#{row['id']};")
265
267
  end
268
+ if query == false
269
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
270
+ end
266
271
  jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
267
272
  jso[row["id"]][name] = @hooks.format_field(name, jso[row["id"]][name])
268
273
  end
@@ -284,10 +289,124 @@ module MIDB
284
289
  dbe = MIDB::API::Dbengine.new(@engine.config, @db)
285
290
  dblink = dbe.connect()
286
291
  rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]};")
292
+ if rows == false
293
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
294
+ end
295
+ # Iterate over all rows of this table
296
+ rows.each do |row|
297
+ jso[row["id"]] = self.get_structure
298
+ self.get_structure.each do |name, dbi|
299
+ table = dbi.split("/")[0]
300
+ field = dbi.split("/")[1]
301
+ # Must-match relations ("table2/field/table2-field->row-field")
302
+ if dbi.split("/").length > 2
303
+ match = dbi.split("/")[2]
304
+ matching_field = match.split("->")[0]
305
+ row_field = match.split("->")[1]
306
+ query = dbe.query(dblink, "SELECT #{field} FROM #{table} WHERE #{matching_field}=#{row[row_field]};")
307
+ else
308
+ query = dbe.query(dblink, "SELECT #{field} from #{table} WHERE id=#{row['id']};")
309
+ end
310
+ if query == false
311
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
312
+ end
313
+ jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
314
+ jso[row["id"]][name] = @hooks.format_field(name, jso[row["id"]][name])
315
+ end
316
+ end
317
+ @hooks.after_get_all_entries(dbe.length(rows))
318
+ return jso
319
+ end
320
+
321
+
322
+ # Get all the entries from the database belonging to a column
323
+ def get_column_entries(column)
324
+ jso = Hash.new()
325
+ jss = self.get_structure()
326
+ db_column = nil
327
+ # Is the column recognized?
328
+ if jss.has_key? column then
329
+ db_column = jss[column].split("/")[1]
330
+ else
331
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
332
+ end
333
+
334
+ # Connect to database
335
+ dbe = MIDB::API::Dbengine.new(@engine.config, @db)
336
+ dblink = dbe.connect()
337
+ rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]};")
338
+ if rows == false
339
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
340
+ end
341
+ # Iterate over all rows of this table
342
+ rows.each do |row|
343
+
344
+ name = column
345
+ dbi = jss[name]
346
+ table = dbi.split("/")[0]
347
+ field = dbi.split("/")[1]
348
+ # Must-match relations ("table2/field/table2-field->row-field")
349
+ if dbi.split("/").length > 2
350
+ match = dbi.split("/")[2]
351
+ matching_field = match.split("->")[0]
352
+ row_field = match.split("->")[1]
353
+ query = dbe.query(dblink, "SELECT #{field} FROM #{table} WHERE #{matching_field}=#{row[row_field]};")
354
+ else
355
+ query = dbe.query(dblink, "SELECT #{field} from #{table} WHERE id=#{row['id']};")
356
+ end
357
+ if query == false
358
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
359
+ end
360
+ jso[row["id"]] = {}
361
+ jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
362
+ jso[row["id"]][name] = @hooks.format_field(name, jso[row["id"]][name])
363
+ end
364
+ @hooks.after_get_all_entries(dbe.length(rows))
365
+ return jso
366
+ end
287
367
 
368
+ # Get all the entries from the database belonging to a column matching a pattern
369
+ def get_matching_rows(column, pattern)
370
+ jso = Hash.new()
371
+ jss = self.get_structure()
372
+ db_column = nil
373
+ # Is the column recognized?
374
+ if jss.has_key? column then
375
+ db_column = jss[column].split("/")[1]
376
+ else
377
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
378
+ end
379
+
380
+ # Connect to database
381
+ dbe = MIDB::API::Dbengine.new(@engine.config, @db)
382
+ dblink = dbe.connect()
383
+ rows = dbe.query(dblink, "SELECT * FROM #{self.get_structure.values[0].split('/')[0]};")
384
+ if rows == false
385
+ return MIDB::Interface::Server.json_error(400, "Bad Request")
386
+ end
288
387
  # Iterate over all rows of this table
289
388
  rows.each do |row|
290
- # Replace the "id" in the given JSON with the actual ID and expand it with the fields
389
+ # Does this row match?
390
+ bufd = jss[column]
391
+ b_table = bufd.split("/")[0]
392
+ b_field = bufd.split("/")[1]
393
+ # The column is in another table, let's find it
394
+ if bufd.split("/").length > 2
395
+ b_match = bufd.split("/")[2]
396
+ b_m_field = b_match.split("->")[0]
397
+ b_r_field = b_match.split("->")[1]
398
+
399
+ bquery = dbe.query(dblink, "SELECT #{b_field} FROM #{b_table} WHERE (#{b_m_field}=#{row[b_r_field]} AND #{db_column} LIKE '%#{pattern}%');")
400
+ else
401
+ # It's in the same main table, let's see if it matches
402
+ bquery = dbe.query(dblink, "SELECT #{b_field} FROM #{b_table} WHERE (id=#{row['id']} AND #{db_column} LIKE '%#{pattern}%');")
403
+ end
404
+
405
+ # Unless the query has been successful (thus this row matches), skip to the next row
406
+ unless dbe.length(bquery) > 0
407
+ next
408
+ end
409
+
291
410
  jso[row["id"]] = self.get_structure
292
411
 
293
412
  self.get_structure.each do |name, dbi|
@@ -302,11 +421,14 @@ module MIDB
302
421
  else
303
422
  query = dbe.query(dblink, "SELECT #{field} from #{table} WHERE id=#{row['id']};")
304
423
  end
424
+ if query == false
425
+ next
426
+ end
305
427
  jso[row["id"]][name] = dbe.length(query) > 0 ? dbe.extract(query,field) : "unknown"
306
428
  jso[row["id"]][name] = @hooks.format_field(name, jso[row["id"]][name])
307
429
  end
308
430
  end
309
- @hooks.after_get_all_entries(rows.length)
431
+ @hooks.after_get_all_entries(dbe.length(rows))
310
432
  return jso
311
433
  end
312
434
  end
@@ -47,6 +47,7 @@ module MIDB
47
47
  when :no_auth then ">> No authentication header - sending a 401 error."
48
48
  when :auth_success then ">> Successfully authenticated the request."
49
49
  when :bootstrap then "> Successfully bootstraped!"
50
+ when :fetch then ">> Fetching response... [#{info}]"
50
51
  end
51
52
  puts msg
52
53
  end
@@ -56,16 +57,11 @@ module MIDB
56
57
  # @param what [Symbol] What to show the config for.
57
58
  # @param cnf [Array<String>] The array for the config.
58
59
  def self.out_config(what, cnf)
59
- msg = case what
60
- when :dbengine then "Database engine: #{cnf['dbengine']}."
61
- when :dbhost then "Database server host: #{cnf['dbhost']}."
62
- when :dbport then "Database server port: #{cnf['dbport']}."
63
- when :dbuser then "Database server user: #{cnf['dbuser']}."
64
- when :dbpassword then "Database server password: #{cnf['dbpassword']}."
65
- when :apikey then "Private API key: #{cnf['apikey']}"
66
- else "Error??"
67
- end
68
- puts msg
60
+ if cnf.has_key? what.to_s
61
+ puts "#{what.to_s} set to #{cnf[what.to_s]}"
62
+ else
63
+ puts "Unknown setting."
64
+ end
69
65
  end
70
66
 
71
67
  # Shows the help
@@ -27,6 +27,12 @@ module MIDB
27
27
  # @return [Object] MIDB::API::Hooks instance
28
28
  attr_accessor :config, :db, :http_status, :hooks
29
29
 
30
+ # Handle an unauthorized request
31
+ def unauth_request
32
+ @http_status = "401 Unauthorized"
33
+ MIDB::Interface::Server.info(:no_auth)
34
+ MIDB::Interface::Server.json_error(401, "Unauthorized").to_json
35
+ end
30
36
 
31
37
  # Constructor
32
38
  #
@@ -72,7 +78,11 @@ module MIDB
72
78
 
73
79
  # Endpoint syntax: ["", FILE, ID, (ACTION)]
74
80
  endpoint = request[1].split("/")
75
- ep_file = endpoint[1]
81
+ if endpoint.length >= 2
82
+ ep_file = endpoint[1].split("?")[0]
83
+ else
84
+ ep_file = ""
85
+ end
76
86
 
77
87
  method = request[0]
78
88
  endpoints = [] # Valid endpoints
@@ -92,35 +102,74 @@ module MIDB
92
102
  # Create the model
93
103
  dbop = MIDB::API::Model.new(ep, @db, self)
94
104
  # Analyze the request and pass it to the model
95
- if method == "GET"
96
- case endpoint.length
97
- when 2
98
- # No ID has been specified. Return all the entries
99
- # Pass it to the model and get the JSON
100
- response_json = dbop.get_all_entries().to_json
101
- when 3
102
- # An ID has been specified. Should it exist, return all of its entries.
103
- response_json = dbop.get_entries(endpoint[2]).to_json
104
- end
105
+ # Is the method accepted?
106
+ accepted_methods = ["GET", "POST", "PUT", "DELETE"]
107
+ unless accepted_methods.include? method
108
+ @http_status = "405 Method Not Allowed"
109
+ response_json = MIDB::Interface::Server.json_error(405, "Method Not Allowed").to_json
105
110
  else
106
- # An action has been specified. We're going to need HTTP authentification here.
107
- MIDB::Interface::Server.info(:auth_required)
108
-
109
- if (not headers.has_key? "Authentication") ||
110
- (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apikey"]))
111
- @http_status = "401 Unauthorized"
112
- response_json = MIDB::Interface::Server.json_error(401, "Unauthorized").to_json
113
- MIDB::Interface::Server.info(:no_auth)
111
+ # Do we need authentication?
112
+ auth_req = false
113
+ unauthenticated = false
114
+ if @config["privacy#{method.downcase}"] == true
115
+ MIDB::Interface::Server.info(:auth_required)
116
+ auth_req = true
117
+
118
+ # For GET and DELETE requests, the object of the digest is the endpoint
119
+ if (method == "GET") || (method == "DELETE")
120
+ data = ep_file
121
+ end
114
122
 
123
+ # If it's a GET request and we have a different key for GET methods...
124
+ if (@config["apigetkey"] != nil) && (method == "GET")
125
+ unauthenticated = (not headers.has_key? "Authentication") ||
126
+ (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apigetkey"]))
127
+ else
128
+ unauthenticated = (not headers.has_key? "Authentication") ||
129
+ (not MIDB::API::Security.check?(headers["Authentication"], data, @config["apikey"]))
130
+ end
131
+ end
132
+ # Proceed to handle the request
133
+ if unauthenticated
134
+ response_json = self.unauth_request
135
+ puts ">> has header: #{headers.has_key? "Authentication"}"
115
136
  else
116
- MIDB::Interface::Server.info(:auth_success)
117
- if method == "POST"
137
+ MIDB::Interface::Server.info(:auth_success) if (not unauthenticated) && auth_req
138
+ if method == "GET"
139
+ case endpoint.length
140
+ when 2
141
+ # No ID has been specified. Return all the entries
142
+ # Pass it to the model and get the JSON
143
+ MIDB::Interface::Server.info(:fetch, "get_all_entries()")
144
+ response_json = dbop.get_all_entries().to_json
145
+ when 3
146
+ # This regular expression checks if it contains an integer
147
+ if /\A[-+]?\d+\z/ === endpoint[2]
148
+ # An ID has been specified. Should it exist, return all of its entries.
149
+ MIDB::Interface::Server.info(:fetch, "get_entries(#{endpoint[2]})")
150
+ response_json = dbop.get_entries(endpoint[2].to_i).to_json
151
+ else
152
+ # A row has been specified, but no pattern
153
+ MIDB::Interface::Server.info(:fetch, "get_column_entries(#{endpoint[2]})")
154
+ response_json = dbop.get_column_entries(endpoint[2]).to_json
155
+ end
156
+ when 4
157
+ if (endpoint[2].is_a? String) && (endpoint[3].is_a? String) then
158
+ # A row and a pattern have been specified
159
+ MIDB::Interface::Server.info(:fetch, "get_matching_rows(#{endpoint[2]}, #{endpoint[3]})")
160
+ response_json = dbop.get_matching_rows(endpoint[2], endpoint[3]).to_json
161
+ end
162
+ end
163
+ elsif method == "POST"
164
+ MIDB::Interface::Server.info(:fetch, "post(#{data})")
118
165
  response_json = dbop.post(data).to_json
119
166
  else
120
167
  if endpoint.length >= 3
121
168
  if method == "DELETE"
169
+ MIDB::Interface::Server.info(:fetch, "delete(#{endpoint[2]})")
122
170
  response_json = dbop.delete(endpoint[2]).to_json
123
171
  elsif method == "PUT"
172
+ MIDB::Interface::Server.info(:fetch, "put(#{endpoint[2]}, data)")
124
173
  response_json = dbop.put(endpoint[2], data).to_json
125
174
  end
126
175
  else
@@ -129,7 +178,7 @@ module MIDB
129
178
  end
130
179
  end
131
180
  end
132
- end
181
+ end
133
182
  MIDB::Interface::Server.info(:response, response_json)
134
183
  # Return the results via HTTP
135
184
  socket.print "HTTP/1.1 #{@http_status}\r\n" +
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: midb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - unrar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-20 00:00:00.000000000 Z
11
+ date: 2016-03-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mysql2