mongoid-encrypted-fields 1.2.2 → 1.3.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 +8 -8
- data/{CHANGLOG.md → CHANGELOG.md} +4 -0
- data/README.md +6 -2
- data/lib/mongoid-encrypted-fields.rb +14 -1
- data/lib/mongoid-encrypted-fields/fields/encrypted_hash.rb +4 -2
- data/lib/mongoid-encrypted-fields/{validations → mongoid3/validations}/uniqueness.rb +1 -3
- data/lib/mongoid-encrypted-fields/mongoid4/validations/uniqueness.rb +25 -0
- data/lib/mongoid-encrypted-fields/version.rb +1 -1
- data/spec/config/mongoid.yml +1 -4
- data/spec/mongoid-encrypted-fields/fields/encrypted_hash_spec.rb +1 -1
- data/spec/mongoid-encrypted-fields/fields/model_spec.rb +6 -3
- data/spec/mongoid-encrypted-fields/mongoid3/validations/uniqueness_spec.rb +153 -0
- data/spec/mongoid-encrypted-fields/mongoid4/validations/uniqueness_spec.rb +152 -0
- data/spec/spec_helper.rb +12 -4
- data/spec/support/models/person.rb +1 -0
- metadata +33 -25
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -23
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -9
- data/Gemfile +0 -13
- data/examples/encrypted_strings_asymmetric_cipher.rb +0 -20
- data/examples/encrypted_strings_symmetric_cipher.rb +0 -20
- data/examples/gibberish_cipher.rb +0 -20
- data/mongoid-encrypted-fields.gemspec +0 -27
- data/spec/mongoid-encrypted-fields/validations/uniqueness_spec.rb +0 -151
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmI2YWVkOGM2ZjlmOTY1NmQxMDJhNzNjMzhkNTdkZmVjNGY2NTA2Yw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NmFkYzIzNjBlNWNjYTNmYTQ5NzI0ZjEwMzliODhkYjQ4NDUzYWI1ZQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjI5MTUxMmY5NzEzMjRiYmZjZjZjM2Y1ZGVhOGI3MTUzYzhhYzlkNmJmOGJj
|
10
|
+
N2EzYzk0ZDM0NzQ4N2NlYzZjZTZiNjJjZmY2NWM4OWNlMThlNDhlNDZkMzlh
|
11
|
+
YWFmZDc5ODhlOTM4ZWQ4OTM2ZDcyZWQzNDlkM2JiOTExZDdkOGQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NTVlOWZkNmNlZWI5OGU0NWE1NmNhMjZhNjFmODRiY2ZmMDBlMjYyZTFlZmI0
|
14
|
+
ZmRhNDY0NjY5YTc2YjZmNDVjNTI4OWIxYmU3MGRmMDQ3OTZjODMyOGNhNmZk
|
15
|
+
MDY4OTllMjJkOTJhMjA2YmYzMjMwZTUzZDlkNDNhYzI3NzU3OTI=
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# Revision history
|
2
2
|
|
3
|
+
## 1.3.0 - Breaking change - PLEASE READ
|
4
|
+
* \#11 Support for Mongoid 4
|
5
|
+
* \#12 EncryptedHash will now stringify the keys before storing to be consistent with Mongoid's behavior
|
6
|
+
|
3
7
|
## 1.2.2
|
4
8
|
* Accepted [pull request](https://github.com/KoanHealth/mongoid-encrypted-fields/pull/10) to support aliased fields with the uniqueness validator (@johnnyshields)
|
5
9
|
|
data/README.md
CHANGED
@@ -12,8 +12,8 @@ Mongoid 3 supports [custom types](http://mongoid.org/en/mongoid/docs/documents.h
|
|
12
12
|
Queries encrypt data before searching the database, so equality matches work automatically.
|
13
13
|
|
14
14
|
## Prerequisites
|
15
|
-
* Ruby 1.9.3
|
16
|
-
* [Mongoid](http://mongoid.org) 3.0
|
15
|
+
* >= Ruby 1.9.3
|
16
|
+
* >= [Mongoid](http://mongoid.org) 3.0
|
17
17
|
* "Bring your own" encryption, see below
|
18
18
|
|
19
19
|
## Install
|
@@ -75,5 +75,9 @@ Queries encrypt data before searching the database, so equality matches work aut
|
|
75
75
|
* Time
|
76
76
|
* The uniqueness validator for encrypted fields is always case-sensitive. Using it with case-sensitive false raises an exception.
|
77
77
|
|
78
|
+
## Related Articles
|
79
|
+
* [Storing Encrypted Data in MongoDB](http://jerryclinesmith.me/blog/2013/03/29/storing-encrypted-data-in-mongodb/)
|
80
|
+
* [Transparently Adding Encrypted Fields to a Rails App using Mongoid](http://blog.thesparktree.com/post/69538763994/transparently-adding-encrypted-fields-to-a-rails-app)
|
81
|
+
|
78
82
|
## Copyright
|
79
83
|
(c) 2012 Koan Health. See LICENSE.txt for further details.
|
@@ -6,7 +6,6 @@ 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'
|
10
9
|
|
11
10
|
module Mongoid
|
12
11
|
module EncryptedFields
|
@@ -14,7 +13,21 @@ module Mongoid
|
|
14
13
|
class << self
|
15
14
|
# Set cipher used for all field encryption/decryption
|
16
15
|
attr_accessor :cipher
|
16
|
+
|
17
|
+
def mongoid_major_version
|
18
|
+
@mongoid_major_version ||= ::Mongoid::VERSION[/([^\.]+)/].to_i
|
19
|
+
end
|
20
|
+
|
17
21
|
end
|
18
22
|
|
19
23
|
end
|
20
24
|
end
|
25
|
+
|
26
|
+
case ::Mongoid::EncryptedFields.mongoid_major_version
|
27
|
+
when 3
|
28
|
+
require 'mongoid-encrypted-fields/mongoid3/validations/uniqueness'
|
29
|
+
when 4
|
30
|
+
require 'mongoid-encrypted-fields/mongoid4/validations/uniqueness'
|
31
|
+
else
|
32
|
+
raise "Unsupported version of Mongoid: #{::Mongoid::VERSION::MAJOR}"
|
33
|
+
end
|
@@ -10,7 +10,9 @@
|
|
10
10
|
# p.address = {street: "123 Main St", city: "Springdale", state: "MD"}
|
11
11
|
#
|
12
12
|
# Get returns the unencrypted hash
|
13
|
-
# puts p.address -> {
|
13
|
+
# puts p.address -> {'street'=>"123 Main St", 'city'=>"Springdale", 'state'=>"MD"}
|
14
|
+
#
|
15
|
+
# Note that symbols used as keys are converted to strings (just like using a Hash with Mongo)
|
14
16
|
#
|
15
17
|
# Use the encrypted property to see the encrypted value
|
16
18
|
# puts p.address.encrypted -> '....'
|
@@ -22,7 +24,7 @@ module Mongoid
|
|
22
24
|
|
23
25
|
# Return value to be encrypted
|
24
26
|
def raw_value
|
25
|
-
to_yaml
|
27
|
+
stringify_keys.to_yaml
|
26
28
|
end
|
27
29
|
|
28
30
|
class << self
|
@@ -1,13 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Mongoid
|
4
|
-
module Validations
|
4
|
+
module Validations
|
5
5
|
|
6
6
|
# Monkey-patch for Mongoid's uniqueness validator to enforce that the :case_sensitive option does not work
|
7
7
|
# for encrypted fields; they must always be case-sensitive.
|
8
|
-
#
|
9
8
|
# 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
9
|
class UniquenessValidator
|
12
10
|
|
13
11
|
def setup_with_validation(klass)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Validatable
|
5
|
+
|
6
|
+
# Monkey-patch for Mongoid's uniqueness validator to enforce that the :case_sensitive option does not work
|
7
|
+
# for encrypted fields; they must always be case-sensitive.
|
8
|
+
# Patch is confirmed to work on Mongoid >= 4.0.0
|
9
|
+
class UniquenessValidator
|
10
|
+
|
11
|
+
def setup_with_validation(klass)
|
12
|
+
setup_without_validation(klass)
|
13
|
+
return if case_sensitive?
|
14
|
+
attributes.each do |attribute|
|
15
|
+
field_type = @klass.fields[@klass.database_field_name(attribute)].options[:type]
|
16
|
+
raise ArgumentError, "Encrypted field :#{attribute} cannot support case insensitive uniqueness" if field_type.method_defined?(:encrypted)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :setup_without_validation, :setup
|
21
|
+
alias_method :setup, :setup_with_validation
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/config/mongoid.yml
CHANGED
@@ -12,7 +12,7 @@ describe 'Single model' do
|
|
12
12
|
let (:birth_date_encrypted) { Mongoid::EncryptedDate.mongoize(birth_date) }
|
13
13
|
let (:address) { {street: '123 Main St', city: 'Anytown', state: 'TX' } }
|
14
14
|
let (:address_encrypted) { Mongoid::EncryptedHash.mongoize(address) }
|
15
|
-
let (:person) { Person.new(name: 'John Doe', ssn: ssn, birth_date: birth_date, address: address) }
|
15
|
+
let (:person) { Person.new(name: 'John Doe', ssn: ssn, birth_date: birth_date, address: address, duck_address: address) }
|
16
16
|
|
17
17
|
it "model stores encrypted ssn" do
|
18
18
|
person.attributes['ssn'].should eq(ssn_encrypted)
|
@@ -35,7 +35,7 @@ describe 'Single model' do
|
|
35
35
|
end
|
36
36
|
|
37
37
|
it "address getter returns raw value" do
|
38
|
-
person.address.should eq(address)
|
38
|
+
person.address.should eq(address.stringify_keys)
|
39
39
|
end
|
40
40
|
|
41
41
|
describe "after save" do
|
@@ -67,9 +67,12 @@ describe 'Single model' do
|
|
67
67
|
end
|
68
68
|
|
69
69
|
it "encrypted address getter returns raw value" do
|
70
|
-
@persisted.address.should eq(address)
|
70
|
+
@persisted.address.should eq(address.stringify_keys)
|
71
71
|
end
|
72
72
|
|
73
|
+
it "encrypted address getter is equivalent to non-encrypted address getter" do
|
74
|
+
@persisted.address.should eq(@persisted.duck_address)
|
75
|
+
end
|
73
76
|
end
|
74
77
|
|
75
78
|
describe "find model by encrypted field" do
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
if Mongoid::EncryptedFields.mongoid_major_version == 3
|
4
|
+
describe Mongoid::Validations::UniquenessValidator do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Mongoid.purge!
|
12
|
+
Mongoid::IdentityMap.clear
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#valid?" do
|
16
|
+
|
17
|
+
let(:person) do
|
18
|
+
Person.new(name: "bill", ssn: "abc456789", credit_card: "12345678", phone_number: "12345678")
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
Person.reset_callbacks(:validate)
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the value is in conflict" do
|
26
|
+
|
27
|
+
context "when the field is not encrypted" do
|
28
|
+
|
29
|
+
context "when the validation is case-sensitive" do
|
30
|
+
|
31
|
+
before do
|
32
|
+
Person.validates_uniqueness_of :name, case_sensitive: true
|
33
|
+
Person.create!(name: "bill")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "correctly detects a uniqueness conflict" do
|
37
|
+
expect(person).to_not be_valid
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when the validation is case-insensitive" do
|
42
|
+
|
43
|
+
before do
|
44
|
+
Person.validates_uniqueness_of :name, case_sensitive: false
|
45
|
+
Person.create!(name: "BiLl")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "behaves as case-insensitive" do
|
49
|
+
expect(person).to_not be_valid
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when the field is encrypted" do
|
55
|
+
|
56
|
+
context "when the validation is case-sensitive" do
|
57
|
+
|
58
|
+
before do
|
59
|
+
Person.validates_uniqueness_of :ssn
|
60
|
+
Person.create!(ssn: "abc456789")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "behaves as case-sensitive" do
|
64
|
+
expect(person).not_to be_valid
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "when the validation is case-insensitive" do
|
69
|
+
|
70
|
+
it "throws an exception" do
|
71
|
+
expect { Person.validates_uniqueness_of :ssn, case_sensitive: false }.to raise_error 'Encrypted field :ssn cannot support case insensitive uniqueness'
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when the value is not conflict" do
|
79
|
+
|
80
|
+
context "when the field is not encrypted" do
|
81
|
+
|
82
|
+
before do
|
83
|
+
Person.validates_uniqueness_of :name
|
84
|
+
Person.create!(name: "ted")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "correctly detects a uniqueness conflict" do
|
88
|
+
expect(person).to be_valid
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when the field is encrypted" do
|
93
|
+
|
94
|
+
before do
|
95
|
+
Person.validates_uniqueness_of :ssn
|
96
|
+
Person.create!(ssn: "223456789")
|
97
|
+
end
|
98
|
+
|
99
|
+
it "correctly detects a uniqueness conflict" do
|
100
|
+
expect(person).to be_valid
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "when the field name is aliased" do
|
106
|
+
|
107
|
+
context "when the aliased name is used" do
|
108
|
+
|
109
|
+
context "when the field is encrypted" do
|
110
|
+
|
111
|
+
it "throws an exception" do
|
112
|
+
expect { Person.validates_uniqueness_of :credit_card, case_sensitive: false }.to raise_error 'Encrypted field :credit_card cannot support case insensitive uniqueness'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "when the field is not encrypted" do
|
117
|
+
|
118
|
+
before do
|
119
|
+
Person.validates_uniqueness_of :phone_number, case_sensitive: false
|
120
|
+
Person.create!(phone_number: "12345678")
|
121
|
+
end
|
122
|
+
|
123
|
+
it "correctly detects a uniqueness conflict" do
|
124
|
+
expect(person).to_not be_valid
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when the underlying name is used" do
|
130
|
+
|
131
|
+
context "when the field is encrypted" do
|
132
|
+
|
133
|
+
it "throws an exception" do
|
134
|
+
expect { Person.validates_uniqueness_of :cc, case_sensitive: false }.to raise_error 'Encrypted field :cc cannot support case insensitive uniqueness'
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "when the field is not encrypted" do
|
139
|
+
|
140
|
+
before do
|
141
|
+
Person.validates :ph, uniqueness: {case_sensitive: false}
|
142
|
+
Person.create!(phone_number: "12345678")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "correctly detects a uniqueness conflict" do
|
146
|
+
expect(person).to_not be_valid
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
if Mongoid::EncryptedFields.mongoid_major_version == 4
|
4
|
+
describe Mongoid::Validatable::UniquenessValidator do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password', 'weaksalt')
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Mongoid.purge!
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#valid?" do
|
15
|
+
|
16
|
+
let(:person) do
|
17
|
+
Person.new(name: "bill", ssn: "abc456789", credit_card: "12345678", phone_number: "12345678")
|
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
|
+
before do
|
58
|
+
Person.validates_uniqueness_of :ssn
|
59
|
+
Person.create!(ssn: "abc456789")
|
60
|
+
end
|
61
|
+
|
62
|
+
it "behaves as case-sensitive" do
|
63
|
+
expect(person).not_to be_valid
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when the validation is case-insensitive" do
|
68
|
+
|
69
|
+
it "throws an exception" do
|
70
|
+
expect { Person.validates_uniqueness_of :ssn, case_sensitive: false }.to raise_error 'Encrypted field :ssn cannot support case insensitive uniqueness'
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when the value is not conflict" do
|
78
|
+
|
79
|
+
context "when the field is not encrypted" do
|
80
|
+
|
81
|
+
before do
|
82
|
+
Person.validates_uniqueness_of :name
|
83
|
+
Person.create!(name: "ted")
|
84
|
+
end
|
85
|
+
|
86
|
+
it "correctly detects a uniqueness conflict" do
|
87
|
+
expect(person).to be_valid
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "when the field is encrypted" do
|
92
|
+
|
93
|
+
before do
|
94
|
+
Person.validates_uniqueness_of :ssn
|
95
|
+
Person.create!(ssn: "223456789")
|
96
|
+
end
|
97
|
+
|
98
|
+
it "correctly detects a uniqueness conflict" do
|
99
|
+
expect(person).to be_valid
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "when the field name is aliased" do
|
105
|
+
|
106
|
+
context "when the aliased name is used" do
|
107
|
+
|
108
|
+
context "when the field is encrypted" do
|
109
|
+
|
110
|
+
it "throws an exception" do
|
111
|
+
expect { Person.validates_uniqueness_of :credit_card, case_sensitive: false }.to raise_error 'Encrypted field :credit_card cannot support case insensitive uniqueness'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when the field is not encrypted" do
|
116
|
+
|
117
|
+
before do
|
118
|
+
Person.validates_uniqueness_of :phone_number, case_sensitive: false
|
119
|
+
Person.create!(phone_number: "12345678")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "correctly detects a uniqueness conflict" do
|
123
|
+
expect(person).to_not be_valid
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when the underlying name is used" do
|
129
|
+
|
130
|
+
context "when the field is encrypted" do
|
131
|
+
|
132
|
+
it "throws an exception" do
|
133
|
+
expect { Person.validates_uniqueness_of :cc, case_sensitive: false }.to raise_error 'Encrypted field :cc cannot support case insensitive uniqueness'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when the field is not encrypted" do
|
138
|
+
|
139
|
+
before do
|
140
|
+
Person.validates :ph, uniqueness: {case_sensitive: false}
|
141
|
+
Person.create!(phone_number: "12345678")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "correctly detects a uniqueness conflict" do
|
145
|
+
expect(person).to_not be_valid
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,19 @@
|
|
1
|
-
require 'rubygems'
|
2
1
|
require 'bundler/setup'
|
2
|
+
require 'simplecov'
|
3
|
+
|
4
|
+
if ENV['TRAVIS']
|
5
|
+
require 'coveralls'
|
6
|
+
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
7
|
+
end
|
8
|
+
|
9
|
+
SimpleCov.start do
|
10
|
+
add_filter '/spec/'
|
11
|
+
add_filter '/examples/'
|
12
|
+
end
|
13
|
+
|
3
14
|
require 'mongoid'
|
4
15
|
require 'rspec'
|
5
16
|
|
6
|
-
require 'coveralls'
|
7
|
-
Coveralls.wear!
|
8
|
-
|
9
17
|
require 'mongoid-encrypted-fields'
|
10
18
|
|
11
19
|
Dir["#{File.dirname(__FILE__)}/../examples/**/*.rb"].each {|f| require f}
|
@@ -5,6 +5,7 @@ class Person
|
|
5
5
|
field :ssn, type: Mongoid::EncryptedString
|
6
6
|
field :birth_date, type: Mongoid::EncryptedDate
|
7
7
|
field :address, type: Mongoid::EncryptedHash
|
8
|
+
field :duck_address, type: Hash
|
8
9
|
field :ph, as: :phone_number, type: String
|
9
10
|
field :cc, as: :credit_card, type: Mongoid::EncryptedString
|
10
11
|
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-encrypted-fields
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koan Health
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mongoid
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ! '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - ! '>='
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: A library for storing encrypted data in Mongo
|
98
112
|
email:
|
99
113
|
- development@koanhealth.com
|
@@ -101,20 +115,6 @@ executables: []
|
|
101
115
|
extensions: []
|
102
116
|
extra_rdoc_files: []
|
103
117
|
files:
|
104
|
-
- .coveralls.yml
|
105
|
-
- .gitignore
|
106
|
-
- .ruby-gemset
|
107
|
-
- .ruby-version
|
108
|
-
- .travis.yml
|
109
|
-
- CHANGLOG.md
|
110
|
-
- Gemfile
|
111
|
-
- LICENSE.txt
|
112
|
-
- README.md
|
113
|
-
- Rakefile
|
114
|
-
- examples/encrypted_strings_asymmetric_cipher.rb
|
115
|
-
- examples/encrypted_strings_symmetric_cipher.rb
|
116
|
-
- examples/gibberish_cipher.rb
|
117
|
-
- lib/mongoid-encrypted-fields.rb
|
118
118
|
- lib/mongoid-encrypted-fields/fields/encrypted_date.rb
|
119
119
|
- lib/mongoid-encrypted-fields/fields/encrypted_date_time.rb
|
120
120
|
- lib/mongoid-encrypted-fields/fields/encrypted_field.rb
|
@@ -122,9 +122,14 @@ files:
|
|
122
122
|
- lib/mongoid-encrypted-fields/fields/encrypted_string.rb
|
123
123
|
- lib/mongoid-encrypted-fields/fields/encrypted_time.rb
|
124
124
|
- lib/mongoid-encrypted-fields/logging.rb
|
125
|
-
- lib/mongoid-encrypted-fields/validations/uniqueness.rb
|
125
|
+
- lib/mongoid-encrypted-fields/mongoid3/validations/uniqueness.rb
|
126
|
+
- lib/mongoid-encrypted-fields/mongoid4/validations/uniqueness.rb
|
126
127
|
- lib/mongoid-encrypted-fields/version.rb
|
127
|
-
- mongoid-encrypted-fields.
|
128
|
+
- lib/mongoid-encrypted-fields.rb
|
129
|
+
- CHANGELOG.md
|
130
|
+
- LICENSE.txt
|
131
|
+
- README.md
|
132
|
+
- Rakefile
|
128
133
|
- spec/config/mongoid.yml
|
129
134
|
- spec/mongoid-encrypted-fields/fields/encrypted_date_spec.rb
|
130
135
|
- spec/mongoid-encrypted-fields/fields/encrypted_datetime_spec.rb
|
@@ -133,11 +138,13 @@ files:
|
|
133
138
|
- spec/mongoid-encrypted-fields/fields/encrypted_string_spec.rb
|
134
139
|
- spec/mongoid-encrypted-fields/fields/encrypted_time_spec.rb
|
135
140
|
- spec/mongoid-encrypted-fields/fields/model_spec.rb
|
136
|
-
- spec/mongoid-encrypted-fields/validations/uniqueness_spec.rb
|
141
|
+
- spec/mongoid-encrypted-fields/mongoid3/validations/uniqueness_spec.rb
|
142
|
+
- spec/mongoid-encrypted-fields/mongoid4/validations/uniqueness_spec.rb
|
137
143
|
- spec/spec_helper.rb
|
138
144
|
- spec/support/models/person.rb
|
139
145
|
homepage: https://github.com/KoanHealth/mongoid-encrypted-fields
|
140
|
-
licenses:
|
146
|
+
licenses:
|
147
|
+
- MIT
|
141
148
|
metadata: {}
|
142
149
|
post_install_message:
|
143
150
|
rdoc_options: []
|
@@ -147,12 +154,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
147
154
|
requirements:
|
148
155
|
- - ! '>='
|
149
156
|
- !ruby/object:Gem::Version
|
150
|
-
version: '
|
157
|
+
version: '1.9'
|
151
158
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
152
159
|
requirements:
|
153
160
|
- - ! '>='
|
154
161
|
- !ruby/object:Gem::Version
|
155
|
-
version:
|
162
|
+
version: 1.3.6
|
156
163
|
requirements: []
|
157
164
|
rubyforge_project:
|
158
165
|
rubygems_version: 2.0.6
|
@@ -168,6 +175,7 @@ test_files:
|
|
168
175
|
- spec/mongoid-encrypted-fields/fields/encrypted_string_spec.rb
|
169
176
|
- spec/mongoid-encrypted-fields/fields/encrypted_time_spec.rb
|
170
177
|
- spec/mongoid-encrypted-fields/fields/model_spec.rb
|
171
|
-
- spec/mongoid-encrypted-fields/validations/uniqueness_spec.rb
|
178
|
+
- spec/mongoid-encrypted-fields/mongoid3/validations/uniqueness_spec.rb
|
179
|
+
- spec/mongoid-encrypted-fields/mongoid4/validations/uniqueness_spec.rb
|
172
180
|
- spec/spec_helper.rb
|
173
181
|
- spec/support/models/person.rb
|
data/.coveralls.yml
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
service_name: travis-ci
|
data/.gitignore
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
|
-
.bundle
|
4
|
-
.config
|
5
|
-
.sw?
|
6
|
-
.yardoc
|
7
|
-
.*.sw?
|
8
|
-
Gemfile.lock
|
9
|
-
InstalledFiles
|
10
|
-
_yardoc
|
11
|
-
coverage
|
12
|
-
doc/
|
13
|
-
lib/bundler/man
|
14
|
-
pkg
|
15
|
-
rdoc
|
16
|
-
spec/reports
|
17
|
-
test/tmp
|
18
|
-
test/version_tmp
|
19
|
-
tmp
|
20
|
-
|
21
|
-
#Ignore IDEA directory
|
22
|
-
/.idea
|
23
|
-
.tags*
|
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
mongoid-encrypted-fields
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.9.3
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'encrypted_strings'
|
2
|
-
|
3
|
-
class EncryptedStringsAsymmetricCipher
|
4
|
-
|
5
|
-
attr_reader :algorithm, :password, :public_key_file, :private_key_file
|
6
|
-
|
7
|
-
def initialize(options = {})
|
8
|
-
@options = options
|
9
|
-
@options.each { |key, value| instance_variable_set "@#{key}", value }
|
10
|
-
end
|
11
|
-
|
12
|
-
def encrypt(data)
|
13
|
-
data.encrypt(:asymmetric, @options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def decrypt(data)
|
17
|
-
data.decrypt(:asymmetric, @options)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'encrypted_strings'
|
2
|
-
|
3
|
-
class EncryptedStringsSymmetricCipher
|
4
|
-
|
5
|
-
attr_reader :algorithm, :password
|
6
|
-
|
7
|
-
def initialize(options = {})
|
8
|
-
@options = options
|
9
|
-
@options.each { |key, value| instance_variable_set "@#{key}", value }
|
10
|
-
end
|
11
|
-
|
12
|
-
def encrypt(data)
|
13
|
-
data.encrypt(:symmetric, @options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def decrypt(data)
|
17
|
-
data.decrypt(:symmetric, @options)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'gibberish/aes'
|
2
|
-
|
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
|
-
|
7
|
-
def initialize(password, salt)
|
8
|
-
@cipher = Gibberish::AES.new(password)
|
9
|
-
@salt = salt
|
10
|
-
end
|
11
|
-
|
12
|
-
def encrypt(data)
|
13
|
-
@cipher.encrypt(data, salt: @salt)
|
14
|
-
end
|
15
|
-
|
16
|
-
def decrypt(data)
|
17
|
-
@cipher.decrypt(data)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'mongoid-encrypted-fields/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |gem|
|
7
|
-
gem.name = 'mongoid-encrypted-fields'
|
8
|
-
gem.version = Mongoid::EncryptedFields::VERSION
|
9
|
-
gem.authors = ['Koan Health']
|
10
|
-
gem.email = ['development@koanhealth.com']
|
11
|
-
gem.description = 'A library for storing encrypted data in Mongo'
|
12
|
-
gem.summary = 'Custom types for storing encrypted data'
|
13
|
-
gem.homepage = 'https://github.com/KoanHealth/mongoid-encrypted-fields'
|
14
|
-
|
15
|
-
gem.files = `git ls-files`.split($/)
|
16
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
-
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
-
gem.require_paths = ['lib']
|
19
|
-
|
20
|
-
gem.add_dependency 'mongoid', '~> 3'
|
21
|
-
|
22
|
-
gem.add_development_dependency 'rake'
|
23
|
-
gem.add_development_dependency 'rspec'
|
24
|
-
gem.add_development_dependency 'gibberish', '~> 1.2.2'
|
25
|
-
gem.add_development_dependency 'encrypted_strings', '~> 0.3'
|
26
|
-
gem.add_development_dependency 'coveralls'
|
27
|
-
end
|
@@ -1,151 +0,0 @@
|
|
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", credit_card: "12345678", phone_number: "12345678")
|
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
|
-
before do
|
58
|
-
Person.validates_uniqueness_of :ssn
|
59
|
-
Person.create!(ssn: "abc456789")
|
60
|
-
end
|
61
|
-
|
62
|
-
it "behaves as case-sensitive" do
|
63
|
-
expect(person).not_to be_valid
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "when the validation is case-insensitive" do
|
68
|
-
|
69
|
-
it "throws an exception" do
|
70
|
-
expect { Person.validates_uniqueness_of :ssn, case_sensitive: false }.to raise_error 'Encrypted field :ssn cannot support case insensitive uniqueness'
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
context "when the value is not conflict" do
|
78
|
-
|
79
|
-
context "when the field is not encrypted" do
|
80
|
-
|
81
|
-
before do
|
82
|
-
Person.validates_uniqueness_of :name
|
83
|
-
Person.create!(name: "ted")
|
84
|
-
end
|
85
|
-
|
86
|
-
it "correctly detects a uniqueness conflict" do
|
87
|
-
expect(person).to be_valid
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
context "when the field is encrypted" do
|
92
|
-
|
93
|
-
before do
|
94
|
-
Person.validates_uniqueness_of :ssn
|
95
|
-
Person.create!(ssn: "223456789")
|
96
|
-
end
|
97
|
-
|
98
|
-
it "correctly detects a uniqueness conflict" do
|
99
|
-
expect(person).to be_valid
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
context "when the field name is aliased" do
|
105
|
-
|
106
|
-
context "when the aliased name is used" do
|
107
|
-
|
108
|
-
context "when the field is encrypted" do
|
109
|
-
|
110
|
-
it "throws an exception" do
|
111
|
-
expect { Person.validates_uniqueness_of :credit_card, case_sensitive: false }.to raise_error 'Encrypted field :credit_card cannot support case insensitive uniqueness'
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
context "when the field is not encrypted" do
|
116
|
-
|
117
|
-
before do
|
118
|
-
Person.validates_uniqueness_of :phone_number, case_sensitive: false
|
119
|
-
Person.create!(phone_number: "12345678")
|
120
|
-
end
|
121
|
-
|
122
|
-
it "correctly detects a uniqueness conflict" do
|
123
|
-
expect(person).to_not be_valid
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context "when the underlying name is used" do
|
129
|
-
|
130
|
-
context "when the field is encrypted" do
|
131
|
-
|
132
|
-
it "throws an exception" do
|
133
|
-
expect { Person.validates_uniqueness_of :cc, case_sensitive: false }.to raise_error 'Encrypted field :cc cannot support case insensitive uniqueness'
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
context "when the field is not encrypted" do
|
138
|
-
|
139
|
-
before do
|
140
|
-
Person.validates :ph, uniqueness: { case_sensitive: false }
|
141
|
-
Person.create!(phone_number: "12345678")
|
142
|
-
end
|
143
|
-
|
144
|
-
it "correctly detects a uniqueness conflict" do
|
145
|
-
expect(person).to_not be_valid
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|