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 +37 -15
- data/lib/yattr_encrypted/version.rb +1 -1
- data/lib/yattr_encrypted.rb +12 -48
- data/test/yattr_encrypted_test.rb +11 -1
- metadata +5 -5
data/README.mdown
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# YattrEncrypted #
|
2
2
|
|
3
|
-
Version: 0.1.
|
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
|
-
|
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
|
135
|
+
The encoding code for a field is:
|
136
136
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
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
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
data/lib/yattr_encrypted.rb
CHANGED
@@ -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}] =
|
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}] =
|
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
|
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
|
191
|
+
def yate_attribute_changed?(attribute)
|
229
192
|
attribute = attribute.to_sym unless Symbol === attribute
|
230
|
-
|
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
|
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
|
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.
|
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-
|
12
|
+
date: 2012-03-19 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pry
|
16
|
-
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: *
|
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: -
|
59
|
+
hash: -3636252519896888946
|
60
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|