pod4 0.10.2 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0ee46818707291111af50ad94a88168a026f9d2a
4
- data.tar.gz: 9cae9859c19fbbd3784d8a43cb33d40a0effe7ca
3
+ metadata.gz: c65450007521cb6a9e312bdc47350624c85f381f
4
+ data.tar.gz: 37a401097db2e57f2ea96c337e4b3e151a154606
5
5
  SHA512:
6
- metadata.gz: ac96259ec5dad9ce2ad6b92d80e4b92987138ceba5d176dee3debfdc647699bd8dfa88858ecca0690384217fac34b6d8a175eae6da38ce1bccb4c2589dd48f47
7
- data.tar.gz: 4fa5a9bbec0022d3da862b4b1a5d14b0298417fc0a3ef0e710a4f1db44c25f61cabb01110db83ead8f33a8ca4e9a1cfed434ffbf52ba94b948da30802a35309d
6
+ metadata.gz: c7c157f8b35a9961a48e9457e7aa328dd1a4c361876aa236cdfbe009448a21e005c659fa7a089c79f4e61d169580d622e5539d3b04cf4d78f293422b0e20dc8e
7
+ data.tar.gz: 5be81a188a0e925fdbb45cb4f1bb74240eed34b16c6e103ce75905aefe3bc378354c744cf8437fca0389b1c68b82ef2f6fdb21b0b14a26672babcb133838c6e4
data/.hgtags CHANGED
@@ -28,3 +28,4 @@ f1b3a814e7a8c7d818b57f6d35ce7ed749573d76 0.9.1
28
28
  4860714dd83ddb5b4ed0b7e32459fdaa845ac2fa 0.9.3
29
29
  77ad625b2ab048bd660194cf093015b1f2698069 0.10
30
30
  7ac52e99228fe39ca59c87fad8fe2c1236fce4fa 0.10.1
31
+ 7a39c71dceaf05b9204e1f2870f904940fdac4d3 0.10.3
@@ -1,4 +1,5 @@
1
1
  require "openssl"
2
+ require "base64"
2
3
  require "pod4/errors"
3
4
  require "pod4/metaxing"
4
5
 
@@ -137,8 +138,9 @@ module Pod4
137
138
  # If the IV is not set we need to set it both in the model object AND the hash, since we've
138
139
  # already obtained the hash from the model object.
139
140
  if use_iv? && encryption_iv.nil?
140
- set_encryption_iv( cipher.random_iv )
141
- hash[self.class.encryption_iv_column] = encryption_iv
141
+ iv = cipher.random_iv
142
+ set_encryption_iv(iv)
143
+ hash[self.class.encryption_iv_column] = Base64.strict_encode64(iv)
142
144
  end
143
145
 
144
146
  self.class.encryption_columns.each do |col|
@@ -154,10 +156,15 @@ module Pod4
154
156
  def map_to_model(ot)
155
157
  hash = ot.to_h
156
158
  cipher = get_cipher(:decrypt)
157
- iv = hash[self.class.encryption_iv_column] # not yet set on the model
159
+
160
+ # The IV is not in columns, we need to de-base-64 it and set it on the model ourselves
161
+ if use_iv?
162
+ iv = Base64.strict_decode64 hash[self.class.encryption_iv_column]
163
+ set_encryption_iv(iv)
164
+ end
158
165
 
159
166
  self.class.encryption_columns.each do |col|
160
- hash[col] = crypt(cipher, :decrypt, iv, hash[col])
167
+ hash[col] = crypt(cipher, :decrypt, encryption_iv, hash[col])
161
168
  end
162
169
 
163
170
  super Octothorpe.new(hash)
@@ -212,9 +219,13 @@ module Pod4
212
219
  cipher.key = self.class.encryption_key
213
220
  cipher.iv = iv if use_iv?
214
221
 
222
+ string = Base64.strict_decode64(string) if direction == :decrypt
223
+
215
224
  answer = ""
216
225
  answer << cipher.update(string.to_s) unless direction == :encrypt && string.empty?
217
226
  answer << cipher.final
227
+
228
+ answer = Base64.strict_encode64(answer) if direction == :encrypt
218
229
  answer
219
230
 
220
231
  rescue OpenSSL::Cipher::CipherError
data/lib/pod4/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pod4
2
- VERSION = '0.10.2'
2
+ VERSION = '0.10.3'
3
3
  end
@@ -1,5 +1,6 @@
1
1
  require "date"
2
2
  require "openssl"
3
+ require "base64"
3
4
  require "octothorpe"
4
5
 
5
6
  require "pod4"
@@ -19,7 +20,8 @@ describe "(Model with Encryption)" do
19
20
  cipher.key = key
20
21
  cipher.iv = iv if iv
21
22
 
22
- (plaintext.empty? ? "" : cipher.update(plaintext) ) + cipher.final
23
+ answer = (plaintext.empty? ? "" : cipher.update(plaintext) ) + cipher.final
24
+ Base64.strict_encode64(answer)
23
25
  end
24
26
 
25
27
  let(:encryption_key) { "dflkasdgklajndgnalkghlgasdgasdghaalsdg" }
@@ -197,8 +199,10 @@ describe "(Model with Encryption)" do
197
199
  it "writes an IV to the nonce field" do
198
200
  m40.create
199
201
  expect( m40_record.>>.nonce ).not_to be_nil
200
- expect( m40_record.>>.nonce ).to eq m40.nonce
201
- expect( m40_record.>>.nonce ).to eq m40.encryption_iv
202
+
203
+ recordiv = Base64.strict_decode64(m40_record.>>.nonce)
204
+ expect( recordiv ).to eq m40.nonce
205
+ expect( recordiv ).to eq m40.encryption_iv
202
206
  end
203
207
 
204
208
  it "scrambles the encrypted columns and leaves the others alone" do
@@ -11,3 +11,8 @@ DB[:pg] =
11
11
  user: 'pod4test',
12
12
  password: 'pod4test' }
13
13
 
14
+ DB[:sequel] =
15
+ { host: 'centos7andy',
16
+ user: 'pod4test',
17
+ password: 'pod4test' }
18
+
@@ -0,0 +1,80 @@
1
+ require "pod4"
2
+ require "pod4/sequel_interface"
3
+ require "pod4/encrypting"
4
+ require "sequel"
5
+ require "jdbc/postgres"
6
+
7
+
8
+ describe "(writing encrypted data via sequel_interface)" do
9
+
10
+ def db_setup(connection)
11
+ connection.run(%Q| drop table if exists medical;
12
+ create table medical (
13
+ id serial primary key,
14
+ nonce text,
15
+ name text,
16
+ ailment text );| )
17
+
18
+ end
19
+
20
+ def db_truncate(connection)
21
+ connection.run(%Q| truncate table medical restart identity; |)
22
+ end
23
+
24
+
25
+ before(:all) do
26
+ @Connection = Sequel.connect('jdbc:postgresql://centos7andy/pod4_test?user=pod4test&password=pod4test')
27
+ db_setup(@connection)
28
+ end
29
+
30
+ before(:each) do
31
+ db_truncate(@connection)
32
+ medical_model_class.set_interface medical_interface_class.new(@connection)
33
+ end
34
+
35
+
36
+ let(:medical_interface_class) do
37
+ Class.new Pod4::SequelInterface do
38
+ set_table :medical
39
+ set_id_fld :id
40
+ end
41
+ end
42
+
43
+ let(:medical_model_class) do
44
+ Class.new Pod4::Model do
45
+ include Pod4::Encrypting
46
+
47
+ encrypted_columns :name, :ailment
48
+ set_key "dflkasdgklajndgnalkghlgasdgasdghaalsdg"
49
+ set_iv_column :nonce
50
+ end
51
+ end
52
+
53
+ #####
54
+
55
+
56
+ it "writes encrypted data to the database" do
57
+ m = medical_model_class.new
58
+ m.name = "frank"
59
+ m.ailment = "sore toe"
60
+
61
+ expect{ m.create }.not_to raise_exception
62
+
63
+ record = m.class.interface.list.first
64
+ expect( record ).not_to be_nil
65
+ end
66
+
67
+ it "reads encrypted data back from the database" do
68
+ m1 = medical_model_class.new
69
+ m1.name = "roger"
70
+ m1.ailment = "hiccups"
71
+ m1.create
72
+
73
+ m2 = medical_model_class.list.first
74
+ expect( m2 ).not_to be_nil
75
+ expect( m2.name ).to eq "roger"
76
+ expect( m2.ailment ).to eq "hiccups"
77
+ end
78
+
79
+ end
80
+
@@ -0,0 +1,87 @@
1
+ require "pod4"
2
+ require "pod4/pg_interface"
3
+ require "pod4/encrypting"
4
+ require "pg"
5
+
6
+ require_relative '../fixtures/database'
7
+
8
+
9
+ describe "(writing encrypted data via pg_interface)" do
10
+
11
+ def db_setup(connect)
12
+ client = PG.connect(connect)
13
+ client.exec(%Q| drop table if exists medical;
14
+ create table medical (
15
+ id serial primary key,
16
+ nonce text,
17
+ name text,
18
+ ailment text );| )
19
+
20
+ ensure
21
+ client.finish if client
22
+ end
23
+
24
+ def db_truncate(connect)
25
+ client = PG.connect(connect)
26
+ client.exec(%Q| truncate table medical restart identity; |)
27
+ ensure
28
+ client.finish if client
29
+ end
30
+
31
+
32
+ before(:all) do
33
+ @connect_hash = DB[:pg]
34
+ db_setup(@connect_hash)
35
+ end
36
+
37
+ before(:each) do
38
+ db_truncate(@connect_hash)
39
+ medical_model_class.set_interface medical_interface_class.new(@connect_hash)
40
+ end
41
+
42
+
43
+ let(:medical_interface_class) do
44
+ Class.new Pod4::PgInterface do
45
+ set_table :medical
46
+ set_id_fld :id
47
+ end
48
+ end
49
+
50
+ let(:medical_model_class) do
51
+ Class.new Pod4::Model do
52
+ include Pod4::Encrypting
53
+
54
+ encrypted_columns :name, :ailment
55
+ set_key "dflkasdgklajndgnalkghlgasdgasdghaalsdg"
56
+ set_iv_column :nonce
57
+ end
58
+ end
59
+
60
+ #####
61
+
62
+
63
+ it "writes encrypted data to the database" do
64
+ m = medical_model_class.new
65
+ m.name = "frank"
66
+ m.ailment = "sore toe"
67
+
68
+ expect{ m.create }.not_to raise_exception
69
+
70
+ record = m.class.interface.list.first
71
+ expect( record ).not_to be_nil
72
+ end
73
+
74
+ it "reads encrypted data back from the database" do
75
+ m1 = medical_model_class.new
76
+ m1.name = "roger"
77
+ m1.ailment = "hiccups"
78
+ m1.create
79
+
80
+ m2 = medical_model_class.list.first
81
+ expect( m2 ).not_to be_nil
82
+ expect( m2.name ).to eq "roger"
83
+ expect( m2.ailment ).to eq "hiccups"
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,611 @@
1
+ require 'pod4/pg_interface'
2
+ require 'pg'
3
+
4
+ require_relative '../common/shared_examples_for_interface'
5
+ require_relative '../fixtures/database'
6
+
7
+
8
+ describe "PgInterface" do
9
+
10
+ let(:pg_interface_class) do
11
+ Class.new PgInterface do
12
+ set_table :customer
13
+ set_id_fld :id
14
+
15
+ def stop; close; end # We open a lot of connections, unusually
16
+ end
17
+ end
18
+
19
+ let(:schema_interface_class) do
20
+ Class.new PgInterface do
21
+ set_schema :public
22
+ set_table :customer
23
+ set_id_fld :id
24
+ end
25
+ end
26
+
27
+ let(:prod_interface_class) do
28
+ Class.new PgInterface do
29
+ set_table :product
30
+ set_id_fld :code
31
+ end
32
+ end
33
+
34
+ let(:bad_interface_class1) do
35
+ Class.new PgInterface do
36
+ set_table :customer
37
+ end
38
+ end
39
+
40
+ let(:bad_interface_class2) do
41
+ Class.new PgInterface do
42
+ set_id_fld :id
43
+ end
44
+ end
45
+
46
+
47
+ def db_setup(connect)
48
+ client = PG.connect(connect)
49
+
50
+ client.exec(%Q|
51
+ drop table if exists customer;
52
+ drop table if exists product;
53
+
54
+ create table customer (
55
+ id serial primary key,
56
+ medical_model_class.name text,
57
+ level real null,
58
+ day date null,
59
+ timestamp timestamp null,
60
+ price money null,
61
+ flag boolean null,
62
+ qty numeric null );
63
+
64
+ create table product (
65
+ code text,
66
+ name text );| )
67
+
68
+ ensure
69
+ client.finish if client
70
+ end
71
+
72
+
73
+ def fill_data(ifce)
74
+ @data.each{|r| ifce.create(r) }
75
+ end
76
+
77
+
78
+ def fill_product_data(ifce)
79
+ ifce.create( {code: "foo", name: "bar"} )
80
+ end
81
+
82
+
83
+ before(:all) do
84
+ @connect_hash = DB[:pg]
85
+ db_setup(@connect_hash)
86
+
87
+ @data = []
88
+ @data << { name: 'Barney',
89
+ level: 1.23,
90
+ day: Date.parse("2016-01-01"),
91
+ timestamp: Time.parse('2015-01-01 12:11'),
92
+ price: BigDecimal.new("1.24"),
93
+ flag: true,
94
+ qty: BigDecimal.new("1.25") }
95
+
96
+ @data << { name: 'Fred',
97
+ level: 2.34,
98
+ day: Date.parse("2016-02-02"),
99
+ timestamp: Time.parse('2015-01-02 12:22'),
100
+ price: BigDecimal.new("2.35"),
101
+ flag: false,
102
+ qty: BigDecimal.new("2.36") }
103
+
104
+ @data << { name: 'Betty',
105
+ level: 3.45,
106
+ day: Date.parse("2016-03-03"),
107
+ timestamp: Time.parse('2015-01-03 12:33'),
108
+ price: BigDecimal.new("3.46"),
109
+ flag: nil,
110
+ qty: BigDecimal.new("3.47") }
111
+
112
+ end
113
+
114
+
115
+ before do
116
+ interface.execute(%Q|
117
+ truncate table customer restart identity;
118
+ truncate table product;|)
119
+
120
+ end
121
+
122
+
123
+ after do
124
+ # We open a lot of connections, unusually
125
+ interface.stop if interface
126
+ end
127
+
128
+
129
+ let(:interface) do
130
+ pg_interface_class.new(@connect_hash)
131
+ end
132
+
133
+ let(:prod_interface) do
134
+ prod_interface_class.new(@connect_hash)
135
+ end
136
+
137
+ #####
138
+
139
+
140
+ it_behaves_like 'an interface' do
141
+
142
+ let(:interface) do
143
+ pg_interface_class.new(@connect_hash)
144
+ end
145
+
146
+ let(:record) { {name: 'Barney'} }
147
+
148
+ end
149
+ ##
150
+
151
+
152
+ describe 'PgInterface.set_schema' do
153
+ it 'takes one argument' do
154
+ expect( pg_interface_class ).to respond_to(:set_schema).with(1).argument
155
+ end
156
+ end
157
+ ##
158
+
159
+
160
+ describe 'PgInterface.schema' do
161
+ it 'returns the schema' do
162
+ expect( schema_interface_class.schema ).to eq :public
163
+ end
164
+
165
+ it 'is optional' do
166
+ expect{ pg_interface_class.schema }.not_to raise_exception
167
+ expect( pg_interface_class.schema ).to eq nil
168
+ end
169
+ end
170
+ ##
171
+
172
+
173
+ describe 'PgInterface.set_table' do
174
+ it 'takes one argument' do
175
+ expect( pg_interface_class ).to respond_to(:set_table).with(1).argument
176
+ end
177
+ end
178
+ ##
179
+
180
+
181
+ describe 'PgInterface.table' do
182
+ it 'returns the table' do
183
+ expect( pg_interface_class.table ).to eq :customer
184
+ end
185
+ end
186
+ ##
187
+
188
+
189
+ describe 'PgInterface.set_id_fld' do
190
+ it 'takes one argument' do
191
+ expect( pg_interface_class ).to respond_to(:set_id_fld).with(1).argument
192
+ end
193
+ end
194
+ ##
195
+
196
+
197
+ describe 'PgInterface.id_fld' do
198
+ it 'returns the ID field name' do
199
+ expect( pg_interface_class.id_fld ).to eq :id
200
+ end
201
+ end
202
+ ##
203
+
204
+
205
+ describe '#new' do
206
+
207
+ it 'requires a TinyTds connection string' do
208
+ expect{ pg_interface_class.new }.to raise_exception ArgumentError
209
+ expect{ pg_interface_class.new(nil) }.to raise_exception ArgumentError
210
+ expect{ pg_interface_class.new('foo') }.to raise_exception ArgumentError
211
+
212
+ expect{ pg_interface_class.new(@connect_hash) }.not_to raise_exception
213
+ end
214
+
215
+ end
216
+ ##
217
+
218
+
219
+ describe '#quoted_table' do
220
+
221
+ it 'returns just the table when the schema is not set' do
222
+ expect( interface.quoted_table ).to eq( %Q|"customer"| )
223
+ end
224
+
225
+ it 'returns the schema plus table when the schema is set' do
226
+ ifce = schema_interface_class.new(@connect_hash)
227
+ expect( ifce.quoted_table ).to eq( %|"public"."customer"| )
228
+ end
229
+
230
+ end
231
+ ##
232
+
233
+
234
+ describe '#create' do
235
+
236
+ let(:hash) { {name: 'Bam-Bam', price: 4.44} }
237
+ let(:ot) { Octothorpe.new(name: 'Wilma', price: 5.55) }
238
+
239
+ it 'raises a Pod4::DatabaseError if anything goes wrong' do
240
+ expect{ interface.create(one: 'two') }.to raise_exception DatabaseError
241
+ end
242
+
243
+ it 'creates the record when given a hash' do
244
+ # kinda impossible to seperate these two tests
245
+ id = interface.create(hash)
246
+
247
+ expect{ interface.read(id) }.not_to raise_exception
248
+ expect( interface.read(id).to_h ).to include hash
249
+ end
250
+
251
+ it 'creates the record when given an Octothorpe' do
252
+ id = interface.create(ot)
253
+
254
+ expect{ interface.read(id) }.not_to raise_exception
255
+ expect( interface.read(id).to_h ).to include ot.to_h
256
+ end
257
+
258
+ it 'shouldn\'t have a problem with record values of nil' do
259
+ record = {name: 'Ranger', price: nil}
260
+ expect{ interface.create(record) }.not_to raise_exception
261
+ id = interface.create(record)
262
+ expect( interface.read(id).to_h ).to include(record)
263
+ end
264
+
265
+ it 'shouldn\'t have a problem with strings containing special characters' do
266
+ record = {name: %Q|T'Challa""|, price: nil}
267
+ expect{ interface.create(record) }.not_to raise_exception
268
+ id = interface.create(record)
269
+ expect( interface.read(id).to_h ).to include(record)
270
+ end
271
+
272
+ it 'shouldn\'t have a problem with non-integer keys' do
273
+ hash = {code: "foo", name: "bar"}
274
+ id = prod_interface.create( Octothorpe.new(hash) )
275
+
276
+ expect( id ).to eq "foo"
277
+ expect{ prod_interface.read("foo") }.not_to raise_exception
278
+ expect( prod_interface.read("foo").to_h ).to include hash
279
+ end
280
+
281
+ end
282
+ ##
283
+
284
+
285
+ describe '#read' do
286
+ before { fill_data(interface) }
287
+
288
+ it 'returns the record for the id as an Octothorpe' do
289
+ rec = interface.read(2)
290
+ expect( rec ).to be_a_kind_of Octothorpe
291
+ expect( rec.>>.name ).to eq 'Fred'
292
+ end
293
+
294
+ it 'raises a Pod4::CantContinue if the ID is bad' do
295
+ expect{ interface.read(:foo) }.to raise_exception CantContinue
296
+ end
297
+
298
+ it 'returns an empty Octothorpe if no record matches the ID' do
299
+ expect{ interface.read(99) }.not_to raise_exception
300
+ expect( interface.read(99) ).to be_a_kind_of Octothorpe
301
+ expect( interface.read(99) ).to be_empty
302
+ end
303
+
304
+ it 'returns real fields as Float' do
305
+ level = interface.read(1).>>.level
306
+
307
+ expect( level ).to be_a_kind_of Float
308
+ expect( level ).to be_within(0.001).of( @data.first[:level] )
309
+ end
310
+
311
+ it 'returns date fields as Date' do
312
+ date = interface.read(1).>>.day
313
+
314
+ expect( date ).to be_a_kind_of Date
315
+ expect( date ).to eq @data.first[:day]
316
+ end
317
+
318
+ it 'returns datetime fields as Time' do
319
+ timestamp = interface.read(1).>>.timestamp
320
+
321
+ expect( timestamp ).to be_a_kind_of Time
322
+ expect( timestamp ).to eq @data.first[:timestamp]
323
+ end
324
+
325
+ it 'returns numeric fields as BigDecimal' do
326
+ qty = interface.read(1).>>.qty
327
+
328
+ expect( qty ).to be_a_kind_of BigDecimal
329
+ expect( qty ).to eq @data.first[:qty]
330
+ end
331
+
332
+ it 'returns money fields as BigDecimal' do
333
+ price = interface.read(1).>>.price
334
+
335
+ expect( price ).to be_a_kind_of BigDecimal
336
+ expect( price ).to eq @data.first[:price]
337
+ end
338
+
339
+ it 'returns boolean fields as boolean' do
340
+ [1,2,3].each do |i|
341
+ flag = interface.read(i).>>.flag
342
+ expect( [true, false, nil].include? flag ).to be true
343
+ expect( flag ).to be @data[i - 1][:flag]
344
+ end
345
+ end
346
+
347
+ it 'shouldn\'t have a problem with non-integer keys' do
348
+ # this is a 100% overlap with the create test above...
349
+ fill_product_data(prod_interface)
350
+
351
+ expect{ prod_interface.read("foo") }.not_to raise_exception
352
+ expect( prod_interface.read("foo").to_h ).to include(code: "foo", name: "bar")
353
+ end
354
+
355
+ end
356
+ ##
357
+
358
+
359
+ describe '#list' do
360
+ before { fill_data(interface) }
361
+
362
+ it 'has an optional selection parameter, a hash' do
363
+ # Actually it does not have to be a hash, but FTTB we only support that.
364
+ expect{ interface.list(name: 'Barney') }.not_to raise_exception
365
+ end
366
+
367
+ it 'returns an array of Octothorpes that match the records' do
368
+ # convert each OT to a hash and remove the ID key
369
+ arr = interface.list.map {|ot| x = ot.to_h; x.delete(:id); x }
370
+
371
+ expect( arr ).to match_array @data
372
+ end
373
+
374
+ it 'returns a subset of records based on the selection parameter' do
375
+ expect( interface.list(name: 'Fred').size ).to eq 1
376
+
377
+ expect( interface.list(name: 'Betty').first.to_h ).
378
+ to include(name: 'Betty')
379
+
380
+ end
381
+
382
+ it 'returns an empty Array if nothing matches' do
383
+ expect( interface.list(name: 'Yogi') ).to eq([])
384
+ end
385
+
386
+ it 'raises ArgumentError if the selection criteria is nonsensical' do
387
+ expect{ interface.list('foo') }.to raise_exception ArgumentError
388
+ end
389
+
390
+ end
391
+ ##
392
+
393
+
394
+ describe '#update' do
395
+ before { fill_data(interface) }
396
+
397
+ let(:id) { interface.list.first[:id] }
398
+
399
+ def float_price(row)
400
+ row[:price] = row[:price].to_f
401
+ row
402
+ end
403
+
404
+ it 'updates the record at ID with record parameter' do
405
+ record = {name: 'Booboo', price: 99.99}
406
+ interface.update(id, record)
407
+
408
+ expect( float_price( interface.read(id).to_h ) ).to include(record)
409
+ end
410
+
411
+ it 'raises a CantContinue if anything weird happens with the ID' do
412
+ expect{ interface.update(99, name: 'Booboo') }.
413
+ to raise_exception CantContinue
414
+
415
+ end
416
+
417
+ it 'raises a DatabaseError if anything weird happens with the record' do
418
+ expect{ interface.update(id, smarts: 'more') }.
419
+ to raise_exception DatabaseError
420
+
421
+ end
422
+
423
+ it 'shouldn\'t have a problem with record values of nil' do
424
+ record = {name: 'Ranger', price: nil}
425
+ expect{ interface.update(id, record) }.not_to raise_exception
426
+ expect( interface.read(id).to_h ).to include(record)
427
+ end
428
+
429
+ it 'shouldn\'t have a problem with strings containing special characters' do
430
+ record = {name: %Q|T'Challa""|, price: nil}
431
+ expect{ interface.update(id, record) }.not_to raise_exception
432
+ expect( interface.read(id).to_h ).to include(record)
433
+ end
434
+
435
+ it 'shouldn\'t have a problem with non-integer keys' do
436
+ fill_product_data(prod_interface)
437
+ expect{ prod_interface.update("foo", name: "baz") }.not_to raise_error
438
+ expect( prod_interface.read("foo").to_h[:name] ).to eq "baz"
439
+ end
440
+
441
+ end
442
+ ##
443
+
444
+
445
+ describe '#delete' do
446
+
447
+ def list_contains(ifce, id)
448
+ ifce.list.find {|x| x[ifce.id_fld] == id }
449
+ end
450
+
451
+ let(:id) { interface.list.first[:id] }
452
+
453
+ before { fill_data(interface) }
454
+
455
+ it 'raises CantContinue if anything hinky happens with the id' do
456
+ expect{ interface.delete(:foo) }.to raise_exception CantContinue
457
+ expect{ interface.delete(99) }.to raise_exception CantContinue
458
+ end
459
+
460
+ it 'makes the record at ID go away' do
461
+ expect( list_contains(interface, id) ).to be_truthy
462
+ interface.delete(id)
463
+ expect( list_contains(interface, id) ).to be_falsy
464
+ end
465
+
466
+ it 'shouldn\'t have a problem with non-integer keys' do
467
+ fill_product_data(prod_interface)
468
+ expect( list_contains(prod_interface, "foo") ).to be_truthy
469
+ prod_interface.delete("foo")
470
+ expect( list_contains(prod_interface, "foo") ).to be_falsy
471
+ end
472
+
473
+ end
474
+ ##
475
+
476
+
477
+ describe '#execute' do
478
+
479
+ let(:sql) { 'delete from customer where cast(price as numeric) < 2.0;' }
480
+
481
+ before { fill_data(interface) }
482
+
483
+ it 'requires an SQL string' do
484
+ expect{ interface.execute }.to raise_exception ArgumentError
485
+ expect{ interface.execute(nil) }.to raise_exception ArgumentError
486
+ expect{ interface.execute(14) }.to raise_exception ArgumentError
487
+ end
488
+
489
+ it 'raises some sort of Pod4 error if it runs into problems' do
490
+ expect{ interface.execute('delete from not_a_table') }.
491
+ to raise_exception Pod4Error
492
+
493
+ end
494
+
495
+ it 'executes the string' do
496
+ expect{ interface.execute(sql) }.not_to raise_exception
497
+ expect( interface.list.size ).to eq(@data.size - 1)
498
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
499
+ end
500
+
501
+ end
502
+ ##
503
+
504
+
505
+ describe '#executep' do
506
+
507
+ let(:sql) { 'delete from customer where cast(price as numeric) < %s and name = %s;' }
508
+
509
+ before { fill_data(interface) }
510
+
511
+ it 'requires an SQL string' do
512
+ expect{ interface.executep }.to raise_exception ArgumentError
513
+ expect{ interface.executep(nil) }.to raise_exception ArgumentError
514
+ expect{ interface.executep(14) }.to raise_exception ArgumentError
515
+ end
516
+
517
+ it 'raises some sort of Pod4 error if it runs into problems' do
518
+ expect{ interface.executep('delete from not_a_table where foo = %s', 12) }.
519
+ to raise_exception Pod4Error
520
+
521
+ end
522
+
523
+ it 'executes the string with the given parameters' do
524
+ expect{ interface.executep(sql, 12.0, 'Barney') }.not_to raise_exception
525
+ expect( interface.list.size ).to eq(@data.size - 1)
526
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
527
+ end
528
+
529
+ end
530
+ ##
531
+
532
+
533
+ describe '#select' do
534
+
535
+ before { fill_data(interface) }
536
+
537
+ it 'requires an SQL string' do
538
+ expect{ interface.select }.to raise_exception ArgumentError
539
+ expect{ interface.select(nil) }.to raise_exception ArgumentError
540
+ expect{ interface.select(14) }.to raise_exception ArgumentError
541
+ end
542
+
543
+ it 'raises some sort of Pod4 error if it runs into problems' do
544
+ expect{ interface.select('select * from not_a_table') }.
545
+ to raise_exception Pod4Error
546
+
547
+ end
548
+
549
+ it 'returns the result of the sql' do
550
+ sql1 = 'select name from customer where cast(price as numeric) < 2.0;'
551
+ sql2 = 'select name from customer where cast(price as numeric) < 0.0;'
552
+
553
+ expect{ interface.select(sql1) }.not_to raise_exception
554
+ expect( interface.select(sql1) ).to eq( [{name: 'Barney'}] )
555
+ expect( interface.select(sql2) ).to eq( [] )
556
+ end
557
+
558
+ it 'works if you pass a non-select' do
559
+ # By which I mean: still executes the SQL; returns []
560
+ sql = 'delete from customer where cast(price as numeric) < 2.0;'
561
+ ret = interface.select(sql)
562
+
563
+ expect( interface.list.size ).to eq(@data.size - 1)
564
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
565
+ expect( ret ).to eq( [] )
566
+ end
567
+
568
+ end
569
+ ##
570
+
571
+
572
+ describe '#selectp' do
573
+
574
+ before { fill_data(interface) }
575
+
576
+ it 'requires an SQL string' do
577
+ expect{ interface.selectp }.to raise_exception ArgumentError
578
+ expect{ interface.selectp(nil) }.to raise_exception ArgumentError
579
+ expect{ interface.selectp(14) }.to raise_exception ArgumentError
580
+ end
581
+
582
+ it 'raises some sort of Pod4 error if it runs into problems' do
583
+ expect{ interface.selectp('select * from not_a_table where thingy = %s', 12) }.
584
+ to raise_exception Pod4Error
585
+
586
+ end
587
+
588
+ it 'returns the result of the sql' do
589
+ sql = 'select name from customer where cast(price as numeric) < %s;'
590
+
591
+ expect{ interface.selectp(sql, 2.0) }.not_to raise_exception
592
+ expect( interface.selectp(sql, 2.0) ).to eq( [{name: 'Barney'}] )
593
+ expect( interface.selectp(sql, 0.0) ).to eq( [] )
594
+ end
595
+
596
+ it 'works if you pass a non-select' do
597
+ sql = 'delete from customer where cast(price as numeric) < %s;'
598
+ ret = interface.selectp(sql, 2.0)
599
+
600
+ expect( interface.list.size ).to eq(@data.size - 1)
601
+ expect( interface.list.map{|r| r[:name] } ).not_to include 'Barney'
602
+ expect( ret ).to eq( [] )
603
+ end
604
+
605
+
606
+ end
607
+ ##
608
+
609
+
610
+ end
611
+
@@ -0,0 +1,81 @@
1
+ require "pod4"
2
+ require "pod4/sequel_interface"
3
+ require "pod4/encrypting"
4
+ require "sequel"
5
+
6
+ require_relative '../fixtures/database'
7
+
8
+
9
+ describe "(writing encrypted data via sequel_interface)" do
10
+
11
+ def db_setup(connection)
12
+ connection.run(%Q| drop table if exists medical;
13
+ create table medical (
14
+ id serial primary key,
15
+ nonce text,
16
+ name text,
17
+ ailment text );| )
18
+
19
+ end
20
+
21
+ def db_truncate(connection)
22
+ connection.run(%Q| truncate table medical restart identity; |)
23
+ end
24
+
25
+
26
+ before(:all) do
27
+ @connection = Sequel.postgres("pod4_test", DB[:sequel])
28
+ db_setup(@connection)
29
+ end
30
+
31
+ before(:each) do
32
+ db_truncate(@connection)
33
+ medical_model_class.set_interface medical_interface_class.new(@connection)
34
+ end
35
+
36
+
37
+ let(:medical_interface_class) do
38
+ Class.new Pod4::SequelInterface do
39
+ set_table :medical
40
+ set_id_fld :id
41
+ end
42
+ end
43
+
44
+ let(:medical_model_class) do
45
+ Class.new Pod4::Model do
46
+ include Pod4::Encrypting
47
+
48
+ encrypted_columns :name, :ailment
49
+ set_key "dflkasdgklajndgnalkghlgasdgasdghaalsdg"
50
+ set_iv_column :nonce
51
+ end
52
+ end
53
+
54
+ #####
55
+
56
+
57
+ it "writes encrypted data to the database" do
58
+ m = medical_model_class.new
59
+ m.name = "frank"
60
+ m.ailment = "sore toe"
61
+
62
+ expect{ m.create }.not_to raise_exception
63
+
64
+ record = m.class.interface.list.first
65
+ expect( record ).not_to be_nil
66
+ end
67
+
68
+ it "reads encrypted data back from the database" do
69
+ m1 = medical_model_class.new
70
+ m1.name = "roger"
71
+ m1.ailment = "hiccups"
72
+ m1.create
73
+
74
+ m2 = medical_model_class.list.first
75
+ expect( m2 ).not_to be_nil
76
+ expect( m2.name ).to eq "roger"
77
+ expect( m2.ailment ).to eq "hiccups"
78
+ end
79
+
80
+ end
81
+
@@ -0,0 +1,98 @@
1
+ require "pod4"
2
+ require "pod4/tds_interface"
3
+ require "pod4/encrypting"
4
+ require "tiny_tds"
5
+
6
+ require_relative '../fixtures/database'
7
+
8
+
9
+ describe "(writing encrypted data via tds_interface)" do
10
+
11
+ def db_setup(connect)
12
+ client = TinyTds::Client.new(connect)
13
+ client.execute(%Q|use [pod4_test];|).do
14
+
15
+ # Our SQL Server does not support DROP TABLE IF EXISTS !
16
+ # This is apparently an SQL-agnostic way of doing it:
17
+ client.execute(%Q|
18
+ if exists (select * from INFORMATION_SCHEMA.TABLES
19
+ where TABLE_NAME = 'medical'
20
+ and TABLE_SCHEMA = 'dbo' )
21
+ drop table dbo.medical;
22
+
23
+ create table dbo.medical (
24
+ id int identity(1,1) not null,
25
+ nonce nvarchar(max),
26
+ name nvarchar(max),
27
+ ailment nvarchar(max) );| ).do
28
+
29
+ ensure
30
+ client.close if client
31
+ end
32
+
33
+ def db_truncate(connect)
34
+ client = TinyTds::Client.new(connect)
35
+ client.execute(%Q|use [pod4_test];|).do
36
+ client.execute(%Q| truncate table medical; |).do
37
+ ensure
38
+ client.close if client
39
+ end
40
+
41
+
42
+ before(:all) do
43
+ @connect_hash = DB[:tds]
44
+ db_setup(@connect_hash)
45
+ end
46
+
47
+ before(:each) do
48
+ db_truncate(@connect_hash)
49
+ medical_model_class.set_interface medical_interface_class.new(@connect_hash)
50
+ end
51
+
52
+
53
+ let(:medical_interface_class) do
54
+ Class.new Pod4::TdsInterface do
55
+ set_db :pod4_test
56
+ set_table :medical
57
+ set_id_fld :id
58
+ end
59
+ end
60
+
61
+ let(:medical_model_class) do
62
+ Class.new Pod4::Model do
63
+ include Pod4::Encrypting
64
+
65
+ encrypted_columns :name, :ailment
66
+ set_key "dflkasdgklajndgnalkghlgasdgasdghaalsdg"
67
+ set_iv_column :nonce
68
+ end
69
+ end
70
+
71
+ #####
72
+
73
+
74
+ it "writes encrypted data to the database" do
75
+ m = medical_model_class.new
76
+ m.name = "frank"
77
+ m.ailment = "sore toe"
78
+
79
+ expect{ m.create }.not_to raise_exception
80
+
81
+ record = m.class.interface.list.first
82
+ expect( record ).not_to be_nil
83
+ end
84
+
85
+ it "reads encrypted data back from the database" do
86
+ m1 = medical_model_class.new
87
+ m1.name = "roger"
88
+ m1.ailment = "hiccups"
89
+ m1.create
90
+
91
+ m2 = medical_model_class.list.first
92
+ expect( m2 ).not_to be_nil
93
+ expect( m2.name ).to eq "roger"
94
+ expect( m2.ailment ).to eq "hiccups"
95
+ end
96
+
97
+ end
98
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pod4
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.2
4
+ version: 0.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Jones
@@ -96,10 +96,15 @@ files:
96
96
  - spec/common/sql_helper_spec.rb
97
97
  - spec/doc_no_pending.rb
98
98
  - spec/fixtures/database.rb
99
+ - spec/jruby/sequel_encrypting_jdbc_pg_spec.rb
99
100
  - spec/jruby/sequel_interface_jdbc_ms_spec.rb
100
101
  - spec/jruby/sequel_interface_jdbc_pg_spec.rb
102
+ - spec/mri/pg_encrypting_spec.rb
101
103
  - spec/mri/pg_interface_spec.rb
104
+ - spec/mri/pg_interface_spec.rb.orig
105
+ - spec/mri/sequel_encrypting_spec.rb
102
106
  - spec/mri/sequel_interface_spec.rb
107
+ - spec/mri/tds_encrypting_spec.rb
103
108
  - spec/mri/tds_interface_spec.rb
104
109
  - spec/spec_helper.rb
105
110
  - tags
@@ -145,9 +150,14 @@ test_files:
145
150
  - spec/common/sql_helper_spec.rb
146
151
  - spec/doc_no_pending.rb
147
152
  - spec/fixtures/database.rb
153
+ - spec/jruby/sequel_encrypting_jdbc_pg_spec.rb
148
154
  - spec/jruby/sequel_interface_jdbc_ms_spec.rb
149
155
  - spec/jruby/sequel_interface_jdbc_pg_spec.rb
156
+ - spec/mri/pg_encrypting_spec.rb
150
157
  - spec/mri/pg_interface_spec.rb
158
+ - spec/mri/pg_interface_spec.rb.orig
159
+ - spec/mri/sequel_encrypting_spec.rb
151
160
  - spec/mri/sequel_interface_spec.rb
161
+ - spec/mri/tds_encrypting_spec.rb
152
162
  - spec/mri/tds_interface_spec.rb
153
163
  - spec/spec_helper.rb