mongoid-encrypted-fields 1.2.0 → 1.2.1

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.
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ mongoid-encrypted-fields
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3
data/CHANGLOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Revision history
2
2
 
3
+ ## 1.2.1 - Uniqueness validator fails if developer attempts to use case-insensitive option for an encrypted field
4
+
3
5
  ## 1.2 - Add EncryptedHash
4
6
 
5
7
  * Accepted [pull request](https://github.com/KoanHealth/mongoid-encrypted-fields/pull/4) from ashirazi to add support for encrypted hashes
data/README.md CHANGED
@@ -20,7 +20,7 @@ Queries encrypt data before searching the database, so equality matches work aut
20
20
 
21
21
  GibberishCipher can be found in examples - uses the [Gibberish](https://github.com/mdp/gibberish) gem:
22
22
  ```Ruby
23
- Mongoid::EncryptedFields.cipher = GibberishCipher.new(ENV['MY_PASSWORD'])
23
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new(ENV['MY_PASSWORD'], ENV['MY_SALT'])
24
24
  ```
25
25
 
26
26
  * Use encrypted types for fields in your models:
@@ -48,6 +48,18 @@ Queries encrypt data before searching the database, so equality matches work aut
48
48
  ```Ruby
49
49
  Person.where(ssn: '123456789').count() # ssn is encrypted before querying the database
50
50
  ```
51
+ * The Mongoid uniqueness validator is patched to detect of encrypted fields:
52
+ ```Ruby
53
+ class Person
54
+ ...
55
+ field :ssn, type: Mongoid::EncryptedString
56
+ validates_uniqueness_of :ssn, case_sensitive: true # Works as expected
57
+ validates_uniqueness_of :ssn, case_sensitive: false # Raises exception - encrypted field cannot support a case insensitive match
58
+ end
59
+
60
+ Person.create!(name: 'Bill', ssn: '123456789')
61
+ Person.create!(name: 'Ted', ssn: '123456789') #=> fails with uniqueness error
62
+ ```
51
63
 
52
64
  ## Known Limitations
53
65
  * Single cipher for all encrypted fields
@@ -57,6 +69,7 @@ Queries encrypt data before searching the database, so equality matches work aut
57
69
  * Hash
58
70
  * String
59
71
  * Time
72
+ * The uniqueness validator for encrypted fields is always case-sensitive. Using it with case-sensitive false raises an exception.
60
73
 
61
74
  ## Copyright
62
75
  (c) 2012 Koan Health. See LICENSE.txt for further details.
@@ -1,19 +1,20 @@
1
1
  require 'gibberish/aes'
2
2
 
3
- # Gibberish uses a unique salt for every encryption, but we need the same text
4
- # to return the same ciphertext so Searching for encrypted field will work
5
- class GibberishCipher < Gibberish::AES
3
+ # Gibberish uses a unique salt for every encryption, but we need the same text to return the same ciphertext
4
+ # so Searching for encrypted field will work
5
+ class GibberishCipher
6
6
 
7
- SALT = "OU812FTW" # TODO: If you use this example class, make a unique 8-byte salt for your project
7
+ def initialize(password, salt)
8
+ @cipher = Gibberish::AES.new(password)
9
+ @salt = salt
10
+ end
8
11
 
9
- def initialize(password, options = {})
10
- super password, options[:size] || 256
11
- @salt = options[:salt] || SALT
12
+ def encrypt(data)
13
+ @cipher.encrypt(data, salt: @salt)
12
14
  end
13
15
 
14
- def generate_salt(supplied_salt)
15
- return @salt if @salt
16
- super
16
+ def decrypt(data)
17
+ @cipher.decrypt(data)
17
18
  end
18
19
 
19
20
  end
@@ -6,6 +6,7 @@ require 'mongoid-encrypted-fields/fields/encrypted_string'
6
6
  require 'mongoid-encrypted-fields/fields/encrypted_date'
7
7
  require 'mongoid-encrypted-fields/fields/encrypted_date_time'
8
8
  require 'mongoid-encrypted-fields/fields/encrypted_time'
9
+ require 'mongoid-encrypted-fields/validations/uniqueness'
9
10
 
10
11
  module Mongoid
11
12
  module EncryptedFields
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ module Mongoid
4
+ module Validations # renamed to module Validatable in Mongoid 4.0
5
+
6
+ # Monkey-patch for Mongoid's uniqueness validator to encrypt
7
+ # uniqueness test values before querying the database
8
+ #
9
+ # Patch is confirmed to work on Mongoid >= 3.0.0
10
+ # Should work in Mongoid >= 4.0.0 by renaming module Validations to Validatable
11
+ #
12
+ # A known limitation is that the :case_sensitive option does not work
13
+ # for encrypted fields; they must always be case-sensitive.
14
+ class UniquenessValidator
15
+
16
+ def setup_with_validation(klass)
17
+ setup_without_validation(klass)
18
+ return if case_sensitive?
19
+ attributes.each do |attribute|
20
+ field_type = @klass.fields[attribute.to_s].options[:type]
21
+ raise ArgumentError, "Encrypted field :#{attribute} cannot support case insensitive uniqueness" if field_type.method_defined?(:encrypted)
22
+ end
23
+ end
24
+
25
+ alias_method :setup_without_validation, :setup
26
+ alias_method :setup, :setup_with_validation
27
+
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module EncryptedFields
3
- VERSION = '1.2.0'
3
+ VERSION = '1.2.1'
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ module Mongoid
4
4
  describe EncryptedDate do
5
5
 
6
6
  before(:all) do
7
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
7
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
8
8
  end
9
9
 
10
10
  subject { Mongoid::EncryptedDate }
@@ -4,7 +4,7 @@ module Mongoid
4
4
  describe EncryptedDateTime do
5
5
 
6
6
  before(:all) do
7
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
7
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
8
8
  end
9
9
 
10
10
  subject { Mongoid::EncryptedDateTime }
@@ -4,7 +4,7 @@ module Mongoid
4
4
  describe EncryptedField do
5
5
 
6
6
  before(:all) do
7
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
7
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
8
8
  end
9
9
 
10
10
  it "should encrypt and decrypt a string" do
@@ -4,7 +4,7 @@ module Mongoid
4
4
  describe EncryptedHash do
5
5
 
6
6
  before(:all) do
7
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
7
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
8
8
  end
9
9
 
10
10
  subject { Mongoid::EncryptedHash }
@@ -4,7 +4,7 @@ module Mongoid
4
4
  describe EncryptedString do
5
5
 
6
6
  before(:all) do
7
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
7
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
8
8
  end
9
9
 
10
10
  let(:raw) { "abc123" }
@@ -4,7 +4,7 @@ module Mongoid
4
4
  describe EncryptedTime do
5
5
 
6
6
  before(:all) do
7
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
7
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
8
8
  end
9
9
 
10
10
  subject { Mongoid::EncryptedTime }
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe 'Single model' do
4
4
 
5
5
  before(:all) do
6
- Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
6
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
7
7
  end
8
8
 
9
9
  let (:ssn) { '123456789' }
@@ -0,0 +1,99 @@
1
+ require "spec_helper"
2
+
3
+ describe Mongoid::Validations::UniquenessValidator do
4
+
5
+ before(:all) do
6
+ Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
7
+ end
8
+
9
+ before(:each) do
10
+ Mongoid.purge!
11
+ Mongoid::IdentityMap.clear
12
+ end
13
+
14
+ describe "#valid?" do
15
+
16
+ let(:person) do
17
+ Person.new(name: "bill", ssn: "abc456789")
18
+ end
19
+
20
+ after do
21
+ Person.reset_callbacks(:validate)
22
+ end
23
+
24
+ context "when the value is in conflict" do
25
+
26
+ context "when the field is not encrypted" do
27
+
28
+ context "when the validation is case-sensitive" do
29
+
30
+ before do
31
+ Person.validates_uniqueness_of :name, case_sensitive: true
32
+ Person.create!(name: "bill")
33
+ end
34
+
35
+ it "correctly detects a uniqueness conflict" do
36
+ expect(person).to_not be_valid
37
+ end
38
+ end
39
+
40
+ context "when the validation is case-insensitive" do
41
+
42
+ before do
43
+ Person.validates_uniqueness_of :name, case_sensitive: false
44
+ Person.create!(name: "BiLl")
45
+ end
46
+
47
+ it "behaves as case-insensitive" do
48
+ expect(person).to_not be_valid
49
+ end
50
+ end
51
+ end
52
+
53
+ context "when the field is encrypted" do
54
+
55
+ context "when the validation is case-sensitive" do
56
+
57
+ it "behaves as case-sensitive" do
58
+ expect(person).to be_valid
59
+ end
60
+ end
61
+
62
+ context "when the validation is case-insensitive" do
63
+
64
+ it "throws an exception" do
65
+ expect { Person.validates_uniqueness_of :ssn, case_sensitive: false }.to raise_error 'Encrypted field :ssn cannot support case insensitive uniqueness'
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+
72
+ context "when the value is not conflict" do
73
+
74
+ context "when the field is not encrypted" do
75
+
76
+ before do
77
+ Person.validates_uniqueness_of :name
78
+ Person.create!(name: "ted")
79
+ end
80
+
81
+ it "correctly detects a uniqueness conflict" do
82
+ expect(person).to be_valid
83
+ end
84
+ end
85
+
86
+ context "when the field is encrypted" do
87
+
88
+ before do
89
+ Person.validates_uniqueness_of :ssn
90
+ Person.create!(ssn: "223456789")
91
+ end
92
+
93
+ it "correctly detects a uniqueness conflict" do
94
+ expect(person).to be_valid
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-encrypted-fields
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-20 00:00:00.000000000 Z
12
+ date: 2013-04-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: mongoid
@@ -100,6 +100,8 @@ extra_rdoc_files: []
100
100
  files:
101
101
  - .gitignore
102
102
  - .rspec
103
+ - .ruby-gemset
104
+ - .ruby-version
103
105
  - .rvmrc
104
106
  - CHANGLOG.md
105
107
  - Gemfile
@@ -117,6 +119,7 @@ files:
117
119
  - lib/mongoid-encrypted-fields/fields/encrypted_string.rb
118
120
  - lib/mongoid-encrypted-fields/fields/encrypted_time.rb
119
121
  - lib/mongoid-encrypted-fields/logging.rb
122
+ - lib/mongoid-encrypted-fields/validations/uniqueness.rb
120
123
  - lib/mongoid-encrypted-fields/version.rb
121
124
  - mongoid-encrypted-fields.gemspec
122
125
  - spec/config/mongoid.yml
@@ -127,6 +130,7 @@ files:
127
130
  - spec/mongoid-encrypted-fields/fields/encrypted_string_spec.rb
128
131
  - spec/mongoid-encrypted-fields/fields/encrypted_time_spec.rb
129
132
  - spec/mongoid-encrypted-fields/fields/model_spec.rb
133
+ - spec/mongoid-encrypted-fields/validations/uniqueness_spec.rb
130
134
  - spec/spec_helper.rb
131
135
  - spec/support/models/person.rb
132
136
  homepage: https://github.com/KoanHealth/mongoid-encrypted-fields
@@ -143,7 +147,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
147
  version: '0'
144
148
  segments:
145
149
  - 0
146
- hash: -3259061833501052041
150
+ hash: 3728144320335115622
147
151
  required_rubygems_version: !ruby/object:Gem::Requirement
148
152
  none: false
149
153
  requirements:
@@ -152,7 +156,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
156
  version: '0'
153
157
  segments:
154
158
  - 0
155
- hash: -3259061833501052041
159
+ hash: 3728144320335115622
156
160
  requirements: []
157
161
  rubyforge_project:
158
162
  rubygems_version: 1.8.25
@@ -168,5 +172,6 @@ test_files:
168
172
  - spec/mongoid-encrypted-fields/fields/encrypted_string_spec.rb
169
173
  - spec/mongoid-encrypted-fields/fields/encrypted_time_spec.rb
170
174
  - spec/mongoid-encrypted-fields/fields/model_spec.rb
175
+ - spec/mongoid-encrypted-fields/validations/uniqueness_spec.rb
171
176
  - spec/spec_helper.rb
172
177
  - spec/support/models/person.rb