mongoid-kms 0.0.14 → 0.0.15

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
  SHA1:
3
- metadata.gz: 49b32845a5b6f1ca1f9d205d59915df58e7425e1
4
- data.tar.gz: 1c9dbdf7f83534f9b1f94d7e1e03e83d1aef930f
3
+ metadata.gz: 029a4f6efe9f5dafce5aef2c5fab0801229f04cc
4
+ data.tar.gz: 749be3c2641a52cebab3d4f7f2e8d1491a765649
5
5
  SHA512:
6
- metadata.gz: 85e1bf962a3660b1bd10f844c117383fb590c8777f5575e012fa1cb85a9526ef822d0c9d4066862301928fdb696009faf92ed6c7a8b86dd07387f598cf47580f
7
- data.tar.gz: dd9bfa5812eccc83711c2c39538558e39a4f9950af12ee93c73cdba7d370b3acb1e8cc36cd2b8ea4a97783033b21b8ef93ea483355a6d7332a6828c2b8c62205
6
+ metadata.gz: b1386752d377273539f4f06130d820316d7398e10aac8303b7794d84015c95213b2ad5d909ff64a77516a5491f2f9878c04957d78888f93dcc5c36808eac8f27
7
+ data.tar.gz: 759ce38aba35b4d9b49401e2b3cb08f64ef4017c832bf7b6608bd09b9caac659228d44517a9a0e0eac480fbdfca5167ee51095cb3245cfb4078656b99b3405bb
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module Kms
3
- VERSION = "0.0.14"
3
+ VERSION = "0.0.15"
4
4
  end
5
5
  end
data/lib/mongoid/kms.rb CHANGED
@@ -6,73 +6,144 @@ module Mongoid
6
6
  module Kms
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ included do
10
+ @kms_field_map ||= {}
11
+
12
+ unless self.ancestors.include?(ActiveModel::Dirty)
13
+ include ActiveModel::Dirty
14
+ end
15
+ end
16
+
9
17
  @configuration = {}
10
18
  @kms = nil
11
19
 
12
- def self.configure(args)
13
- @configuration = args
14
- end
20
+ # Module methods
21
+ class << self
22
+ def configure(args)
23
+ @configuration = args
24
+ end
15
25
 
16
- def self.configuration
17
- @configuration || {}
18
- end
26
+ def configuration
27
+ @configuration || {}
28
+ end
19
29
 
20
- def self.kms
21
- @kms ||= Aws::KMS::Client.new(region: self.region)
22
- end
30
+ def kms
31
+ @kms ||= Aws::KMS::Client.new(region: self.region)
32
+ end
23
33
 
24
- def self.region
25
- configuration[:region]
26
- end
34
+ def region
35
+ configuration[:region]
36
+ end
37
+
38
+ def key
39
+ configuration[:key]
40
+ end
27
41
 
28
- def self.key
29
- configuration[:key]
42
+ def bson_class
43
+ if defined? Moped::BSON
44
+ Moped::BSON
45
+ elsif defined? BSON
46
+ BSON
47
+ end
48
+ end
30
49
  end
31
50
 
32
- def self.bson_class
33
- if defined? Moped::BSON
34
- Moped::BSON
35
- elsif defined? BSON
36
- BSON
51
+ # Instance methods
52
+ def set_kms_values
53
+ self.class.kms_field_map.each do |field_name, settings|
54
+ if self.send("#{field_name}_changed?") || kms_context_value_changed?(field_name)
55
+ encrypted_field_name = self.class.get_encrypted_field_name(field_name)
56
+
57
+ if instance_variable_get("@#{field_name}").nil? && kms_context_value_changed?(field_name)
58
+ value = self.class.decrypt_field(self, field_name, self.send(encrypted_field_name), self.class.kms_context_was(self, field_name))
59
+ else
60
+ value = send("#{field_name}")
61
+ end
62
+
63
+ if value.nil?
64
+ self.send("#{encrypted_field_name}=", nil)
65
+ else
66
+ self.send("#{encrypted_field_name}=", self.class.encrypt_field(self, field_name, value))
67
+ end
68
+ end
37
69
  end
