yattr_encrypted 0.1.1
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/Gemfile +7 -0
- data/MIT-LICENSE +20 -0
- data/README.mdown +149 -0
- data/Rakefile +38 -0
- data/lib/yattr_encrypted/railtie.rb +11 -0
- data/lib/yattr_encrypted/version.rb +3 -0
- data/lib/yattr_encrypted.rb +261 -0
- data/test/test_helper.rb +8 -0
- data/test/yattr_encrypted_test.rb +66 -0
- metadata +74 -0
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Sean Huber - shuber@huberry.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.mdown
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# YattrEncrypted #
|
2
|
+
|
3
|
+
Version: 0.1.0 (but you should check lib/yattr_encrypted/version.rb to be sure)
|
4
|
+
|
5
|
+
## Applicability ##
|
6
|
+
|
7
|
+
This code has been tested
|
8
|
+
|
9
|
+
* using ruby 1.9.2-p290
|
10
|
+
* stand alone
|
11
|
+
* Rails 3.1.4
|
12
|
+
|
13
|
+
## Description ##
|
14
|
+
|
15
|
+
This is based on code stolen from *yattr_encrypted* and *encryptor* - both by
|
16
|
+
github.com/shuber - and both very fine gems.
|
17
|
+
|
18
|
+
So why another dumb attribute encryptor gem?
|
19
|
+
|
20
|
+
shuber's gems don't do what I want and they appear to be dormant.
|
21
|
+
|
22
|
+
The primary difference between **shuber**'s gems and this one is a matter of
|
23
|
+
flexibility and simplicity. **yattr_encrypted** is simple, easy to use and
|
24
|
+
(should be) pretty secure. This comes at the expense of *flexibility*.
|
25
|
+
|
26
|
+
In **yattr_encrypted** you do not have a choice of algorithm, encoding, additional
|
27
|
+
encrypt/decrypt methods, conditional encryption, or underlying data mapper.
|
28
|
+
You also have to use Rails 3.1+.
|
29
|
+
|
30
|
+
In more detail, here is where they differ:
|
31
|
+
|
32
|
+
**yattr_encrypted** does not support:
|
33
|
+
|
34
|
+
* DataMapper
|
35
|
+
* Sequel
|
36
|
+
* ActiveRecord find_by*** methods for encrypted fields
|
37
|
+
* alternate encryption methods
|
38
|
+
* alternate encryption algorithms
|
39
|
+
* String#encrypt, #encrypt!, #decrypt, and #decrypt!
|
40
|
+
* most of the options **yattr_encrypted** supports
|
41
|
+
* conditional encrypting - **yattr_encrypted** supports conditionally encrypting
|
42
|
+
fields based on some logic. I don't have a use case for this, so **yattr_encrypted**
|
43
|
+
does not support it.
|
44
|
+
|
45
|
+
**yattr_encrypted** is also self contained - only relies on the *openssl* (part
|
46
|
+
of the Ruby Standard Library), whereas the *shuber* gem depends on *encryptor*.
|
47
|
+
|
48
|
+
What **yattr_encrypted** *does* support:
|
49
|
+
|
50
|
+
* **yattr_encrypted** ONLY works with ActiveRecord
|
51
|
+
* random initial values for each encrypted attribute. This is done by creating a
|
52
|
+
random *iv* and including it in the encrypted data. See *openssl* documentation
|
53
|
+
for details [OpenSSL::Cipher]
|
54
|
+
* detects when fields are modified by actions other than assignment. This supports
|
55
|
+
encrypting complex types - such as hashes and arrays. This is implemented by adding
|
56
|
+
*save* and *save!* methods to models
|
57
|
+
* Rails 3.1 & Rails 3.2 - doesn't pretend to support anything lower (but it might work)
|
58
|
+
|
59
|
+
|
60
|
+
## Installation ##
|
61
|
+
|
62
|
+
Either
|
63
|
+
|
64
|
+
gem install yattr_encrypted
|
65
|
+
|
66
|
+
Or add to your Gemfile
|
67
|
+
|
68
|
+
gem yattr_encrypted
|
69
|
+
|
70
|
+
## Usage ##
|
71
|
+
|
72
|
+
Name each field you want to encrypt in the `yattr_encrypted` macro.
|
73
|
+
|
74
|
+
For example, assume that you want to encrypt a field named `foo`. Then create
|
75
|
+
a field in your *migration* named `foo_encrypted`.
|
76
|
+
|
77
|
+
class Foo < ActiveRecord::Base
|
78
|
+
yattr_encrypted :foo
|
79
|
+
end
|
80
|
+
|
81
|
+
This will add accessor methods `foo` and `foo=` to your model. You do not use the
|
82
|
+
actual `foo_encrypted` field directly.
|
83
|
+
|
84
|
+
NOTE: `foo` is not useable for search. If you want to search on encrypted fields,
|
85
|
+
use **attr\_encrypted**. **yattr\_encrypted** does not create searchable fields because
|
86
|
+
it automatically generates random initial values so it is not possible to generate
|
87
|
+
matching encrypted values without retrieving the encrypted data.
|
88
|
+
|
89
|
+
### Options ###
|
90
|
+
|
91
|
+
The encrypted field name defaults to `<field>_encrypted`. You can change this on
|
92
|
+
a field by field basis using the `:prefix` and `:suffix` - which define strings
|
93
|
+
which are prefixed and suffixed to the field name to create the encrypted field name.
|
94
|
+
They default to:
|
95
|
+
|
96
|
+
:prefix = ''
|
97
|
+
:suffix = '_encrypted'
|
98
|
+
|
99
|
+
Notice that the underscore ('_') must be included in `:prefix` and `:suffix`, if you
|
100
|
+
want them.
|
101
|
+
|
102
|
+
### Encryption keys
|
103
|
+
|
104
|
+
The default encryption key is the value of `<application>::Application.config.secret_token`
|
105
|
+
which is in `config/initializers/secret_token.rb`.
|
106
|
+
|
107
|
+
If you want to use some other key - on a field by field basis,
|
108
|
+
you can specify the key on a field by field basis using the `:key` option.
|
109
|
+
|
110
|
+
NOTE: all encryption uses `ase-256-cbc` with random initial values. For some reason this
|
111
|
+
triggers a key length check in **openssl** which raises an exception if your key is
|
112
|
+
too short. I don't know what the required key length is, but `secrete_token` is long enough
|
113
|
+
and 'this is a very long secret key' is not.
|
114
|
+
|
115
|
+
If you supply your own key, it can be a String or a Proc which returns a String.
|
116
|
+
|
117
|
+
### Initial Values ###
|
118
|
+
|
119
|
+
As stated everywhere - random initial values are automatically generated for all fields.
|
120
|
+
They are prepended to the actual encrypted data and stripped during decryption. You can't
|
121
|
+
override this, nor can you provide your own initial values.
|
122
|
+
|
123
|
+
### Encryption Method ###
|
124
|
+
|
125
|
+
**yattr\_encrypted** only uses `aes-256-cbc`. If you want variety, use **attr\_encrypted**, which
|
126
|
+
supports the entire gamete supplied by **openssl**
|
127
|
+
|
128
|
+
### Encoding of Encrypted Data and Database Compatibility ###
|
129
|
+
|
130
|
+
All data saved in the database is `base64` encode. All fields are further
|
131
|
+
serialized as JSON objects. This is to avoid dealing with any database idiodicy
|
132
|
+
and to transparently handle complex data types being used in database fields [such as
|
133
|
+
Hashes and Arrays].
|
134
|
+
|
135
|
+
The encoding algorithm for a field is:
|
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
|
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
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rake'
|
2
|
+
# require 'rake/testtask'
|
3
|
+
# require 'rdoc/task'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the yattr_encrypted gem.'
|
9
|
+
task :test do
|
10
|
+
if ENV['TEST']
|
11
|
+
system "ruby #{ENV['TEST']}"
|
12
|
+
else
|
13
|
+
system "ruby test/*_test.rb"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
# Rake::TestTask.new(:test) do |t|
|
17
|
+
# t.libs << 'lib'
|
18
|
+
# # t.pattern = 'test/**/*_test.rb'
|
19
|
+
# t.pattern = ENV['TEST'] ? ENV['TEST'] : 'test/**/*_test.rb'
|
20
|
+
# t.verbose = true
|
21
|
+
# end
|
22
|
+
|
23
|
+
desc 'Build Gem'
|
24
|
+
task 'gem' do
|
25
|
+
system 'gem build yattr_encrypted.gemspec'
|
26
|
+
end
|
27
|
+
|
28
|
+
desc 'Generate documentation for the yattr_encrypted gem.'
|
29
|
+
task 'rdoc' do
|
30
|
+
system 'rdoc README.rdoc lib/'
|
31
|
+
end
|
32
|
+
# RDoc::Task.new do |rdoc|
|
33
|
+
# rdoc.rdoc_dir = 'rdoc'
|
34
|
+
# rdoc.title = 'yattr_encrypted'
|
35
|
+
# rdoc.options << '--line-numbers' << '--inline-source'
|
36
|
+
# rdoc.rdoc_files.include('README*')
|
37
|
+
# rdoc.rdoc_files.include('lib/**/*.rb')
|
38
|
+
# end
|
@@ -0,0 +1,261 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'active_support'
|
3
|
+
require 'base64'
|
4
|
+
require 'yattr_encrypted/railtie' if defined?(Rails)
|
5
|
+
require 'yattr_encrypted/version'
|
6
|
+
|
7
|
+
# how to use
|
8
|
+
#
|
9
|
+
# class Foo < ActiveRecord::Base
|
10
|
+
# yattr_encrypted :foo, :bar
|
11
|
+
# end
|
12
|
+
|
13
|
+
# Adds attr_accessors that encrypt and decrypt an object's attributes
|
14
|
+
module YattrEncrypted
|
15
|
+
|
16
|
+
ALGORITHM = 'aes-256-cbc'
|
17
|
+
|
18
|
+
# Generates attr_accessors that encrypt and decrypt attributes transparently
|
19
|
+
#
|
20
|
+
# Options (any other options you specify are passed to the
|
21
|
+
# encryptor's encrypt and decrypt methods)
|
22
|
+
#
|
23
|
+
# :prefix A prefix used to generate the name of the referenced
|
24
|
+
# encrypted attributes. For example <tt>attr_accessor
|
25
|
+
# :email, :password, :prefix => 'crypted_'</tt> would
|
26
|
+
# generate attributes named 'crypted_email' and
|
27
|
+
# 'crypted_password' to store the encrypted email and
|
28
|
+
# password. Defaults to ''.
|
29
|
+
#
|
30
|
+
# :suffix A suffix used to generate the name of the referenced
|
31
|
+
# encrypted attributes. For example <tt>attr_accessor
|
32
|
+
# :email, :password, :prefix => '', :suffix =>
|
33
|
+
# '_encrypted'</tt> would generate attributes named
|
34
|
+
# 'email_encrypted' and 'password_encrypted' to store the
|
35
|
+
# encrypted email. Defaults to '_encrypted'.
|
36
|
+
#
|
37
|
+
# :key The encryption key. Not generally required.
|
38
|
+
# Defaults to Rails.application.config.secret_token
|
39
|
+
#
|
40
|
+
# Example
|
41
|
+
#
|
42
|
+
# class User
|
43
|
+
# yattr_encrypted :email
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @user = User.new
|
47
|
+
# @user.encrypted_email # nil? is true
|
48
|
+
# @user.email? # false
|
49
|
+
# @user.email = 'test@example.com'
|
50
|
+
# @user.email? # true
|
51
|
+
# @user.encrypted_email # returns the encrypted version of 'test@example.com'
|
52
|
+
#
|
53
|
+
|
54
|
+
def self.included(base)
|
55
|
+
class << base
|
56
|
+
attr_accessor :yate_encrypted_attributes
|
57
|
+
|
58
|
+
def yattr_encrypted(*attributes)
|
59
|
+
# construct options
|
60
|
+
options = {
|
61
|
+
:prefix => '',
|
62
|
+
:suffix => '_encrypted',
|
63
|
+
:key => defined?(::Rails) ? ::Rails.application.config.secret_token : nil,
|
64
|
+
}
|
65
|
+
# merge specific options
|
66
|
+
options.merge!(attributes.pop) if Hash === attributes.last
|
67
|
+
|
68
|
+
# tell self to define instance methods from the database if they have not already been generated
|
69
|
+
define_attribute_methods unless attribute_methods_generated?
|
70
|
+
|
71
|
+
# collect existing instance methods
|
72
|
+
instance_methods_as_symbols = instance_methods.map { |method| method.to_sym }
|
73
|
+
|
74
|
+
# iterate through attributes
|
75
|
+
attributes.map { |x| x.to_sym }.each do |attribute|
|
76
|
+
encrypted_attribute_name = [options[:prefix], attribute, options[:suffix]].join.to_sym
|
77
|
+
|
78
|
+
# barf if reader and write doesn't exist for encrypted attribute
|
79
|
+
raise ArgumentError.new("No Reader method for encrypted version of #{attribute}: #{encrypted_attribute_name}") \
|
80
|
+
unless instance_methods_as_symbols.include?(encrypted_attribute_name)
|
81
|
+
raise ArgumentError.new("No Write method for encrypted version of #{attribute}: #{encrypted_attribute_name}") \
|
82
|
+
unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
|
83
|
+
|
84
|
+
tmp =<<-XXX
|
85
|
+
puts "defining #{attribute}"
|
86
|
+
def #{attribute}
|
87
|
+
unless @#{attribute} && !@#{attribute}.empty?
|
88
|
+
options = yate_encrypted_attributes[:#{attribute}]
|
89
|
+
@#{attribute} = #{encrypted_attribute_name} ? \
|
90
|
+
yate_decrypt(#{encrypted_attribute_name}, options[:key]) : \
|
91
|
+
''
|
92
|
+
self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
|
93
|
+
self.yate_dirty[:#{attribute}] = true
|
94
|
+
end
|
95
|
+
@#{attribute}
|
96
|
+
end
|
97
|
+
XXX
|
98
|
+
class_eval(tmp)
|
99
|
+
|
100
|
+
tmp =<<-XXX
|
101
|
+
puts "self: #{self}"
|
102
|
+
def #{attribute}= value
|
103
|
+
@#{attribute} = value
|
104
|
+
options = yate_encrypted_attributes[:#{attribute}]
|
105
|
+
self.#{encrypted_attribute_name} = yate_encrypt(value, options[:key])
|
106
|
+
self.yate_checksums[:#{attribute}] = yate_field_hash_value(:#{attribute})
|
107
|
+
self.yate_dirty[:#{attribute}] = true
|
108
|
+
end
|
109
|
+
XXX
|
110
|
+
class_eval(tmp)
|
111
|
+
|
112
|
+
define_method("#{attribute}?") do
|
113
|
+
value = send(attribute)
|
114
|
+
value.respond_to?(:empty?) ? !value.empty? : !!value
|
115
|
+
end
|
116
|
+
|
117
|
+
self.yate_encrypted_attributes ||= {}
|
118
|
+
|
119
|
+
self.yate_encrypted_attributes[attribute.to_sym] = \
|
120
|
+
options.merge(:attribute => encrypted_attribute_name)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
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
|
+
# protected methods - nobody needs to use these outside of the model
|
165
|
+
protected
|
166
|
+
|
167
|
+
def yate_checksums
|
168
|
+
@yate_checksums ||= {}
|
169
|
+
end
|
170
|
+
|
171
|
+
def yate_dirty
|
172
|
+
@yate_dirty ||= {}
|
173
|
+
end
|
174
|
+
|
175
|
+
def yate_encrypted_attributes
|
176
|
+
self.class.yate_encrypted_attributes
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
# yate_encrypt(value, key)
|
182
|
+
#
|
183
|
+
def yate_encrypt(value, key)
|
184
|
+
cipher = OpenSSL::Cipher.new(ALGORITHM)
|
185
|
+
cipher.encrypt
|
186
|
+
cipher.key = key
|
187
|
+
iv = cipher.random_iv
|
188
|
+
|
189
|
+
# jsonify data
|
190
|
+
value_marshalled = Marshal.dump value
|
191
|
+
|
192
|
+
# encrypt data
|
193
|
+
result = cipher.update value_marshalled
|
194
|
+
result << cipher.final
|
195
|
+
|
196
|
+
# return encrypted data and iv
|
197
|
+
Base64.encode64(("%04d" % iv.length) + iv + result)
|
198
|
+
end
|
199
|
+
|
200
|
+
# yate_decrypt(encrypted_value, key)
|
201
|
+
def yate_decrypt(marshalled_value, key)
|
202
|
+
# initialize decryptor
|
203
|
+
cipher = OpenSSL::Cipher.new(ALGORITHM)
|
204
|
+
cipher.decrypt
|
205
|
+
cipher.key = key
|
206
|
+
|
207
|
+
# extract encrypted_value
|
208
|
+
encrypted_value = Base64.decode64 marshalled_value
|
209
|
+
|
210
|
+
# extract and set iv
|
211
|
+
iv_end = encrypted_value[0..3].to_i + 3
|
212
|
+
cipher.iv = encrypted_value[4..(iv_end)]
|
213
|
+
encrypted_value = encrypted_value[(iv_end+1)..-1]
|
214
|
+
|
215
|
+
# derypte and return
|
216
|
+
result = cipher.update(encrypted_value)
|
217
|
+
result << cipher.final
|
218
|
+
|
219
|
+
Marshal.load result
|
220
|
+
end
|
221
|
+
|
222
|
+
# support for fields which are not atomic values
|
223
|
+
def yate_field_hash_value(attribute)
|
224
|
+
attribute = attribute.to_s if Symbol === attribute
|
225
|
+
OpenSSL::HMAC.digest('md5', 'ersatz key', Marshal.dump(self.instance_variable_get("@#{attribute}")))
|
226
|
+
end
|
227
|
+
|
228
|
+
def yate_field_changed?(attribute)
|
229
|
+
attribute = attribute.to_sym unless Symbol === attribute
|
230
|
+
yate_field_hash_value(attribute) != self.yate_checksums[attribute] || self.yate_dirty[attribute]
|
231
|
+
end
|
232
|
+
|
233
|
+
def yate_update_encrypted_values
|
234
|
+
yate_encrypted_attributes.each do |attribute, options|
|
235
|
+
if yate_field_changed?(attribute)
|
236
|
+
self.send "#{options[:attribute]}=".to_sym, yate_encrypt(self.send(attribute), options[:key])
|
237
|
+
yate_dirty.delete(attribute)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
# protected
|
243
|
+
#
|
244
|
+
# # Returns yattr_encrypted options evaluated in the current object's scope for the attribute specified
|
245
|
+
# def yate_evaluated_options_for(attribute)
|
246
|
+
# self.class.encrypted_attributes[attribute.to_sym].inject({}) do |hash, (option, value)|
|
247
|
+
# hash.merge!(option => yate_evaluate_option(value))
|
248
|
+
# end
|
249
|
+
# end
|
250
|
+
#
|
251
|
+
# # Evaluates symbol (method reference) or proc (responds to call) options
|
252
|
+
# #
|
253
|
+
# # If the option is not a proc then the original option is returned
|
254
|
+
# def yate_evaluate_option(option)
|
255
|
+
# if option.respond_to?(:call)
|
256
|
+
# option.call(self)
|
257
|
+
# else
|
258
|
+
# option
|
259
|
+
# end
|
260
|
+
# end
|
261
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
# require 'active_record'
|
4
|
+
require 'yattr_encrypted'
|
5
|
+
|
6
|
+
module ActiveRecord
|
7
|
+
class Base
|
8
|
+
include YattrEncrypted
|
9
|
+
|
10
|
+
def self.attribute_methods_generated?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def save
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def save!
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
22
|
+
def update_attribute attribute, value
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def update_attributes attribute_hash, options
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class SomeClass < ActiveRecord::Base
|
33
|
+
attr_accessor :field_encrypted
|
34
|
+
yattr_encrypted :field, :key => 'a honkin big key: honk honk honk honk honk'
|
35
|
+
end
|
36
|
+
|
37
|
+
class TestYattrEncrypted < MiniTest::Unit::TestCase
|
38
|
+
def setup
|
39
|
+
@sc = SomeClass.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_yattr_encrypted_should_create_accessors
|
43
|
+
assert @sc.respond_to?(:field), "a SomeClass instance should respond to :field"
|
44
|
+
assert @sc.respond_to?(:field=), "a SomeClass instance should respond to :field="
|
45
|
+
assert @sc.respond_to?(:field?), "a SomeClass instance should respond to :field?"
|
46
|
+
assert @sc.respond_to?(:yate_encrypted_attributes), "a SomeClass instance should respond to :yate_encrypted_attributes"
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_assigning_field_should_assign_field_encrypted
|
50
|
+
assert_nil @sc.field_encrypted, "field_encrypted should be nil prior to assignment to field"
|
51
|
+
@sc.field = 'a field value'
|
52
|
+
refute_nil @sc.field_encrypted, "field_encrypted should not be nil"
|
53
|
+
assert_equal 'a field value', @sc.field, "@sc.field should match input"
|
54
|
+
options = @sc.send(:yate_encrypted_attributes)[:field]
|
55
|
+
assert_equal 'a field value', @sc.send(:yate_decrypt, @sc.field_encrypted, options[:key]), \
|
56
|
+
"decrypting @sc.field_encrypted should match input"
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_save_should_update_encrypted
|
60
|
+
@sc.field = { key: 'value' }
|
61
|
+
@sc.save
|
62
|
+
options = @sc.send(:yate_encrypted_attributes)[:field]
|
63
|
+
decrypted = @sc.send(:yate_decrypt, @sc.field_encrypted, options[:key])
|
64
|
+
assert_equal( { key: 'value' }, decrypted, "decrypt @sc.field_encrypted should be correct")
|
65
|
+
end
|
66
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yattr_encrypted
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Howard
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-18 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: pry
|
16
|
+
requirement: &2151816860 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2151816860
|
25
|
+
description: Generates yattr_accessors that encrypt and decrypt attributes transparently.
|
26
|
+
Based on attr_encrypted by Sean Huber [https://github.com/shuber]
|
27
|
+
email: mike@clove.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- lib/yattr_encrypted/railtie.rb
|
33
|
+
- lib/yattr_encrypted/version.rb
|
34
|
+
- lib/yattr_encrypted.rb
|
35
|
+
- MIT-LICENSE
|
36
|
+
- Rakefile
|
37
|
+
- README.mdown
|
38
|
+
- Gemfile
|
39
|
+
- test/test_helper.rb
|
40
|
+
- test/yattr_encrypted_test.rb
|
41
|
+
homepage: http://github.mikhoward/yattr_encrypted
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options:
|
45
|
+
- --line-numbers
|
46
|
+
- --inline-source
|
47
|
+
- --main
|
48
|
+
- README.mdown
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
hash: -3522761952983170560
|
60
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
requirements: []
|
67
|
+
rubyforge_project:
|
68
|
+
rubygems_version: 1.8.6
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: Encrypt and decrypt attributes
|
72
|
+
test_files:
|
73
|
+
- test/test_helper.rb
|
74
|
+
- test/yattr_encrypted_test.rb
|