mongoid-encrypted-fields 1.2.0 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
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