attr_vault 0.2.1 → 1.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: e5fb84d6365f1c3a98cf46d1033cb50a3d15fb84
4
- data.tar.gz: e0dec5b5e5a06425a9ec2c84464defe8881132df
3
+ metadata.gz: 9fcb265c898624cea0181abc4fcc39589e95014e
4
+ data.tar.gz: 75ea676dfabd0e3fea0c2436d48234539381a789
5
5
  SHA512:
6
- metadata.gz: 95f82a2d0f96fc20d7b3806c17c2c9cc30424eca24890215b298a9a6d99a78f6263fd3bd9504744460b9f9cdf211511d7e8b653f02e8147002af38fe764835b3
7
- data.tar.gz: 9c12a178ecefff35146d55823c95dd16351318fefa1991b0c9c799e26cba941dcbe4613cad8d5158110c41c095e42022143f94860483a8eb5123fa9e14213540
6
+ metadata.gz: f33061f44368a2c3c30b8d14ed0a80aec258180dde3cc25b2df8c512db396eb491ba86d7ff5fb5a15e84128c5cb2c88f4423c2632fe4876eae2fefa57ceb20aa
7
+ data.tar.gz: 292c8f1a98d7f472d9faeeda946b593e498ecd3a44d649d3025b6013b59ebaa0a8c96139d18be9f4198a6a4fac4ac0d1b65b87efa96f18128d53b5e8a8001cf8
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.2
1
+ 2.4.0
data/.travis.yml CHANGED
@@ -1,12 +1,13 @@
1
1
  language: ruby
2
2
  cache: bundler
3
- before_script: createdb attr_vault
3
+ before_script: createdb -U postgres attr_vault
4
+ dist: trusty
4
5
  sudo: false
5
6
  script: DATABASE_URL="postgres:///attr_vault" bundle exec rspec
6
7
  addons:
7
- postgresql: "9.3"
8
+ postgresql: "9.6"
8
9
  env:
9
10
  rvm:
10
- - 2.2.2
11
+ - 2.4.0
11
12
  notifications:
12
13
  email: false
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- ruby '2.2.2'
3
+ ruby '2.4.0'
4
4
 
5
5
  # Specify your gem's dependencies in attr_vault.gemspec
6
6
  gemspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- attr_vault (0.2.1)
4
+ attr_vault (1.0.0)
5
5
  pg (~> 0.18)
6
6
  sequel (~> 4.13)
7
7
 
@@ -32,7 +32,7 @@ DEPENDENCIES
32
32
  rspec (~> 3.0)
33
33
 
34
34
  RUBY VERSION
35
- ruby 2.2.2p95
35
+ ruby 2.4.0p0
36
36
 
37
37
  BUNDLED WITH
38
- 1.14.5
38
+ 1.14.4
data/README.md CHANGED
@@ -49,81 +49,6 @@ key with a larger id is assumed to be newer. The `value` is the actual
49
49
  bytes of the encryption key, used for encryption and verification: see
50
50
  below.
51
51
 
52
- #### Legacy keyrings
53
-
54
- A legacy keyring format is also supported for backwards
55
- compatibility. The keyring must be a JSON array of objects with the
56
- fields `id`, `created_at`, and `value`, and also must have at least
57
- one key:
58
-
59
- ```json
60
- [
61
- {
62
- "id": "1380e471-038e-459a-801d-10e7988ee6a3",
63
- "created_at": "2016-02-04 01:55:00+00",
64
- "value": "PV8+EHgJlHfsVVVstJHgEo+3OCSn4iJDzqJs55U650Q="
65
- }
66
- ]
67
- ```
68
-
69
- The `id` must be a uuid. The `created_at` must be an ISO-8601
70
- timestamp indicating the age of a key relative to the other keys. The
71
- `value` is the same structure as for a normal keyring.
72
-
73
- #### Legacy keyring migration
74
-
75
- You can migrate from legacy keyrings to the new format via the
76
- following process:
77
-
78
- Add a new key_id column:
79
-
80
- ```ruby
81
- Sequel.migration do
82
- change do
83
- alter_table(:diary_entries) do
84
- add_column :new_key_id, :integer
85
- end
86
- end
87
- end
88
- ```
89
-
90
- Devise new numeric ids for all in-use keys (based on their
91
- `created_at` dates), and link the ids with sql like the following:
92
-
93
- ```sql
94
- WITH key_map(new_key_id, old_key_id) AS (
95
- VALUES (1, 'first-uuid'),
96
- (2, 'next-uuid'),
97
- (3, '...')
98
- )
99
- UPDATE
100
- diary_entries
101
- SET
102
- diary_entries.new_key_id = key_map.new_key_id
103
- FROM
104
- key_map
105
- WHERE
106
- diary_entries.key_id = key_map.old_key_id
107
- ```
108
-
109
- Rename the new column to be used as the main key id and drop the old
110
- id column:
111
-
112
- ```ruby
113
- Sequel.migration do
114
- change do
115
- alter_table(:diary_entries) do
116
- rename_column :key_id, :old_key_id
117
- rename_column :new_key_id, :key_id
118
- set_column_not_null :key_id
119
- drop_column :old_key_id, :integer
120
- end
121
- end
122
- end
123
- ```
124
-
125
- Then change the keyring in your application to use the new numeric
126
- ids.
127
52
 
128
53
  ### Encryption and verification
129
54
 
@@ -256,8 +181,7 @@ It's safe to use the same name as the name of the encrypted attribute.
256
181
 
257
182
  Because AttrVault uses a keyring, with access to multiple keys at
258
183
  once, key rotation is fairly straightforward: if you add a key to the
259
- keyring with a higher id than any other key (or more recent
260
- `created_at` for the legacy keyring format), that key will
184
+ keyring with a higher id than any other key, that key will
261
185
  automatically be used for encryption. Any keys that are no longer in
262
186
  use can be removed from the keyring.
263
187
 
@@ -1,25 +1,19 @@
1
1
  module AttrVault
2
2
  class Key
3
- attr_reader :id, :value, :created_at
3
+ attr_reader :id, :value
4
4
 
5
- def initialize(id, value, created_at=nil)
6
- if id.nil?
7
- raise InvalidKey, "key id required"
8
- end
9
- if value.nil?
5
+ def initialize(id, value)
6
+ if value.nil? || value.empty?
10
7
  raise InvalidKey, "key value required"
11
8
  end
12
9
  begin
13
10
  id = Integer(id)
14
11
  rescue
15
- if created_at.nil?
16
- raise InvalidKey, "key created_at required"
17
- end
12
+ raise InvalidKey, "key id must be an integer"
18
13
  end
19
14
 
20
15
  @id = id
21
16
  @value = value
22
- @created_at = created_at
23
17
  end
24
18
 
25
19
  def digest(data)
@@ -27,7 +21,7 @@ module AttrVault
27
21
  end
28
22
 
29
23
  def to_json(*args)
30
- { id: id, value: value, created_at: created_at }.to_json
24
+ { id: id, value: value }.to_json
31
25
  end
32
26
  end
33
27
 
@@ -39,13 +33,7 @@ module AttrVault
39
33
  begin
40
34
  candidate_keys = JSON.parse(keyring_data, symbolize_names: true)
41
35
 
42
- case candidate_keys
43
- when Array
44
- candidate_keys.each do |k|
45
- created_at = Time.parse(k[:created_at]) if k.has_key?(:created_at)
46
- keyring.add_key(Key.new(k[:id], k[:value], created_at || Time.now))
47
- end
48
- when Hash
36
+ if candidate_keys.is_a?(Hash)
49
37
  candidate_keys.each do |key_id, key|
50
38
  keyring.add_key(Key.new(key_id.to_s, key))
51
39
  end
@@ -84,7 +72,7 @@ module AttrVault
84
72
  end
85
73
 
86
74
  def current_key
87
- k = @keys.sort_by(&:created_at).last
75
+ k = @keys.sort_by(&:id).last
88
76
  if k.nil?
89
77
  raise KeyringEmpty, "No keys in keyring"
90
78
  end
@@ -96,14 +84,9 @@ module AttrVault
96
84
  end
97
85
 
98
86
  def to_json
99
- if @keys.all? { |k| k.created_at.nil? }
100
- @keys.each_with_object({}) do |k,obj|
101
- obj[k.id] = k.value
102
- end.to_json
103
- else
104
- # Assume we are dealing with a legacy keyring
105
- @keys.to_json
106
- end
87
+ @keys.each_with_object({}) do |k,obj|
88
+ obj[k.id] = k.value
89
+ end.to_json
107
90
  end
108
91
  end
109
92
  end
@@ -1,3 +1,3 @@
1
1
  module AttrVault
2
- VERSION = "0.2.1"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,25 +1,25 @@
1
1
  require "spec_helper"
2
2
  require "json"
3
+ require "securerandom"
3
4
 
4
5
  module AttrVault
5
6
  describe Keyring do
6
7
 
7
8
  describe ".load" do
8
9
  let(:key_data) {
9
- [
10
- { id: SecureRandom.uuid, value: SecureRandom.base64(32), created_at: Time.now },
11
- { id: SecureRandom.uuid, value: SecureRandom.base64(32), created_at: Time.now }
12
- ]
10
+ {
11
+ '1' => SecureRandom.base64(32),
12
+ '2' => SecureRandom.base64(32),
13
+ }
13
14
  }
14
15
 
15
16
  it "loads a valid keyring string" do
16
17
  keyring = Keyring.load(key_data.to_json)
17
18
  expect(keyring).to be_a Keyring
18
19
  expect(keyring.keys.count).to eq 2
19
- (0..1).each do |i|
20
- expect(keyring.keys[i].id).to eq key_data[i][:id]
21
- expect(keyring.keys[i].value).to eq key_data[i][:value]
22
- expect(keyring.keys[i].created_at).to be_within(60).of(key_data[i][:created_at])
20
+ key_data.keys.each do |key_id|
21
+ key = keyring.keys.find { |k| k.id == Integer(key_id) }
22
+ expect(key.value).to eq key_data[key_id]
23
23
  end
24
24
  end
25
25
 
@@ -28,19 +28,19 @@ module AttrVault
28
28
  end
29
29
 
30
30
  it "rejects unknown formats" do
31
- keys = key_data.map do |k|
32
- "<key id='#{k[:id]}' value='#{k[:value]}' created_at='#{k[:created_at]}'/>"
33
- end
31
+ keys = key_data.map do |k,v|
32
+ "<key id='#{k}' value='#{v}'/>"
33
+ end.join(',')
34
34
  expect { Keyring.load("<keys>#{keys}</keys>") }.to raise_error(InvalidKeyring)
35
35
  end
36
36
 
37
- it "rejects keys with missing ids" do
38
- key_data[0].delete :id
37
+ it "rejects keys with missing values" do
38
+ key_data['1'] = nil
39
39
  expect { Keyring.load(key_data) }.to raise_error(InvalidKeyring)
40
40
  end
41
41
 
42
- it "rejects keys with missing values" do
43
- key_data[0].delete :value
42
+ it "rejects keys with empty values" do
43
+ key_data['1'] = ''
44
44
  expect { Keyring.load(key_data) }.to raise_error(InvalidKeyring)
45
45
  end
46
46
  end
@@ -48,8 +48,8 @@ module AttrVault
48
48
 
49
49
  describe "#keys" do
50
50
  let(:keyring) { Keyring.new }
51
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
52
- let(:k2) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
51
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
52
+ let(:k2) { Key.new(2, ::SecureRandom.base64(32)) }
53
53
 
54
54
  before do
55
55
  keyring.add_key(k1)
@@ -64,8 +64,8 @@ module AttrVault
64
64
 
65
65
  describe "#fetch" do
66
66
  let(:keyring) { Keyring.new }
67
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
68
- let(:k2) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
67
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
68
+ let(:k2) { Key.new(2, ::SecureRandom.base64(32)) }
69
69
 
70
70
  before do
71
71
  keyring.add_key(k1)
@@ -85,8 +85,8 @@ module AttrVault
85
85
 
86
86
  describe "#has_key?" do
87
87
  let(:keyring) { Keyring.new }
88
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
89
- let(:k2) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
88
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
89
+ let(:k2) { Key.new(2, ::SecureRandom.base64(32)) }
90
90
 
91
91
  before do
92
92
  keyring.add_key(k1)
@@ -99,13 +99,13 @@ module AttrVault
99
99
  end
100
100
 
101
101
  it "is false if no such key is present" do
102
- expect(keyring.has_key?('867344d2-ac73-493b-9a9e-5fa688ba25ef')).to be false
102
+ expect(keyring.has_key?(5)).to be false
103
103
  end
104
104
  end
105
105
 
106
106
  describe "#add_key" do
107
107
  let(:keyring) { Keyring.new }
108
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
108
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
109
109
 
110
110
  it "adds keys" do
111
111
  expect(keyring.keys).to be_empty
@@ -116,8 +116,8 @@ module AttrVault
116
116
 
117
117
  describe "#drop_key" do
118
118
  let(:keyring) { Keyring.new }
119
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
120
- let(:k2) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
119
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
120
+ let(:k2) { Key.new(2, ::SecureRandom.base64(32)) }
121
121
 
122
122
  before do
123
123
  keyring.add_key(k1)
@@ -141,8 +141,8 @@ module AttrVault
141
141
 
142
142
  describe "#to_json" do
143
143
  let(:keyring) { Keyring.new }
144
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
145
- let(:k2) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
144
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
145
+ let(:k2) { Key.new(2, ::SecureRandom.base64(32)) }
146
146
 
147
147
  before do
148
148
  keyring.add_key(k1)
@@ -152,27 +152,22 @@ module AttrVault
152
152
  it "serializes the keyring to an expected format" do
153
153
  keyring_data = keyring.to_json
154
154
  reparsed = JSON.parse(keyring_data)
155
- expect(reparsed[0]["id"]).to eq k1.id
156
- expect(reparsed[0]["value"]).to eq k1.value
157
- expect(reparsed[0]["created_at"]).to eq k1.created_at.to_s
158
-
159
- expect(reparsed[1]["id"]).to eq k2.id
160
- expect(reparsed[1]["value"]).to eq k2.value
161
- expect(reparsed[1]["created_at"]).to eq k2.created_at.to_s
155
+ expect(reparsed[k1.id.to_s]).to eq k1.value
156
+ expect(reparsed[k2.id.to_s]).to eq k2.value
162
157
  end
163
158
  end
164
159
 
165
160
  describe "#current_key" do
166
161
  let(:keyring) { Keyring.new }
167
- let(:k1) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now - 3) }
168
- let(:k2) { Key.new(SecureRandom.uuid, SecureRandom.base64(32), Time.now) }
162
+ let(:k1) { Key.new(1, ::SecureRandom.base64(32)) }
163
+ let(:k2) { Key.new(2, ::SecureRandom.base64(32)) }
169
164
 
170
165
  before do
171
166
  keyring.add_key(k1)
172
167
  keyring.add_key(k2)
173
168
  end
174
169
 
175
- it "returns the newest key" do
170
+ it "returns the key with the largest id" do
176
171
  expect(keyring.current_key).to eq k2
177
172
  end
178
173
 
@@ -2,489 +2,454 @@ require 'spec_helper'
2
2
  require 'json'
3
3
 
4
4
  describe AttrVault do
5
- [ [ 'numeric', :items, [ 1, 2 ] ],
6
- [ 'uuid', :items_legacy,
7
- [ 'bf72f2ca-b478-4abf-a077-8eb7e071810f',
8
- '5a8d7477-6604-4801-9f1d-d4f412890cc3' ] ] ].each do |desc, table, key_ids|
9
-
10
- describe "with #{desc} key ids" do
11
- let(:key_values) do
12
- [ 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=',
13
- 'hUL1orBBRckZOuSuptRXYMV9lx5Qp54zwFUVwpwTpdk=' ]
5
+ let(:key_ids) { [ 1, 2 ] }
6
+ let(:key_values) do
7
+ [ 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=',
8
+ 'hUL1orBBRckZOuSuptRXYMV9lx5Qp54zwFUVwpwTpdk=' ]
9
+ end
10
+
11
+ def make_keyring(key_ids)
12
+ Hash[key_ids.zip(key_values)]
13
+ end
14
+
15
+ let(:key1_id) { key_ids.fetch(0) }
16
+ let(:key1) { keyring[key1_id] }
17
+
18
+ let(:key2_id) { key_ids.fetch(1) }
19
+ let(:key2) { keyring[key2_id] }
20
+
21
+ let(:key_id) { key1_id }
22
+ let(:key) { key1 }
23
+
24
+ let(:old_keyring) do
25
+ make_keyring(key_ids.take(1))
26
+ end
27
+ let(:new_keyring) do
28
+ make_keyring(key_ids)
29
+ end
30
+ let(:keyring) { old_keyring }
31
+
32
+ context "with a single encrypted column" do
33
+ let(:item) do
34
+ k = keyring.to_json
35
+ Class.new(Sequel::Model(:items)) do
36
+ include AttrVault
37
+ vault_keyring k
38
+ vault_attr :secret
14
39
  end
40
+ end
15
41
 
16
- def make_keyring(key_ids)
17
- paired = key_ids.zip(key_values)
18
- if key_ids.all? { |id| id.is_a? Integer }
19
- Hash[paired]
20
- else
21
- result = []
22
- paired.each_with_index do |(id,val), i|
23
- result << { id: id, created_at: Time.now + (i * 60), value: val }
24
- end
25
- result
42
+ context "with a new object" do
43
+ it "does not affect other attributes" do
44
+ not_secret = 'jimi hendrix was rather talented'
45
+ s = item.create(not_secret: not_secret)
46
+ s.reload
47
+ expect(s.not_secret).to eq(not_secret)
48
+ expect(s.this.where(not_secret: not_secret).count).to eq 1
49
+ end
50
+
51
+ it "encrypts non-empty values" do
52
+ secret = 'lady gaga? also rather talented'
53
+ s = item.create(secret: secret)
54
+ s.reload
55
+ expect(s.secret).to eq(secret)
56
+ s.columns.each do |col|
57
+ expect(s.this.where(Sequel.cast(Sequel.cast(col, :text), :bytea) => secret).count).to eq 0
26
58
  end
27
59
  end
28
60
 
29
- let(:key1_id) { key_ids.first }
30
- let(:key2_id) { key_ids.fetch(1) }
31
- let(:key_id) { key1_id }
61
+ it "stores empty values as empty" do
62
+ secret = ''
63
+ s = item.create(secret: secret)
64
+ s.reload
65
+ expect(s.secret).to eq('')
66
+ expect(s.secret_encrypted).to eq('')
67
+ end
32
68
 
33
- let(:old_keyring) do
34
- make_keyring(key_ids.take(1))
69
+ it "stores nil values as nil" do
70
+ s = item.create(secret: nil)
71
+ s.reload
72
+ expect(s.secret).to be_nil
73
+ expect(s.secret_encrypted).to be_nil
35
74
  end
36
- let(:new_keyring) do
37
- make_keyring(key_ids)
75
+
76
+ it "sets fields to empty that were previously not empty" do
77
+ s = item.create(secret: 'joyce hatto')
78
+ s.reload
79
+ s.update(secret: '')
80
+ s.reload
81
+ expect(s.secret).to eq ''
82
+ expect(s.secret_encrypted).not_to be_nil
38
83
  end
39
- let(:keyring) { old_keyring }
40
84
 
41
- let(:key1) do
42
- if keyring.is_a?(Hash)
43
- keyring[key1_id]
44
- else
45
- keyring.find { |k| k[:id] == key1_id }
46
- end
85
+ it "avoids updating existing values when those do not change" do
86
+ the_secret = "I'm not saying it was aliens..."
87
+ s = item.create(secret: the_secret)
88
+ s.reload
89
+ s.secret = the_secret
90
+ expect(s.save_changes).to be_nil
47
91
  end
48
- let(:key2) do
49
- if keyring.is_a?(Hash)
50
- keyring[key2_id]
51
- else
52
- keyring.find { |k| k[:id] == key2_id }
53
- end
92
+
93
+ it "stores the key id" do
94
+ secret = 'it was professor plum with the wrench in the library'
95
+ s = item.create(secret: secret)
96
+ s.reload
97
+ expect(s.key_id).to eq(key_id)
54
98
  end
55
- let(:key) { key1 }
56
-
57
- context "with a single encrypted column" do
58
- let(:item) do
59
- k = keyring.to_json
60
- t = table
61
- Class.new(Sequel::Model(t)) do
62
- include AttrVault
63
- vault_keyring k
64
- vault_attr :secret
65
- end
66
- end
99
+ end
67
100
 
68
- context "with a new object" do
69
- it "does not affect other attributes" do
70
- not_secret = 'jimi hendrix was rather talented'
71
- s = item.create(not_secret: not_secret)
72
- s.reload
73
- expect(s.not_secret).to eq(not_secret)
74
- expect(s.this.where(not_secret: not_secret).count).to eq 1
75
- end
76
-
77
- it "encrypts non-empty values" do
78
- secret = 'lady gaga? also rather talented'
79
- s = item.create(secret: secret)
80
- s.reload
81
- expect(s.secret).to eq(secret)
82
- s.columns.each do |col|
83
- expect(s.this.where(Sequel.cast(Sequel.cast(col, :text), :bytea) => secret).count).to eq 0
84
- end
85
- end
86
-
87
- it "stores empty values as empty" do
88
- secret = ''
89
- s = item.create(secret: secret)
90
- s.reload
91
- expect(s.secret).to eq('')
92
- expect(s.secret_encrypted).to eq('')
93
- end
94
-
95
- it "stores nil values as nil" do
96
- s = item.create(secret: nil)
97
- s.reload
98
- expect(s.secret).to be_nil
99
- expect(s.secret_encrypted).to be_nil
100
- end
101
-
102
- it "sets fields to empty that were previously not empty" do
103
- s = item.create(secret: 'joyce hatto')
104
- s.reload
105
- s.update(secret: '')
106
- s.reload
107
- expect(s.secret).to eq ''
108
- expect(s.secret_encrypted).not_to be_nil
109
- end
110
-
111
- it "avoids updating existing values when those do not change" do
112
- the_secret = "I'm not saying it was aliens..."
113
- s = item.create(secret: the_secret)
114
- s.reload
115
- s.secret = the_secret
116
- expect(s.save_changes).to be_nil
117
- end
118
-
119
- it "stores the key id" do
120
- secret = 'it was professor plum with the wrench in the library'
121
- s = item.create(secret: secret)
122
- s.reload
123
- expect(s.key_id).to eq(key_id)
124
- end
125
- end
101
+ context "with an existing object" do
102
+ it "does not affect other attributes" do
103
+ not_secret = 'soylent is not especially tasty'
104
+ s = item.create
105
+ s.update(not_secret: not_secret)
106
+ s.reload
107
+ expect(s.not_secret).to eq(not_secret)
108
+ expect(s.this.where(not_secret: not_secret).count).to eq 1
109
+ end
126
110
 
127
- context "with an existing object" do
128
- it "does not affect other attributes" do
129
- not_secret = 'soylent is not especially tasty'
130
- s = item.create
131
- s.update(not_secret: not_secret)
132
- s.reload
133
- expect(s.not_secret).to eq(not_secret)
134
- expect(s.this.where(not_secret: not_secret).count).to eq 1
135
- end
136
-
137
- it "encrypts non-empty values" do
138
- secret = 'soylent green is made of people'
139
- s = item.create
140
- s.update(secret: secret)
141
- s.reload
142
- expect(s.secret).to eq(secret)
143
- s.columns.each do |col|
144
- expect(s.this.where(Sequel.cast(Sequel.cast(col, :text), :bytea) => secret).count).to eq 0
145
- end
146
- end
147
-
148
- it "stores empty values as empty" do
149
- s = item.create(secret: "darth vader is luke's father")
150
- s.update(secret: '')
151
- s.reload
152
- expect(s.secret).to eq('')
153
- expect(s.secret_encrypted).to eq('')
154
- end
155
-
156
- it "leaves nil values as nil" do
157
- s = item.create(secret: "dr. crowe was dead all along")
158
- s.update(secret: nil)
159
- s.reload
160
- expect(s.secret).to be_nil
161
- expect(s.secret_encrypted).to be_nil
162
- end
163
-
164
- it "stores the key id" do
165
- secret = 'animal style'
166
- s = item.create
167
- s.update(secret: secret)
168
- s.reload
169
- expect(s.key_id).to eq(key_id)
170
- end
171
-
172
- it "reads a never-set encrypted field as nil" do
173
- s = item.create
174
- expect(s.secret).to be_nil
175
- end
176
-
177
- it "avoids updating existing values when those do not change" do
178
- the_secret = "Satoshi Nakamoto"
179
- s = item.create
180
- s.update(secret: the_secret)
181
- s.secret = the_secret
182
- expect(s.save_changes).to be_nil
183
- end
184
-
185
- it "reads the correct value for a dirty field before the object is saved" do
186
- s = item.create
187
- secret = 'mcmurphy is lobotomized =('
188
- s.secret = secret
189
- expect(s.secret).to eq secret
190
- end
111
+ it "encrypts non-empty values" do
112
+ secret = 'soylent green is made of people'
113
+ s = item.create
114
+ s.update(secret: secret)
115
+ s.reload
116
+ expect(s.secret).to eq(secret)
117
+ s.columns.each do |col|
118
+ expect(s.this.where(Sequel.cast(Sequel.cast(col, :text), :bytea) => secret).count).to eq 0
191
119
  end
192
120
  end
193
121
 
194
- context "with multiple encrypted columns" do
195
- let(:key_data) do
196
- [ { id: 1,
197
- value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' } ].to_json
198
- end
199
- let(:item) do
200
- k = key_data
201
- Class.new(Sequel::Model(:items)) do
202
- include AttrVault
203
- vault_keyring k
204
- vault_attr :secret
205
- vault_attr :other
206
- end
207
- end
122
+ it "stores empty values as empty" do
123
+ s = item.create(secret: "darth vader is luke's father")
124
+ s.update(secret: '')
125
+ s.reload
126
+ expect(s.secret).to eq('')
127
+ expect(s.secret_encrypted).to eq('')
128
+ end
208
129
 
209
- it "does not clobber other attributes" do
210
- secret1 = "superman is really mild-mannered reporter clark kent"
211
- secret2 = "batman is really millionaire playboy bruce wayne"
212
- s = item.create(secret: secret1)
213
- s.reload
214
- expect(s.secret).to eq secret1
215
- s.update(other: secret2)
216
- s.reload
217
- expect(s.secret).to eq secret1
218
- expect(s.other).to eq secret2
219
- end
130
+ it "leaves nil values as nil" do
131
+ s = item.create(secret: "dr. crowe was dead all along")
132
+ s.update(secret: nil)
133
+ s.reload
134
+ expect(s.secret).to be_nil
135
+ expect(s.secret_encrypted).to be_nil
220
136
  end
221
137
 
222
- context "with items encrypted with an older key" do
223
- let(:key1_id) { 1 }
224
- let(:key1) do
225
- { id: key1_id,
226
- value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }
227
- end
228
- let(:key2_id) { 2 }
229
- let(:key2) do
230
- { id: key2_id,
231
- value: 'hUL1orBBRckZOuSuptRXYMV9lx5Qp54zwFUVwpwTpdk=' }
232
- end
233
- let(:partial_keyring) { [key1].to_json }
234
- let(:full_keyring) { [key1, key2].to_json }
235
- let(:item1) do
236
- k = partial_keyring
237
- Class.new(Sequel::Model(:items)) do
238
- include AttrVault
239
- vault_keyring k
240
- vault_attr :secret
241
- vault_attr :other
242
- end
243
- end
244
- let(:item2) do
245
- k = full_keyring
246
- Class.new(Sequel::Model(:items)) do
247
- include AttrVault
248
- vault_keyring k
249
- vault_attr :secret
250
- vault_attr :other
251
- end
252
- end
138
+ it "stores the key id" do
139
+ secret = 'animal style'
140
+ s = item.create
141
+ s.update(secret: secret)
142
+ s.reload
143
+ expect(s.key_id).to eq(key_id)
144
+ end
253
145
 
254
- it "rewrites the items using the current key" do
255
- secret1 = 'mrs. doubtfire is really a man'
256
- secret2 = 'tootsie? also a man'
257
- record = item1.create(secret: secret1)
258
- expect(record.key_id).to eq key1_id
259
- expect(record.secret).to eq secret1
146
+ it "reads a never-set encrypted field as nil" do
147
+ s = item.create
148
+ expect(s.secret).to be_nil
149
+ end
150
+
151
+ it "avoids updating existing values when those do not change" do
152
+ the_secret = "Satoshi Nakamoto"
153
+ s = item.create
154
+ s.update(secret: the_secret)
155
+ s.secret = the_secret
156
+ expect(s.save_changes).to be_nil
157
+ end
260
158
 
261
- old_secret_encrypted = record.secret_encrypted
159
+ it "reads the correct value for a dirty field before the object is saved" do
160
+ s = item.create
161
+ secret = 'mcmurphy is lobotomized =('
162
+ s.secret = secret
163
+ expect(s.secret).to eq secret
164
+ end
165
+ end
166
+ end
262
167
 
263
- new_key_record = item2[record.id]
264
- new_key_record.update(secret: secret2)
265
- new_key_record.reload
168
+ context "with multiple encrypted columns" do
169
+ let(:key_data) do
170
+ { "1" => 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }
171
+ end
172
+ let(:item) do
173
+ k = key_data.to_json
174
+ Class.new(Sequel::Model(:items)) do
175
+ include AttrVault
176
+ vault_keyring k
177
+ vault_attr :secret
178
+ vault_attr :other
179
+ end
180
+ end
266
181
 
267
- expect(new_key_record.key_id).to eq key2_id
268
- expect(new_key_record.secret).to eq secret2
269
- expect(new_key_record.secret_encrypted).not_to eq old_secret_encrypted
270
- end
182
+ it "does not clobber other attributes" do
183
+ secret1 = "superman is really mild-mannered reporter clark kent"
184
+ secret2 = "batman is really millionaire playboy bruce wayne"
185
+ s = item.create(secret: secret1)
186
+ s.reload
187
+ expect(s.secret).to eq secret1
188
+ s.update(other: secret2)
189
+ s.reload
190
+ expect(s.secret).to eq secret1
191
+ expect(s.other).to eq secret2
192
+ end
193
+ end
194
+
195
+ context "with items encrypted with an older key" do
196
+ let(:key1_id) { 1 }
197
+ let(:key1) do
198
+ { key1_id => 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }
199
+ end
200
+ let(:key2_id) { 2 }
201
+ let(:key2) do
202
+ { key2_id => 'hUL1orBBRckZOuSuptRXYMV9lx5Qp54zwFUVwpwTpdk=' }
203
+ end
204
+ let(:partial_keyring) { key1.to_json }
205
+ let(:full_keyring) { key1.merge(key2).to_json }
206
+ let(:item1) do
207
+ k = partial_keyring
208
+ Class.new(Sequel::Model(:items)) do
209
+ include AttrVault
210
+ vault_keyring k
211
+ vault_attr :secret
212
+ vault_attr :other
213
+ end
214
+ end
215
+ let(:item2) do
216
+ k = full_keyring
217
+ Class.new(Sequel::Model(:items)) do
218
+ include AttrVault
219
+ vault_keyring k
220
+ vault_attr :secret
221
+ vault_attr :other
222
+ end
223
+ end
271
224
 
272
- it "rewrites the items using the current key even if they are not updated" do
273
- secret1 = 'the planet of the apes is really earth'
274
- secret2 = 'the answer is 42'
275
- record = item1.create(secret: secret1)
276
- expect(record.key_id).to eq key1_id
277
- expect(record.secret).to eq secret1
225
+ it "rewrites the items using the current key" do
226
+ secret1 = 'mrs. doubtfire is really a man'
227
+ secret2 = 'tootsie? also a man'
228
+ record = item1.create(secret: secret1)
229
+ expect(record.key_id).to eq key1_id
230
+ expect(record.secret).to eq secret1
278
231
 
279
- old_secret_encrypted = record.secret_encrypted
232
+ old_secret_encrypted = record.secret_encrypted
280
233
 
281
- new_key_record = item2[record.id]
282
- new_key_record.update(other: secret2)
283
- new_key_record.reload
234
+ new_key_record = item2[record.id]
235
+ new_key_record.update(secret: secret2)
236
+ new_key_record.reload
284
237
 
285
- expect(new_key_record.key_id).to eq key2_id
286
- expect(new_key_record.secret).to eq secret1
287
- expect(new_key_record.secret_encrypted).not_to eq old_secret_encrypted
288
- expect(new_key_record.other).to eq secret2
289
- end
238
+ expect(new_key_record.key_id).to eq key2_id
239
+ expect(new_key_record.secret).to eq secret2
240
+ expect(new_key_record.secret_encrypted).not_to eq old_secret_encrypted
241
+ end
242
+
243
+ it "rewrites the items using the current key even if they are not updated" do
244
+ secret1 = 'the planet of the apes is really earth'
245
+ secret2 = 'the answer is 42'
246
+ record = item1.create(secret: secret1)
247
+ expect(record.key_id).to eq key1_id
248
+ expect(record.secret).to eq secret1
249
+
250
+ old_secret_encrypted = record.secret_encrypted
251
+
252
+ new_key_record = item2[record.id]
253
+ new_key_record.update(other: secret2)
254
+ new_key_record.reload
255
+
256
+ expect(new_key_record.key_id).to eq key2_id
257
+ expect(new_key_record.secret).to eq secret1
258
+ expect(new_key_record.secret_encrypted).not_to eq old_secret_encrypted
259
+ expect(new_key_record.other).to eq secret2
260
+ end
261
+ end
262
+
263
+ context "with plaintext source fields" do
264
+ let(:key_id) { 1 }
265
+ let(:key_data) do
266
+ { key_id => 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }.to_json
267
+ end
268
+ let(:item1) do
269
+ k = key_data
270
+ Class.new(Sequel::Model(:items)) do
271
+ include AttrVault
272
+ vault_keyring k
273
+ vault_attr :secret
274
+ vault_attr :other
275
+ end
276
+ end
277
+ let(:item2) do
278
+ k = key_data
279
+ Class.new(Sequel::Model(:items)) do
280
+ include AttrVault
281
+ vault_keyring k
282
+ vault_attr :secret, migrate_from_field: :not_secret
283
+ vault_attr :other, migrate_from_field: :other_not_secret
290
284
  end
285
+ end
291
286
 
292
- context "with plaintext source fields" do
293
- let(:key_id) { 1 }
294
- let(:key_data) do
295
- [ { id: key_id,
296
- value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' } ].to_json
297
- end
298
- let(:item1) do
299
- k = key_data
300
- Class.new(Sequel::Model(:items)) do
301
- include AttrVault
302
- vault_keyring k
303
- vault_attr :secret
304
- vault_attr :other
305
- end
306
- end
307
- let(:item2) do
308
- k = key_data
309
- Class.new(Sequel::Model(:items)) do
310
- include AttrVault
311
- vault_keyring k
312
- vault_attr :secret, migrate_from_field: :not_secret
313
- vault_attr :other, migrate_from_field: :other_not_secret
314
- end
315
- end
287
+ it "copies a plaintext field to an encrypted field when saving the object" do
288
+ becomes_secret = 'the location of the lost continent of atlantis'
289
+ s = item1.create(not_secret: becomes_secret)
290
+ reloaded = item2[s.id]
291
+ expect(reloaded.not_secret).to eq becomes_secret
292
+ reloaded.save
293
+ reloaded.reload
294
+ expect(reloaded.not_secret).to be_nil
295
+ expect(reloaded.secret).to eq becomes_secret
296
+ end
316
297
 
317
- it "copies a plaintext field to an encrypted field when saving the object" do
318
- becomes_secret = 'the location of the lost continent of atlantis'
319
- s = item1.create(not_secret: becomes_secret)
320
- reloaded = item2[s.id]
321
- expect(reloaded.not_secret).to eq becomes_secret
322
- reloaded.save
323
- reloaded.reload
324
- expect(reloaded.not_secret).to be_nil
325
- expect(reloaded.secret).to eq becomes_secret
326
- end
298
+ it "supports converting multiple fields" do
299
+ becomes_secret1 = 'the location of the fountain of youth'
300
+ becomes_secret2 = 'the location of the lost city of el dorado'
301
+ s = item1.create(not_secret: becomes_secret1, other_not_secret: becomes_secret2)
302
+ reloaded = item2[s.id]
303
+ expect(reloaded.not_secret).to eq becomes_secret1
304
+ expect(reloaded.other_not_secret).to eq becomes_secret2
305
+ reloaded.save
306
+ reloaded.reload
307
+ expect(reloaded.not_secret).to be_nil
308
+ expect(reloaded.secret).to eq becomes_secret1
309
+ expect(reloaded.other_not_secret).to be_nil
310
+ expect(reloaded.other).to eq becomes_secret2
311
+ end
327
312
 
328
- it "supports converting multiple fields" do
329
- becomes_secret1 = 'the location of the fountain of youth'
330
- becomes_secret2 = 'the location of the lost city of el dorado'
331
- s = item1.create(not_secret: becomes_secret1, other_not_secret: becomes_secret2)
332
- reloaded = item2[s.id]
333
- expect(reloaded.not_secret).to eq becomes_secret1
334
- expect(reloaded.other_not_secret).to eq becomes_secret2
335
- reloaded.save
336
- reloaded.reload
337
- expect(reloaded.not_secret).to be_nil
338
- expect(reloaded.secret).to eq becomes_secret1
339
- expect(reloaded.other_not_secret).to be_nil
340
- expect(reloaded.other).to eq becomes_secret2
341
- end
313
+ it "nils out the plaintext field and persists the encrypted field on save" do
314
+ becomes_secret = 'the location of all those socks that disappear from the dryer'
315
+ new_secret = 'the location of pliny the younger drafts'
316
+ s = item1.create(not_secret: becomes_secret)
317
+ reloaded = item2[s.id]
318
+ expect(reloaded.secret).to eq(becomes_secret)
319
+ reloaded.secret = new_secret
320
+ expect(reloaded.secret).to eq(new_secret)
321
+ reloaded.save
322
+ expect(reloaded.secret).to eq(new_secret)
323
+ expect(reloaded.not_secret).to be_nil
324
+ end
325
+ end
342
326
 
343
- it "nils out the plaintext field and persists the encrypted field on save" do
344
- becomes_secret = 'the location of all those socks that disappear from the dryer'
345
- new_secret = 'the location of pliny the younger drafts'
346
- s = item1.create(not_secret: becomes_secret)
347
- reloaded = item2[s.id]
348
- expect(reloaded.secret).to eq(becomes_secret)
349
- reloaded.secret = new_secret
350
- expect(reloaded.secret).to eq(new_secret)
351
- reloaded.save
352
- expect(reloaded.secret).to eq(new_secret)
353
- expect(reloaded.not_secret).to be_nil
354
- end
327
+ context "with renamed database fields" do
328
+ let(:key_data) do
329
+ { '1' => 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }.to_json
330
+ end
331
+
332
+ it "supports renaming the encrypted field" do
333
+ k = key_data
334
+ item = Class.new(Sequel::Model(:items)) do
335
+ include AttrVault
336
+ vault_keyring k
337
+ vault_attr :classified_info,
338
+ encrypted_field: :secret_encrypted
355
339
  end
356
340
 
357
- context "with renamed database fields" do
358
- let(:key_data) do
359
- [ { id: 1,
360
- value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' } ].to_json
361
- end
341
+ secret = "we've secretly replaced the fine coffee they usually serve with Folgers Crystals"
342
+ s = item.create(classified_info: secret)
343
+ s.reload
344
+ expect(s.classified_info).to eq secret
345
+ expect(s.secret_encrypted).not_to eq secret
346
+ end
362
347
 
363
- it "supports renaming the encrypted field" do
364
- k = key_data
365
- item = Class.new(Sequel::Model(:items)) do
366
- include AttrVault
367
- vault_keyring k
368
- vault_attr :classified_info,
369
- encrypted_field: :secret_encrypted
370
- end
371
-
372
- secret = "we've secretly replaced the fine coffee they usually serve with Folgers Crystals"
373
- s = item.create(classified_info: secret)
374
- s.reload
375
- expect(s.classified_info).to eq secret
376
- expect(s.secret_encrypted).not_to eq secret
377
- end
348
+ it "supports renaming the key id field" do
349
+ k = key_data
350
+ item = Class.new(Sequel::Model(:items)) do
351
+ include AttrVault
352
+ vault_keyring k, key_field: :alt_key_id
353
+ vault_attr :secret
354
+ end
378
355
 
379
- it "supports renaming the key id field" do
380
- k = key_data
381
- item = Class.new(Sequel::Model(:items)) do
382
- include AttrVault
383
- vault_keyring k, key_field: :alt_key_id
384
- vault_attr :secret
385
- end
356
+ secret = "up up down down left right left right b a"
357
+ s = item.create(secret: secret)
358
+ s.reload
359
+ expect(s.secret).to eq secret
360
+ expect(s.secret_encrypted).not_to eq secret
361
+ expect(s.alt_key_id).not_to be_nil
362
+ expect(s.key_id).to be_nil
363
+ end
364
+ end
386
365
 
387
- secret = "up up down down left right left right b a"
388
- s = item.create(secret: secret)
389
- s.reload
390
- expect(s.secret).to eq secret
391
- expect(s.secret_encrypted).not_to eq secret
392
- expect(s.alt_key_id).not_to be_nil
393
- expect(s.key_id).to be_nil
394
- end
366
+ context "with a digest field" do
367
+ let(:key_id) { 1 }
368
+ let(:key) do
369
+ { key_id => 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }
370
+ end
371
+ let(:item) do
372
+ k = key.to_json
373
+ Class.new(Sequel::Model(:items)) do
374
+ include AttrVault
375
+ vault_keyring k
376
+ vault_attr :secret, digest_field: :secret_digest
377
+ vault_attr :other, digest_field: :other_digest
395
378
  end
379
+ end
396
380
 
397
- context "with a digest field" do
398
- let(:key_id) { 1 }
399
- let(:key) do
400
- [ { id: key_id,
401
- value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' } ]
402
- end
403
- let(:item) do
404
- k = key.to_json
405
- Class.new(Sequel::Model(:items)) do
406
- include AttrVault
407
- vault_keyring k
408
- vault_attr :secret, digest_field: :secret_digest
409
- vault_attr :other, digest_field: :other_digest
410
- end
411
- end
381
+ def test_digest(key, data)
382
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'),
383
+ key[key_id], data)
384
+ end
412
385
 
413
- def test_digest(key, data)
414
- OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'),
415
- key.first.fetch(:value), data)
416
- end
386
+ def count_matching_digests(item_class, digest_field, secret)
387
+ item.where({digest_field => item_class.vault_digests(secret)}).count
388
+ end
417
389
 
418
- def count_matching_digests(item_class, digest_field, secret)
419
- item.where({digest_field => item_class.vault_digests(secret)}).count
420
- end
390
+ it "records the hmac of the plaintext value" do
391
+ secret = 'snape kills dumbledore'
392
+ s = item.create(secret: secret)
393
+ expect(s.secret_digest).to eq(test_digest(key, secret))
394
+ expect(count_matching_digests(item, :secret_digest, secret)).to eq(1)
395
+ end
421
396
 
422
- it "records the hmac of the plaintext value" do
423
- secret = 'snape kills dumbledore'
424
- s = item.create(secret: secret)
425
- expect(s.secret_digest).to eq(test_digest(key, secret))
426
- expect(count_matching_digests(item, :secret_digest, secret)).to eq(1)
427
- end
397
+ it "can record multiple digest fields" do
398
+ secret = 'joffrey kills ned'
399
+ other_secret = '"gomer pyle" lawrence kills himself'
400
+ s = item.create(secret: secret, other: other_secret)
401
+ expect(s.secret_digest).to eq(test_digest(key, secret))
402
+ expect(s.other_digest).to eq(test_digest(key, other_secret))
403
+
404
+ # Check vault_digests feature matching against the database.
405
+ expect(count_matching_digests(item, :secret_digest, secret)).to eq(1)
406
+ expect(count_matching_digests(item, :other_digest, other_secret)).to eq(1)
407
+
408
+ # Negative tests for mismatched digesting.
409
+ expect(count_matching_digests(item, :secret_digest, other_secret))
410
+ .to eq(0)
411
+ expect(count_matching_digests(item, :other_digest, secret)).to eq(0)
412
+ end
428
413
 
429
- it "can record multiple digest fields" do
430
- secret = 'joffrey kills ned'
431
- other_secret = '"gomer pyle" lawrence kills himself'
432
- s = item.create(secret: secret, other: other_secret)
433
- expect(s.secret_digest).to eq(test_digest(key, secret))
434
- expect(s.other_digest).to eq(test_digest(key, other_secret))
435
-
436
- # Check vault_digests feature matching against the database.
437
- expect(count_matching_digests(item, :secret_digest, secret)).to eq(1)
438
- expect(count_matching_digests(item, :other_digest, other_secret)).to eq(1)
439
-
440
- # Negative tests for mismatched digesting.
441
- expect(count_matching_digests(item, :secret_digest, other_secret))
442
- .to eq(0)
443
- expect(count_matching_digests(item, :other_digest, secret)).to eq(0)
444
- end
414
+ it "records the digest for an empty field" do
415
+ s = item.create(secret: '', other: '')
416
+ expect(s.secret_digest).to eq(test_digest(key, ''))
417
+ expect(s.other_digest).to eq(test_digest(key, ''))
418
+ end
445
419
 
446
- it "records the digest for an empty field" do
447
- s = item.create(secret: '', other: '')
448
- expect(s.secret_digest).to eq(test_digest(key, ''))
449
- expect(s.other_digest).to eq(test_digest(key, ''))
450
- end
420
+ it "records the digest of a nil field" do
421
+ s = item.create
422
+ expect(s.secret_digest).to be_nil
423
+ expect(s.other_digest).to be_nil
424
+ end
425
+ end
426
+ end
451
427
 
452
- it "records the digest of a nil field" do
453
- s = item.create
454
- expect(s.secret_digest).to be_nil
455
- expect(s.other_digest).to be_nil
456
- end
457
- end
428
+ describe "stress test" do
429
+ let(:key_id) { 1 }
430
+ let(:key_data) do
431
+ { key_id =>'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' }.to_json
432
+ end
433
+ let(:item) do
434
+ k = key_data
435
+ Class.new(Sequel::Model(:items)) do
436
+ include AttrVault
437
+ vault_keyring k
438
+ vault_attr :secret
458
439
  end
440
+ end
459
441
 
460
- describe "stress test" do
461
- let(:key_id) { 1 }
462
- let(:key_data) do
463
- [ { id: key_id,
464
- value: 'aFJDXs+798G7wgS/nap21LXIpm/Rrr39jIVo2m/cdj8=' } ].to_json
465
- end
466
- let(:item) do
467
- k = key_data
468
- Class.new(Sequel::Model(:items)) do
469
- include AttrVault
470
- vault_keyring k
471
- vault_attr :secret
442
+ it "works" do
443
+ 3.times.map do
444
+ Thread.new do
445
+ s = item.create(secret: 'that Commander Keen level in DOOM II')
446
+ 1_000.times do
447
+ new_secret = [ nil, '', 36.times.map { (0..255).to_a.sample.chr }.join('') ].sample
448
+ s.update(secret: new_secret)
449
+ s.reload
450
+ expect(s.secret).to eq new_secret
472
451
  end
473
452
  end
474
-
475
- it "works" do
476
- 3.times.map do
477
- Thread.new do
478
- s = item.create(secret: 'that Commander Keen level in DOOM II')
479
- 1_000.times do
480
- new_secret = [ nil, '', 36.times.map { (0..255).to_a.sample.chr }.join('') ].sample
481
- s.update(secret: new_secret)
482
- s.reload
483
- expect(s.secret).to eq new_secret
484
- end
485
- end
486
- end.map(&:join)
487
- end
488
- end
453
+ end.map(&:join)
489
454
  end
490
455
  end