blind_index 1.0.2 → 2.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
  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