38
70
  end
39
71
 
72
+ def kms_context_value_changed?(field_name)
73
+ self.class.kms_context_array(self, field_name).find { |f| self.respond_to?(f) && self.send("#{f}_changed?") }
74
+ end
75
+
76
+ # Class methods
40
77
  module ClassMethods
78
+ def kms_field_map
79
+ @kms_field_map
80
+ end
81
+
41
82
  def encrypt_field(object, field_name, value)
42
83
  Mongoid::Kms.kms.encrypt({
43
84
  key_id: Mongoid::Kms.key,
44
85
  plaintext: value,
45
86
  encryption_context: kms_context(object, field_name)
46
87
  })[:ciphertext_blob].force_encoding('UTF-8')
47
- rescue ArgumentError
48
- raise "Error using KMS context. If you use an object's field for context, set your encrypted fields explicitly: myobject.#{field_name} = #{value.inspect}"
49
88
  end
50
89
 
51
- def decrypt_field(object, field_name, data)
90
+ def decrypt_field(object, field_name, data, encryption_context = nil)
91
+ encryption_context ||= kms_context(object, field_name)
92
+
52
93
  Mongoid::Kms.kms.decrypt({
53
94
  ciphertext_blob: data,
54
- encryption_context: kms_context(object, field_name)
95
+ encryption_context: encryption_context
55
96
  })[:plaintext]
56
97
  end
57
98
 
58
99
  def kms_context(object, field_name)
59
- c = @kms_field_map[field_name.to_s][:context]
60
- c = c.call(object) if c.is_a?(Proc)
61
- c
100
+ kms_context_array(object, field_name).inject({}) do |hash, key|
101
+ if object.respond_to?(key)
102
+ hash[key] = object.send(key)
103
+ else
104
+ hash[key] = key
105
+ end
106
+
107
+ hash
108
+ end
109
+ end
110
+
111
+ def kms_context_was(object, field_name)
112
+ kms_context_array(object, field_name).inject({}) do |hash, key|
113
+ if object.respond_to?("#{key}_was") && object.send("#{key}_changed?")
114
+ hash[key] = object.send("#{key}_was")
115
+ elsif object.respond_to?(key)
116
+ hash[key] = object.send(key)
117
+ else
118
+ hash[key] = key
119
+ end
120
+
121
+ hash
122
+ end
123
+ end
124
+
125
+ def kms_context_array(object, field_name)
126
+ @kms_field_map[field_name.to_s][:context] || []
62
127
  end
63
128
 
64
129
  def kms_type(field_name)
65
130
  @kms_field_map[field_name.to_s][:type]
66
131
  end
67
132
 
133
+ def get_encrypted_field_name(field_name)
134
+ "kms_secure_#{field_name}"
135
+ end
136
+
68
137
  def secure_field(field_name, args)
69
- encrypted_field_name = "kms_secure_#{field_name}"
138
+ encrypted_field_name = get_encrypted_field_name(field_name)
70
139
 
71
- @kms_field_map ||= {}
72
140
  @kms_field_map[field_name.to_s] = {context: args.delete(:context), type: args.delete(:type)}
73
141
 
74
142
  field encrypted_field_name, args.merge(type: Mongoid::Kms.bson_class::Binary)
75
143
 
144
+ define_attribute_methods field_name.to_sym
145
+ before_save :set_kms_values
146
+
76
147
  define_method(field_name) do
77
148
  instance_variable_get("@#{field_name}") || begin
78
149
  raw = send("kms_secure_#{field_name}")
@@ -88,13 +159,8 @@ module Mongoid
88
159
  end
89
160
 
90
161
  define_method("#{field_name}=") do |value|
