yattr_encrypted 0.1.2 → 0.1.3

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/README.mdown CHANGED
@@ -1,6 +1,6 @@
1
1
  # YattrEncrypted #
2
2
 
3
- Version: 0.1.2 (but you should check lib/yattr_encrypted/version.rb to be sure)
3
+ Version: 0.1.3 (but you should check lib/yattr_encrypted/version.rb to be sure)
4
4
 
5
5
  ## Applicability ##
6
6
 
@@ -53,7 +53,7 @@ random *iv* and including it in the encrypted data. See *openssl* documentation
53
53
  for details [OpenSSL::Cipher]
54
54
  * detects when fields are modified by actions other than assignment. This supports
55
55
  encrypting complex types - such as hashes and arrays. This is implemented by adding
56
- *save* and *save!* methods to models
56
+ a `before_save` calleback to the private method *yattr_update_encrypted_values*
57
57
  * Rails 3.1 & Rails 3.2 - doesn't pretend to support anything lower (but it might work)
58
58
 
59
59
 
@@ -132,18 +132,40 @@ serialized as JSON objects. This is to avoid dealing with any database idiodicy
132
132
  and to transparently handle complex data types being used in database fields [such as
133
133
  Hashes and Arrays].
134
134
 
135
- The encoding algorithm for a field is:
135
+ The encoding code for a field is:
136
136
 
137
- field_jsonified = ActiveSupprt::JSON.encode field
138
- iv = lambda { (0..16).map { |x| rand(256).chr }.join() }.call
139
- encrypted_data = YattrEncrypted.encrypt field_jsonified, iv
140
- field_encrypted = ActiveSupport::Base64.encode64 ("%04d" % iv.length) + iv + encrypted_data
137
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
138
+ cipher.encrypt
139
+ cipher.key = key
140
+ iv = cipher.random_iv # ask OpenSSL for a new, random initial value
141
141
 
142
- The decoding algorithm is:
143
-
144
- field_decoded = ActiveSupport::Base64::decode64 field_encrypted
145
- len = field_decoded[0..3].to_i + 1
146
- iv = field_decoded[4..(len)]
147
- encrypted_data = field_decoded[(len+1)..-1]
148
- field_jsonified = YattrEncrypted.decrypt encrypted_data, iv
149
- field = ActiveSupprt::JSON.decode field_jsonified
142
+ # jsonify data
143
+ value_marshalled = Marshal.dump value
144
+
145
+ # encrypt data
146
+ result = cipher.update value_marshalled
147
+ result << cipher.final
148
+
149
+ # return encrypted data and iv
150
+ Base64.encode64(("%04d" % iv.length) + iv + result)
151
+
152
+ The decoding code is:
153
+
154
+ # initialize decryptor
155
+ cipher = OpenSSL::Cipher.new(ALGORITHM)
156
+ cipher.decrypt
157
+ cipher.key = key
158
+
159
+ # extract encrypted_value
160
+ encrypted_value = Base64.decode64 marshalled_value
161
+
162
+ # extract and set iv
163
+ iv_end = encrypted_value[0..3].to_i + 3
164
+ cipher.iv = encrypted_value[4..(iv_end)]
165
+ encrypted_value = encrypted_value[(iv_end+1)..-1]
166
+
167
+ # derypte and return
168
+ result = cipher.update(encrypted_value)
169
+ result << cipher.final
170
+
171
+ Marshal.load result
@@ -1,3 +1,3 @@
1
1
  module YattrEncrypted
2
- VERSION = '0.1.2'
2
+ VERSION = '0.1.3'
3
3
  end
@@ -67,11 +67,14 @@ module YattrEncrypted
67
67
 
68
68
  # tell self to define instance methods from the database if they have not already been generated
69
69
  define_attribute_methods unless attribute_methods_generated?
70
+
71
+ # schedule yattr_update_encrypted_values before save
72
+ self.send :before_save, :yate_update_encrypted_values unless self.yate_encrypted_attributes
70
73
 
71
74
  # collect existing instance methods
72
75
  instance_methods_as_symbols = instance_methods.map { |method| method.to_sym }
73
76
 
74
- # iterate through attributes
77
+ # iterate through attributes and create accessors, verify encryped accessors exist
75
78
  attributes.map { |x| x.to_sym }.each do |attribute|
76
79
  encrypted_attribute_name = [options[:prefix], attribute, options[:suffix]].join.to_sym
77
80
 
@@ -82,14 +85,13 @@ module YattrEncrypted
82
85
  unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
83
86
 
84
87
  tmp =<<-XXX
85
- puts "defining #{attribute}"
86
88
  def #{attribute}
87
89
  unless @#{attribute} && !@#{attribute}.empty?
88
90
  options = yate_encrypted_attributes[:#{attribute}]
89
91
  @#{attribute} = #{encrypted_attribute_name} ? \
90
92
  yate_decrypt(#{encrypted_attribute_name}, options[:key]) : \
91
93
  ''
92
- self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
94
+ self.yate_checksums[:#{attribute}] = yate_attribute_hash_value(:#{attribute})
93
95
  self.yate_dirty[:#{attribute}] = true
94
96
  end
95
97
  @#{attribute}
@@ -98,12 +100,11 @@ module YattrEncrypted
98
100
  class_eval(tmp)
99
101
 
100
102
  tmp =<<-XXX
101
- puts "self: #{self}"
102
103
  def #{attribute}= value
103
104
  @#{attribute} = value
104
105
  options = yate_encrypted_attributes[:#{attribute}]
105
106
  self.#{encrypted_attribute_name} = yate_encrypt(value, options[:key])
106
- self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
107
+ self.yate_checksums[:#{attribute}] = yate_attribute_hash_value(:#{attribute})
107
108
  self.yate_dirty[:#{attribute}] = true
108
109
  end
109
110
  XXX
@@ -123,44 +124,6 @@ module YattrEncrypted
123
124
  end
124
125
  end
125
126
 
126
- # Checks if an attribute is configured with <tt>yattr_encrypted</tt>
127
- def yattr_encrypted?(attribute)
128
- self.class.yate_encrypted_attributes.has_key?(attribute.to_sym)
129
- end
130
-
131
- def save *args
132
- yate_update_encrypted_values
133
- super
134
- end
135
-
136
- def save! *args
137
- yate_update_encrypted_values
138
- super
139
- end
140
-
141
- def update_attribute attribute, value
142
- if (options = yate_encrypted_attributes[attribute])
143
- self.send "#{attribute}=".to_sym, value
144
- update_attribute options[:attribute], self.send(options[:attribute]) if yate_field_changed? attribute
145
- else
146
- super
147
- end
148
- end
149
-
150
- def update_attributes params, options = {}
151
- tmp = {}
152
- params.keys.each do |attribute|
153
- if (options = yate_encrypted_attributes[attribute])
154
- self.send "#{attribute}=", params[attribute]
155
- tmp[options[:attribute]] = self.send options[:attribute]
156
- else
157
- tmp[attribute] = params[attribute]
158
- end
159
- end
160
- params = tmp
161
- super
162
- end
163
-
164
127
  # protected methods - nobody needs to use these outside of the model
165
128
  protected
166
129
 
@@ -184,7 +147,7 @@ module YattrEncrypted
184
147
  cipher = OpenSSL::Cipher.new(ALGORITHM)
185
148
  cipher.encrypt
186
149
  cipher.key = key
187
- iv = cipher.random_iv
150
+ iv = cipher.random_iv # ask OpenSSL for a new, random initial value
188
151
 
189
152
  # jsonify data
190
153
  value_marshalled = Marshal.dump value
@@ -220,19 +183,20 @@ module YattrEncrypted
220
183
  end
221
184
 
222
185
  # support for fields which are not atomic values
223
- def yate_field_hash_value(attribute)
186
+ def yate_attribute_hash_value(attribute)
224
187
  attribute = attribute.to_s if Symbol === attribute
225
188
  OpenSSL::HMAC.digest('md5', 'ersatz key', Marshal.dump(self.instance_variable_get("@#{attribute}")))
226
189
  end
227
190
 
228
- def yate_field_changed?(attribute)
191
+ def yate_attribute_changed?(attribute)
229
192
  attribute = attribute.to_sym unless Symbol === attribute
230
- yate_field_hash_value(attribute) != self.yate_checksums[attribute] || self.yate_dirty[attribute]
193
+ yate_attribute_hash_value(attribute) != self.yate_checksums[attribute] || self.yate_dirty[attribute]
231
194
  end
232
195
 
233
196
  def yate_update_encrypted_values
197
+ return unless yate_encrypted_attributes
234
198
  yate_encrypted_attributes.each do |attribute, options|
235
- if yate_field_changed?(attribute)
199
+ if yate_attribute_changed?(attribute)
236
200
  self.send "#{options[:attribute]}=".to_sym, yate_encrypt(self.send(attribute), options[:key])
237
201
  yate_dirty.delete(attribute)
238
202
  end
@@ -10,6 +10,11 @@ module ActiveRecord
10
10
  def self.attribute_methods_generated?
11
11
  true
12
12
  end
13
+
14
+ def self.before_save *methods
15
+ @before_save_hooks ||= []
16
+ @before_save_hooks += methods
17
+ end
13
18
 
14
19
  def save
15
20
  true
@@ -39,6 +44,11 @@ class TestYattrEncrypted < MiniTest::Unit::TestCase
39
44
  @sc = SomeClass.new
40
45
  end
41
46
 
47
+ def test_before_save_hook
48
+ assert SomeClass.instance_variable_get(:@before_save_hooks).include?(:yate_update_encrypted_values), \
49
+ "before_save_hooks should include :yate_encrypted_attributes"
50
+ end
51
+
42
52
  def test_yattr_encrypted_should_create_accessors
43
53
  assert @sc.respond_to?(:field), "a SomeClass instance should respond to :field"
44
54
  assert @sc.respond_to?(:field=), "a SomeClass instance should respond to :field="
@@ -46,7 +56,7 @@ class TestYattrEncrypted < MiniTest::Unit::TestCase
46
56
  assert @sc.respond_to?(:yate_encrypted_attributes), "a SomeClass instance should respond to :yate_encrypted_attributes"
47
57
  end
48
58
 
49
- def test_assigning_field_should_assign_field_encrypted
59
+ def test_assigning_attribute_should_assign_attribute_encrypted
50
60
  assert_nil @sc.field_encrypted, "field_encrypted should be nil prior to assignment to field"
51
61
  @sc.field = 'a field value'
52
62
  refute_nil @sc.field_encrypted, "field_encrypted should not be nil"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yattr_encrypted
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-18 00:00:00.000000000Z
12
+ date: 2012-03-19 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry
16
- requirement: &2151817020 !ruby/object:Gem::Requirement
16
+ requirement: &2151767260 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *2151817020
24
+ version_requirements: *2151767260
25
25
  description: Generates yattr_accessors that encrypt and decrypt attributes transparently.
26
26
  Based on attr_encrypted by Sean Huber [https://github.com/shuber]
27
27
  email: mike@clove.com
@@ -56,7 +56,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
56
  version: '0'
57
57
  segments:
58
58
  - 0
59
- hash: -1650387470150173914
59
+ hash: -3636252519896888946
60
60
  required_rubygems_version: !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements: