pod4 0.9.3 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,379 @@
1
+ require "date"
2
+ require "openssl"
3
+ require "octothorpe"
4
+
5
+ require "pod4"
6
+ require "pod4/encrypting"
7
+ require "pod4/null_interface"
8
+ require "pod4/errors"
9
+
10
+
11
+ describe "(Model with Encryption)" do
12
+
13
+ ##
14
+ # Encrypt / decrypt
15
+ #
16
+ def encrypt(key, iv=nil, plaintext)
17
+ cipher = OpenSSL::Cipher.new(iv ? Pod4::Encrypting::CIPHER_IV : Pod4::Encrypting::CIPHER_NO_IV)
18
+ cipher.encrypt
19
+ cipher.key = key
20
+ cipher.iv = iv if iv
21
+ cipher.update(plaintext) + cipher.final
22
+ end
23
+
24
+ let(:encryption_key) { "dflkasdgklajndgnalkghlgasdgasdghaalsdg" }
25
+
26
+ let(:medical_model_class) do # model with an IV column
27
+ Class.new Pod4::Model do
28
+ include Pod4::Encrypting
29
+ attr_columns :id, :nhs_no # note, we don't bother to name encrypted columns
30
+ encrypted_columns :name, :ailment, :prescription
31
+ set_key "dflkasdgklajndgnalkghlgasdgasdghaalsdg"
32
+ set_iv_column :nonce
33
+ set_interface NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
34
+ end
35
+ end
36
+
37
+ let(:medical_model_bad_class) do # model with an IV column and a very very short key
38
+ Class.new Pod4::Model do
39
+ include Pod4::Encrypting
40
+ attr_columns :id, :nhs_no # note, we don't bother to name encrypted columns
41
+ encrypted_columns :name, :ailment, :prescription
42
+ set_key "d"
43
+ set_iv_column :nonce
44
+ set_interface NullInterface.new(:id, :nhs_no, :name, :ailment, :prescription, :nonce, [])
45
+ end
46
+ end
47
+
48
+ let(:diary_model_class) do # model without an IV column
49
+ Class.new Pod4::Model do
50
+ include Pod4::Encrypting
51
+ attr_columns :id, :date, :heading, :text
52
+ encrypted_columns :heading, :text
53
+ set_key "dflkasdgklajndgnalkghlgasdgasdghaalsdg"
54
+ set_interface NullInterface.new(:id, :date, :heading, :text, [])
55
+ end
56
+ end
57
+
58
+ let(:m40) do
59
+ m = medical_model_class.new
60
+ m.id = 40
61
+ m.nhs_no = "123456"
62
+ m.name = "fred"
63
+ m.ailment = "sore toe"
64
+ m.prescription = "suck thumb"
65
+ m
66
+ end
67
+
68
+ let(:d44) do
69
+ d = diary_model_class.new
70
+ d.id = 44
71
+ d.date = Date.new(2018,4,14)
72
+ d.heading = "fred"
73
+ d.text = "sore toe"
74
+ d
75
+ end
76
+
77
+ let(:m40_record) { m40.interface.read(40) }
78
+ let(:d44_record) { d44.interface.read(44) }
79
+
80
+
81
+ describe "Model::CIPHER_IV" do
82
+
83
+ it "should be a valid string to initialize OpenSSL::Cipher" do
84
+ expect{ OpenSSL::Cipher.new(medical_model_class::CIPHER_IV) }.not_to raise_exception
85
+ end
86
+
87
+ end # of Model::CIPHER_IV
88
+
89
+
90
+ describe "Model::CIPHER_NO_IV" do
91
+
92
+ it "should be a valid string to initialize OpenSSL::Cipher" do
93
+ expect{ OpenSSL::Cipher.new(medical_model_class::CIPHER_NO_IV) }.not_to raise_exception
94
+ end
95
+
96
+ end # of Model::CIPHER_NO_IV
97
+
98
+
99
+ describe "Model.set_key" do
100
+
101
+ it "requires a string" do
102
+ expect( medical_model_class ).to respond_to(:set_key).with(1).argument
103
+ end
104
+
105
+ end # of Model.set_key
106
+
107
+
108
+ describe "Model.encryption_key" do
109
+
110
+ it "returns the key passed to set_key" do
111
+ expect( medical_model_class.encryption_key ).to eq encryption_key
112
+ end
113
+
114
+ end # of Model.encryption_key
115
+
116
+
117
+ describe "Model.set_iv_column" do
118
+
119
+ it "requires an IV column" do
120
+ expect( medical_model_class ).to respond_to(:set_iv_column).with(1).argument
121
+ end
122
+
123
+ it "exposes the encryption_iv attribute, just like attr_accessor" do
124
+ m20 = medical_model_class.new(20)
125
+ expect( m20 ).to respond_to(:nonce)
126
+ expect( m20 ).to respond_to(:nonce=)
127
+ end
128
+
129
+ # See also: Model#encryption_iv
130
+
131
+ end # of Model.set_iv_column
132
+
133
+
134
+ describe "Model.encryption_iv_column" do
135
+
136
+ it "returns the column passed to set_iv_column" do
137
+ expect( medical_model_class.encryption_iv_column ).to eq :nonce
138
+ end
139
+
140
+ end # of Model.encryption_iv_column
141
+
142
+
143
+ describe "Model.encrypted_columns" do
144
+
145
+ it "requires a list of columns" do
146
+ expect( medical_model_class ).to respond_to(:encrypted_columns).with(1).argument
147
+ end
148
+
149
+ it "adds the columns to the model's column list, just like attr_columns" do
150
+ expect( medical_model_class.columns ).
151
+ to match_array( %i|id nonce nhs_no name ailment prescription| )
152
+
153
+ expect( diary_model_class.columns ).to match_array( %i|id date heading text| )
154
+ end
155
+
156
+ it "exposes the columns just like attr_accessor" do
157
+ m20 = medical_model_class.new(20)
158
+
159
+ expect( m20 ).to respond_to(:name)
160
+ expect( m20 ).to respond_to(:ailment)
161
+ expect( m20 ).to respond_to(:prescription)
162
+ expect( m20 ).to respond_to(:name=)
163
+ expect( m20 ).to respond_to(:ailment=)
164
+ expect( m20 ).to respond_to(:prescription=)
165
+ end
166
+
167
+ end # of Model.encrypted_columns
168
+
169
+
170
+ describe "Model.encryption_columns" do
171
+
172
+ it "exposes the columns given in encrypted_columns" do
173
+ expect( medical_model_class.encryption_columns ).
174
+ to match_array( %i|name ailment prescription| )
175
+
176
+ end
177
+
178
+ end # of Model.encryption_columns
179
+
180
+
181
+ describe "(Creating a record)" do
182
+
183
+ context "when we don't have an IV column" do
184
+
185
+ it "scrambles the encrypted columns and leaves the others alone" do
186
+ d44.create
187
+ expect( d44_record.>>.date ).to eq d44.date
188
+ expect( d44_record.>>.heading ).not_to eq d44.heading
189
+ expect( d44_record.>>.text ).not_to eq d44.text
190
+ end
191
+
192
+ end
193
+
194
+ context "when we have an IV column" do
195
+
196
+ it "writes an IV to the nonce field" do
197
+ m40.create
198
+ expect( m40_record.>>.nonce ).not_to be_nil
199
+ expect( m40_record.>>.nonce ).to eq m40.nonce
200
+ expect( m40_record.>>.nonce ).to eq m40.encryption_iv
201
+ end
202
+
203
+ it "scrambles the encrypted columns and leaves the others alone" do
204
+ m40.create
205
+ expect( m40_record.>>.nhs_no ).to eq m40.nhs_no
206
+ expect( m40_record.>>.name ).not_to eq m40.name
207
+ expect( m40_record.>>.ailment ).not_to eq m40.ailment
208
+ expect( m40_record.>>.prescription ).not_to eq m40.prescription
209
+ end
210
+
211
+ end
212
+
213
+ end # of (Creating a record)
214
+
215
+
216
+ describe "(reading a record)" do
217
+
218
+ context "when we have no IV column" do
219
+ before(:each) { d44.create }
220
+
221
+ it "decrypts the columns" do
222
+ d = diary_model_class.new(44).read
223
+ expect( d.date ).to eq Date.new(2018,4,14)
224
+ expect( d.heading ).to eq "fred"
225
+ expect( d.text ).to eq "sore toe"
226
+ end
227
+
228
+ end
229
+
230
+ context "when we have an IV column" do
231
+ before(:each) { m40.create }
232
+
233
+ it "returns the IV field as encryption_iv" do
234
+ m = medical_model_class.new(40).read
235
+ expect( m.encryption_iv ).not_to be_nil
236
+ expect( m.encryption_iv ).to eq m.nonce
237
+ end
238
+
239
+ it "decrypts the columns" do
240
+ m = medical_model_class.new(40).read
241
+ expect( m.nhs_no ).to eq "123456"
242
+ expect( m.name ).to eq "fred"
243
+ expect( m.ailment ).to eq "sore toe"
244
+ expect( m.prescription ).to eq "suck thumb"
245
+ end
246
+
247
+ end
248
+
249
+ end # of (reading a record)
250
+
251
+
252
+ describe "Model#(encryption_iv field)" do
253
+
254
+ it "returns nil for a new model object" do
255
+ m = medical_model_class.new
256
+ m.id = 50
257
+ expect( m.nonce ).to be_nil
258
+ end
259
+
260
+ it "returns the nonce for an existing record" do
261
+ expect( m40.nonce ).to eq m40_record.>>.nonce
262
+ end
263
+
264
+ end # of Model#(encryption_iv field)
265
+
266
+
267
+ describe "Model#encryption_iv" do
268
+
269
+ it "returns nil for a new model object" do
270
+ m = medical_model_class.new
271
+ m.id = 50
272
+
273
+ expect( m.encryption_iv ).to be_nil
274
+ end
275
+
276
+ it "returns nil if we don't have an IV set" do
277
+ d44.create
278
+ expect( d44.encryption_iv ).to be_nil
279
+ end
280
+
281
+ it "returns the nonce for an existing record" do
282
+ expect( m40.encryption_iv ).to eq m40_record.>>.nonce
283
+ end
284
+
285
+ it "returns the same as the actual IV field" do
286
+ expect( m40.encryption_iv ).to eq m40.nonce
287
+ end
288
+
289
+ end # of Model#encryption_iv
290
+
291
+
292
+ describe "Model#map_to_interface" do
293
+
294
+ it "raises Pod4Error if there is an encryption problem, eg, key too short" do
295
+ bad = medical_model_bad_class.new
296
+ bad.id = 999
297
+ bad.nhs_no = "12345"
298
+ bad.name = "alice"
299
+ bad.ailment = "tiny key"
300
+ bad.prescription = "raise an exception"
301
+
302
+ expect{ bad.map_to_interface }.to raise_exception Pod4::Pod4Error
303
+ end
304
+
305
+ context "when we don't have an IV column" do
306
+
307
+ it "encrypts only the encryptable columns for the interface" do
308
+ ot = d44.map_to_interface
309
+ expect( ot.>>.date ).to eq Date.new(2018,4,14)
310
+ expect( ot.>>.heading ).to eq encrypt(encryption_key, "fred")
311
+ expect( ot.>>.text ).to eq encrypt(encryption_key, "sore toe")
312
+ end
313
+
314
+ end
315
+
316
+ context "when we have an IV column" do
317
+ before(:each) { m40.create }
318
+
319
+ it "sets the IV column on create" do
320
+ expect( m40_record.>>.nonce ).not_to be_nil
321
+ end
322
+
323
+ it "encrypts only the encryptable columns for the interface" do
324
+ ot = m40.map_to_interface
325
+ iv = m40.nonce
326
+ expect( ot.>>.nhs_no ).to eq "123456"
327
+ expect( ot.>>.name ).to eq encrypt(encryption_key, iv, "fred")
328
+ expect( ot.>>.ailment ).to eq encrypt(encryption_key, iv, "sore toe")
329
+ expect( ot.>>.prescription ).to eq encrypt(encryption_key, iv, "suck thumb")
330
+ end
331
+
332
+ end
333
+
334
+ end # of Model#map_to_interface
335
+
336
+
337
+ describe "Model#map_to_model" do
338
+
339
+ context "when we don't have an IV column" do
340
+
341
+ it "decrypts only the encryptable columns for the model" do
342
+ d44.create
343
+
344
+ d = diary_model_class.new(44)
345
+ expect( d44_record.>>.heading ).not_to eq "fred"
346
+ expect( d44_record.>>.text ).not_to eq "sore toe"
347
+
348
+ d.read
349
+ expect( d.date ).to eq Date.new(2018,4,14)
350
+ expect( d.heading ).to eq "fred"
351
+ expect( d.text ).to eq "sore toe"
352
+ end
353
+
354
+ end
355
+
356
+ context "when we have an IV column" do
357
+
358
+ it "decrypts only the encryptable columns for the model" do
359
+ m40.create
360
+
361
+ m = medical_model_class.new(40)
362
+ expect( m40_record.>>.name ).not_to eq "fred"
363
+ expect( m40_record.>>.ailment ).not_to eq "sore toe"
364
+ expect( m40_record.>>.prescription ).not_to eq "suck thumb"
365
+
366
+ m.read
367
+ expect( m.nhs_no ).to eq "123456"
368
+ expect( m.name ).to eq "fred"
369
+ expect( m.ailment ).to eq "sore toe"
370
+ expect( m.prescription ).to eq "suck thumb"
371
+ end
372
+
373
+ end
374
+
375
+ end # of Model#map_to_model
376
+
377
+
378
+ end
379
+