162
+ self.send("#{field_name}_will_change!")
91
163
  instance_variable_set("@#{field_name}", value)
92
-
93
- if value.nil?
94
- self.send("#{encrypted_field_name}=", nil)
95
- else
96
- self.send("#{encrypted_field_name}=", self.class.encrypt_field(self, field_name, value))
97
- end
98
164
  end
99
165
  end
100
166
  end
@@ -3,8 +3,7 @@ require 'spec_helper'
3
3
  describe Mongoid::Kms do
4
4
 
5
5
  it "encrypts the secure fields" do
6
- o = MyClass.new(unsecure: "robin")
7
- o.secure = "batman"
6
+ o = MyClass.new(secure: "batman", unsecure: "robin")
8
7
  o.save!
9
8
 
10
9
  expect(o.secure).to eq("batman")
@@ -12,8 +11,7 @@ describe Mongoid::Kms do
12
11
  end
13
12
 
14
13
  it "descripts the secure fields" do
15
- o = MyClass.new(unsecure: "robin")
16
- o.secure = "batman"
14
+ o = MyClass.new(unsecure: "robin", secure: "batman")
17
15
  o.save!
18
16
 
19
17
  o = MyClass.find(o.id)
@@ -21,9 +19,8 @@ describe Mongoid::Kms do
21
19
  expect(o.unsecure).to eq("robin")
22
20
  end
23
21
 
24
- it "encrypts teh other fields" do
25
- o = OtherClass.new(unsecure: "pengiun")
26
- o.super_secure = "joker"
22
+ it "encrypts the other fields" do
23
+ o = OtherClass.new(unsecure: "pengiun", super_secure: "joker")
27
24
  o.save!
28
25
 
29
26
  o = OtherClass.find(o.id)
@@ -31,4 +28,24 @@ describe Mongoid::Kms do
31
28
  expect(o.unsecure).to eq("pengiun")
32
29
  end
33
30
 
31
+ it "modifies the encryption if the context field changes" do
32
+ o = MyClass.new(unsecure: "robin", secure: "other")
33
+ o.save!
34
+
35
+ o = MyClass.find(o.id)
36
+ o.unsecure = "bla"
37
+ o.save!
38
+
39
+ o = MyClass.find(o.id)
40
+ expect(o.secure).to eq("other")
41
+ end
42
+
43
+ it "handles a class without context" do
44
+ o = MyClass.new(secure: "bla", unsecure: "blatoo")
45
+ o.save!
46
+
47
+ o = MyClass.find(o.id)
48
+ expect(o.secure).to eq("bla")
49
+ end
50
+
34
51
  end
data/spec/spec_helper.rb CHANGED
@@ -9,7 +9,7 @@ class MyClass
9
9
  include Mongoid::Document
10
10
  include Mongoid::Kms
11
11
 
12
- secure_field :secure, type: String, context: lambda { |d| {name: d.unsecure} }
12
+ secure_field :secure, type: String, context: [:unsecure]
13
13
  field :unsecure
14
14
  end
15
15
 
@@ -17,9 +17,16 @@ class OtherClass
17
17
  include Mongoid::Document
18
18
  include Mongoid::Kms
19
19
 
20
- secure_field :super_secure, type: String, context: lambda { |d| {some_name: d.unsecure} }
20
+ secure_field :super_secure, type: String, context: [:unsecure, "deployment"]
21
21
  field :unsecure
22
22
  end
23
23
 
24
+ class ClassWithoutContext
25
+ include Mongoid::Document
26
+ include Mongoid::Kms
27
+
28
+ secure_field :secure, type: String
29
+ field :unsecure
30
+ end
24
31
 
25
32
  Mongoid::Kms.configure({region: "us-east-1", key: ENV['AWS_KMS_KEY_ID']})
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-kms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.14
4
+ version: 0.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Winslett
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-13 00:00:00.000000000 Z
11
+ date: 2014-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid