pod4 0.10.2 → 0.10.3

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: 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