blind_index 1.0.2 → 2.0.0

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
  SHA256:
3
- metadata.gz: 2f39918c983140363af00fe1afeda800a7476a5727fd2c676c713b92222c1f35
4
- data.tar.gz: b83995b7b33eddacb2988e7fdb8816a1b2fc644161114b5e191f3d9bb2de2bd2
3
+ metadata.gz: f656fa1765df9bf2bcfa9d994ccb9b5cf0504f27f7524d159323126462d973e0
4
+ data.tar.gz: 88d5f2cd786f840e75540a204dbe7fbb74794de954978d90cdc69297078cf752
5
5
  SHA512:
6
- metadata.gz: b2d03f17acbac80b5ecf8ac1f97321f3c68b0c689db1e50d95cc81cb3769086a5d8da6f8f0386325555c8eb715d46555281e00af38804cd240cadbefa45e86a4
7
- data.tar.gz: 59092061d3b415d399f1a20232d680591ccc832af79387945d23d9309062bf10a4b8105062ea277af2fa06b454cd75f9ad3d49a44dabac1a990e2856bc700849
6
+ metadata.gz: 139d3d8f3aca413d0ee5045fe3212e6ed3327cdb6d0c60cb7eb2b314b4eb849abb42dcf17e386e23fb5521db2875b6c502b143fc46dc3305ad38151688b610be
7
+ data.tar.gz: 0f5df7f99a1b79f2eab0bc9ff959b9fd21c238e772e09265ef88690678134f539968d9aebe508b88e9881657563013cd53031947d6a6322e08c7e29d95f45902
@@ -1,3 +1,10 @@
1
+ ## 2.0.0 (2019-02-10)
2
+
3
+ - Blind indexes are updated immediately instead of in a `before_validation` callback
4
+ - Better Lockbox integration - no need to generate a separate key
5
+ - The `argon2` gem has been replaced with `argon2-kdf` for less dependencies and Windows support
6
+ - Removed deprecated `compute_email_bidx`
7
+
1
8
  ## 1.0.2 (2019-12-26)
2
9
 
3
10
  - Fixed `OpenSSL::KDF` error on some platforms
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017-2019 Andrew Kane
1
+ Copyright (c) 2017-2020 Andrew Kane
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -26,37 +26,13 @@ Add this line to your application’s Gemfile:
26
26
  gem 'blind_index'
27
27
  ```
28
28
 
29
- On Windows, also add:
30
-
31
- ```ruby
32
- gem 'argon2', git: 'https://github.com/technion/ruby-argon2.git', submodules: true
33
- ```
34
-
35
- Until `argon2 > 2.0.2` is released.
36
-
37
29
  ## Getting Started
38
30
 
39
- > Note: Your model should already be set up with Lockbox or attr_encrypted. The examples are for a `User` model with `encrypts :email` or `attr_encrypted :email`. See the full examples for [Lockbox](https://ankane.org/securing-user-emails-lockbox) and [attr_encrypted](https://ankane.org/securing-user-emails-in-rails) if needed.
40
-
41
- First, generate a key
42
-
43
- ```ruby
44
- BlindIndex.generate_key
45
- ```
46
-
47
- Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production. Keys don’t need to be hex-encoded, but it’s often easier to store them this way.
48
-
49
- Set the following environment variable with your key (you can use this one in development)
50
-
51
- ```sh
52
- BLIND_INDEX_MASTER_KEY=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
53
- ```
31
+ Your model should already be set up with Lockbox or attr_encrypted. The examples are for a `User` model with `encrypts :email` or `attr_encrypted :email`. See the full examples for [Lockbox](https://ankane.org/securing-user-emails-lockbox) and [attr_encrypted](https://ankane.org/securing-user-emails-in-rails) if needed.
54
32
 
55
- or create `config/initializers/blind_index.rb` with something like
33
+ Also, if you use attr_encrypted, [generate a key](#key-generation).
56
34
 
57
- ```ruby
58
- BlindIndex.master_key = Rails.application.credentials.blind_index_master_key
59
- ```
35
+ ---
60
36
 
61
37
  Create a migration to add a column for the blind index
62
38
 
@@ -169,18 +145,37 @@ You can also use virtual attributes to index data from multiple columns:
169
145
  ```ruby
170
146
  class User < ApplicationRecord
171
147
  attribute :initials, :string
148
+ blind_index :initials
172
149
 
173
- # must come before the blind_index method so it runs first
174
150
  before_validation :set_initials, if: -> { changes.key?(:first_name) || changes.key?(:last_name) }
175
151
 
176
- blind_index :initials
177
-
178
152
  def set_initials
179
153
  self.initials = "#{first_name[0]}#{last_name[0]}"
180
154
  end
181
155
  end
182
156
  ```
183
157
 
158
+ ## Migrating Data
159
+
160
+ If you’re encrypting a column and adding a blind index at the same time, use the `migrating` option.
161
+
162
+ ```ruby
163
+ class User < ApplicationRecord
164
+ blind_index :email, migrating: true
165
+ end
166
+ ```
167
+
168
+ This allows you to backfill records while still querying the unencrypted field.
169
+
170
+ ```ruby
171
+ User.unscoped.where(email_bidx: nil).find_each do |user|
172
+ user.compute_migrated_email_bidx
173
+ user.save(validate: false)
174
+ end
175
+ ```
176
+
177
+ Once that completes, you can remove the `migrating` option.
178
+
184
179
  ## Key Rotation
185
180
 
186
181
  To rotate keys without downtime, add a new column:
@@ -260,6 +255,30 @@ class User
260
255
  end
261
256
  ```
262
257
 
258
+ ## Key Generation
259
+
260
+ This is optional for Lockbox, as its master key is used by default.
261
+
262
+ Generate a key with:
263
+
264
+ ```ruby
265
+ BlindIndex.generate_key
266
+ ```
267
+
268
+ Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production. Keys don’t need to be hex-encoded, but it’s often easier to store them this way.
269
+
270
+ Set the following environment variable with your key (you can use this one in development)
271
+
272
+ ```sh
273
+ BLIND_INDEX_MASTER_KEY=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
274
+ ```
275
+
276
+ or create `config/initializers/blind_index.rb` with something like
277
+
278
+ ```ruby
279
+ BlindIndex.master_key = Rails.application.credentials.blind_index_master_key
280
+ ```
281
+
263
282
  ## Reference
264
283
 
265
284
  Set default options in an initializer with:
@@ -301,6 +320,14 @@ One alternative to blind indexing is to use a deterministic encryption scheme, l
301
320
 
302
321
  ## Upgrading
303
322
 
323
+ ### 2.0.0
324
+
325
+ 2.0.0 brings a number of improvements.
326
+
327
+ - Blind indexes are updated immediately instead of in a `before_validation` callback
328
+ - Better Lockbox integration - no need to generate a separate key
329
+ - There’s a new gem for Argon2 that has no dependencies and (officially) supports Windows
330
+
304
331
  ### 1.0.0
305
332
 
306
333
  1.0.0 brings a number of improvements. Here are a few to be aware of:
@@ -1,7 +1,7 @@
1
1
  # dependencies
2
2
  require "active_support"
3
3
  require "openssl"
4
- require "argon2"
4
+ require "argon2/kdf"
5
5
 
6
6
  # modules
7
7
  require "blind_index/key_generator"
@@ -18,7 +18,7 @@ module BlindIndex
18
18
  self.default_options = {}
19
19
 
20
20
  def self.master_key
21
- @master_key ||= ENV["BLIND_INDEX_MASTER_KEY"]
21
+ @master_key ||= ENV["BLIND_INDEX_MASTER_KEY"] || (defined?(Lockbox.master_key) && Lockbox.master_key)
22
22
  end
23
23
 
24
24
  def self.generate_bidx(value, key:, **options)
@@ -64,7 +64,7 @@ module BlindIndex
64
64
  # use same bounds as rbnacl
65
65
  raise BlindIndex::Error, "m must be between 3 and 22" if m < 3 || m > 22
66
66
 
67
- [Argon2::Engine.hash_argon2id(value, key, t, m, size)].pack("H*")
67
+ Argon2::KDF.argon2id(value, salt: key, t: t, m: m, p: 1, length: size)
68
68
  when :pbkdf2_sha256
69
69
  iterations = cost_options[:iterations] || options[:iterations] || (options[:slow] ? 100000 : 10000)
70
70
  OpenSSL::PKCS5.pbkdf2_hmac(value, key, iterations, size, "sha256")
@@ -78,7 +78,7 @@ module BlindIndex
78
78
  # use same bounds as rbnacl
79
79
  raise BlindIndex::Error, "m must be between 3 and 22" if m < 3 || m > 22
80
80
 
81
- [Argon2::Engine.hash_argon2i(value, key, t, m, size)].pack("H*")
81
+ Argon2::KDF.argon2i(value, salt: key, t: t, m: m, p: 1, length: size)
82
82
  when :scrypt
83
83
  n = cost_options[:n] || 4096
84
84
  r = cost_options[:r] || 8
@@ -133,12 +133,7 @@ ActiveSupport.on_load(:active_record) do
133
133
  require "blind_index/extensions"
134
134
  extend BlindIndex::Model
135
135
 
136
- if defined?(ActiveRecord::TableMetadata)
137
- ActiveRecord::TableMetadata.prepend(BlindIndex::Extensions::TableMetadata)
138
- else
139
- ActiveRecord::PredicateBuilder.singleton_class.prepend(BlindIndex::Extensions::PredicateBuilder)
140
- end
141
-
136
+ ActiveRecord::TableMetadata.prepend(BlindIndex::Extensions::TableMetadata)
142
137
  ActiveRecord::DynamicMatchers::Method.prepend(BlindIndex::Extensions::DynamicMatchers)
143
138
 
144
139
  unless ActiveRecord::VERSION::STRING.start_with?("5.1.")
@@ -1,6 +1,5 @@
1
1
  module BlindIndex
2
2
  module Extensions
3
- # ActiveRecord 5.0+
4
3
  module TableMetadata
5
4
  def resolve_column_aliases(hash)
6
5
  new_hash = super
@@ -29,37 +28,6 @@ module BlindIndex
29
28
  end
30
29
  end
31
30
 
32
- # ActiveRecord 4.2
33
- module PredicateBuilder
34
- def resolve_column_aliases(klass, hash)
35
- new_hash = super
36
- if has_blind_indexes?(klass)
37
- hash.each do |key, _|
38
- if key.respond_to?(:to_sym) && (bi = klass.blind_indexes[key.to_sym]) && !new_hash[key].is_a?(ActiveRecord::StatementCache::Substitute)
39
- value = new_hash.delete(key)
40
- new_hash[bi[:bidx_attribute]] =
41
- if value.is_a?(Array)
42
- value.map { |v| BlindIndex.generate_bidx(v, bi) }
43
- else
44
- BlindIndex.generate_bidx(value, bi)
45
- end
46
- end
47
- end
48
- end
49
- new_hash
50
- end
51
-
52
- @@blind_index_cache = {}
53
-
54
- # memoize for performance
55
- def has_blind_indexes?(klass)
56
- if @@blind_index_cache[klass].nil?
57
- @@blind_index_cache[klass] = klass.respond_to?(:blind_indexes)
58
- end
59
- @@blind_index_cache[klass]
60
- end
61
- end
62
-
63
31
  module UniquenessValidator
64
32
  if ActiveRecord::VERSION::STRING >= "5.2"
65
33
  def build_relation(klass, attribute, value)
@@ -64,25 +64,30 @@ module BlindIndex
64
64
  BlindIndex.generate_bidx(value, **blind_indexes[name])
65
65
  end
66
66
 
67
- define_singleton_method method_name do |value|
68
- ActiveSupport::Deprecation.warn("Use #{class_method_name} instead")
69
- send(class_method_name, value)
70
- end
71
-
72
67
  define_method method_name do
73
68
  self.send("#{bidx_attribute}=", self.class.send(class_method_name, send(attribute)))
74
69
  end
75
70
 
76
71
  if callback
77
- if defined?(ActiveRecord) && self < ActiveRecord::Base
78
- # Active Record
79
- # prevent deprecation warnings
80
- before_validation method_name, if: -> { changes.key?(attribute.to_s) }
81
- else
82
- # Mongoid
83
- # Lockbox only supports attribute_changed?
84
- before_validation method_name, if: -> { send("#{attribute}_changed?") }
72
+ activerecord = defined?(ActiveRecord) && self < ActiveRecord::Base
73
+
74
+ # TODO reuse module
75
+ m = Module.new do
76
+ define_method "#{attribute}=" do |value|
77
+ result = super(value)
78
+ send(method_name)
79
+ result
80
+ end
81
+
82
+ unless activerecord
83
+ define_method "reset_#{attribute}!" do
84
+ result = super()
85
+ send(method_name)
86
+ result
87
+ end
88
+ end
85
89
  end
90
+ prepend m
86
91
  end
87
92
 
88
93
  # use include so user can override
@@ -1,3 +1,3 @@
1
1
  module BlindIndex
2
- VERSION = "1.0.2"
2
+ VERSION = "2.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blind_index
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-12-27 00:00:00.000000000 Z
11
+ date: 2020-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -25,19 +25,19 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '5'
27
27
  - !ruby/object:Gem::Dependency
28
- name: argon2
28
+ name: argon2-kdf
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '2'
33
+ version: 0.1.1
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '2'
40
+ version: 0.1.1
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement