shuber-attr_encrypted 1.0.2 → 1.0.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/CHANGELOG +1 -0
- data/README.markdown +6 -1
- data/lib/attr_encrypted.rb +8 -8
- data/lib/huberry/attr_encrypted/active_record.rb +50 -0
- data/lib/huberry/attr_encrypted/class.rb +169 -0
- data/lib/huberry/attr_encrypted/data_mapper.rb +23 -0
- data/lib/huberry/attr_encrypted/object.rb +47 -0
- metadata +5 -5
- data/lib/huberry/active_record.rb +0 -48
- data/lib/huberry/class.rb +0 -167
- data/lib/huberry/data_mapper.rb +0 -21
- data/lib/huberry/object.rb +0 -45
data/CHANGELOG
CHANGED
data/README.markdown
CHANGED
@@ -261,13 +261,18 @@ Then you'll have these two class methods available for each attribute: `User.enc
|
|
261
261
|
be useful when you're using ActiveRecord (see below).
|
262
262
|
|
263
263
|
|
264
|
+
### DataMapper ###
|
265
|
+
|
266
|
+
For your convenience, the `:encode` and `:marshal` options are set to true by default since you'll be storing everything in a database.
|
267
|
+
|
268
|
+
|
264
269
|
### ActiveRecord ###
|
265
270
|
|
266
271
|
If you're using this gem with ActiveRecord, you get a few extra features:
|
267
272
|
|
268
273
|
#### Default options ####
|
269
274
|
|
270
|
-
|
275
|
+
Just like the default options for DataMapper, the `:encode` and `:marshal` options are set to true by default since you'll be storing everything in a database.
|
271
276
|
|
272
277
|
|
273
278
|
#### Dynamic find\_by\_ and scoped\_by\_ methods ####
|
data/lib/attr_encrypted.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
|
-
require 'huberry/class'
|
2
|
-
Class.send :include, Huberry::Class
|
1
|
+
require 'huberry/attr_encrypted/class'
|
2
|
+
Class.send :include, Huberry::AttrEncrypted::Class
|
3
3
|
|
4
|
-
require 'huberry/object'
|
5
|
-
Object.send :include, Huberry::Object
|
4
|
+
require 'huberry/attr_encrypted/object'
|
5
|
+
Object.send :include, Huberry::AttrEncrypted::Object
|
6
6
|
|
7
7
|
if defined?(ActiveRecord)
|
8
|
-
require 'huberry/active_record'
|
9
|
-
ActiveRecord::Base.extend Huberry::ActiveRecord
|
8
|
+
require 'huberry/attr_encrypted/active_record'
|
9
|
+
ActiveRecord::Base.extend Huberry::AttrEncrypted::ActiveRecord
|
10
10
|
end
|
11
11
|
|
12
12
|
if defined?(DataMapper)
|
13
|
-
require 'huberry/data_mapper'
|
14
|
-
DataMapper::Resource.send :include, Huberry::DataMapper
|
13
|
+
require 'huberry/attr_encrypted/data_mapper'
|
14
|
+
DataMapper::Resource.send :include, Huberry::AttrEncrypted::DataMapper
|
15
15
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Huberry
|
2
|
+
module AttrEncrypted
|
3
|
+
module ActiveRecord
|
4
|
+
def self.extended(base)
|
5
|
+
base.eigenclass_eval { alias_method_chain :method_missing, :attr_encrypted }
|
6
|
+
end
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
11
|
+
# unless they've already been specified
|
12
|
+
def attr_encrypted(*attrs)
|
13
|
+
define_attribute_methods
|
14
|
+
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
15
|
+
super *(attrs << options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
|
19
|
+
# encrypted attributes
|
20
|
+
#
|
21
|
+
# NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
|
22
|
+
#
|
23
|
+
# This is useful for encrypting fields like email addresses. Your user's email addresses
|
24
|
+
# are encrypted in the database, but you can still look up a user by email for logging in
|
25
|
+
#
|
26
|
+
# Example
|
27
|
+
#
|
28
|
+
# class User < ActiveRecord::Base
|
29
|
+
# attr_encrypted :email, :key => 'secret key'
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# User.find_by_email_and_password('test@example.com', 'testing')
|
33
|
+
# # results in a call to
|
34
|
+
# User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
|
35
|
+
def method_missing_with_attr_encrypted(method, *args, &block)
|
36
|
+
if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
|
37
|
+
attribute_names = match.captures.last.split('_and_')
|
38
|
+
attribute_names.each_with_index do |attribute, index|
|
39
|
+
if attr_encrypted?(attribute)
|
40
|
+
args[index] = send("encrypt_#{attribute}", args[index])
|
41
|
+
attribute_names[index] = encrypted_attributes[attribute]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
|
45
|
+
end
|
46
|
+
method_missing_without_attr_encrypted(method, *args, &block)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module Huberry
|
2
|
+
module AttrEncrypted
|
3
|
+
module Class
|
4
|
+
protected
|
5
|
+
# Generates attr_accessors that encrypt and decrypt attributes transparently
|
6
|
+
#
|
7
|
+
# Options (any other options you specify are passed to the encryptor's encrypt and decrypt methods)
|
8
|
+
#
|
9
|
+
# :attribute => The name of the referenced encrypted attribute. For example
|
10
|
+
# <tt>attr_accessor :email, :attribute => :ee</tt> would generate an
|
11
|
+
# attribute named 'ee' to store the encrypted email. This is useful when defining
|
12
|
+
# one attribute to encrypt at a time or when the :prefix and :suffix options
|
13
|
+
# aren't enough. Defaults to nil.
|
14
|
+
#
|
15
|
+
# :prefix => A prefix used to generate the name of the referenced encrypted attributes.
|
16
|
+
# For example <tt>attr_accessor :email, :password, :prefix => 'crypted_'</tt> would
|
17
|
+
# generate attributes named 'crypted_email' and 'crypted_password' to store the
|
18
|
+
# encrypted email and password. Defaults to 'encrypted_'.
|
19
|
+
#
|
20
|
+
# :suffix => A suffix used to generate the name of the referenced encrypted attributes.
|
21
|
+
# For example <tt>attr_accessor :email, :password, :prefix => '', :suffix => '_encrypted'</tt>
|
22
|
+
# would generate attributes named 'email_encrypted' and 'password_encrypted' to store the
|
23
|
+
# encrypted email. Defaults to ''.
|
24
|
+
#
|
25
|
+
# :key => The encryption key. This option may not be required if you're using a custom encryptor. If you pass
|
26
|
+
# a symbol representing an instance method then the :key option will be replaced with the result of the
|
27
|
+
# method before being passed to the encryptor. Objects that respond to :call are evaluated as well (including procs).
|
28
|
+
# Any other key types will be passed directly to the encryptor.
|
29
|
+
#
|
30
|
+
# :encode => If set to true, attributes will be encoded as well as encrypted. This is useful if you're
|
31
|
+
# planning on storing the encrypted attributes in a database. The default encoding is 'm*' (base64),
|
32
|
+
# however this can be overwritten by setting the :encode option to some other encoding string instead of
|
33
|
+
# just 'true'. See http://www.ruby-doc.org/core/classes/Array.html#M002245 for more encoding directives.
|
34
|
+
# Defaults to false unless you're using it with ActiveRecord or DataMapper.
|
35
|
+
#
|
36
|
+
# :marshal => If set to true, attributes will be marshaled as well as encrypted. This is useful if you're planning
|
37
|
+
# on encrypting something other than a string. Defaults to false unless you're using it with ActiveRecord
|
38
|
+
# or DataMapper.
|
39
|
+
#
|
40
|
+
# :encryptor => The object to use for encrypting. Defaults to Huberry::Encryptor.
|
41
|
+
#
|
42
|
+
# :encrypt_method => The encrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :encrypt.
|
43
|
+
#
|
44
|
+
# :decrypt_method => The decrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :decrypt.
|
45
|
+
#
|
46
|
+
# :if => Attributes are only encrypted if this option evaluates to true. If you pass a symbol representing an instance
|
47
|
+
# method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well.
|
48
|
+
# Defaults to true.
|
49
|
+
#
|
50
|
+
# :unless => Attributes are only encrypted if this option evaluates to false. If you pass a symbol representing an instance
|
51
|
+
# method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well.
|
52
|
+
# Defaults to false.
|
53
|
+
#
|
54
|
+
# You can specify your own default options
|
55
|
+
#
|
56
|
+
# class User
|
57
|
+
# # now all attributes will be encoded and marshaled by default
|
58
|
+
# attr_encrypted_options.merge!(:encode => true, :marshal => true, :some_other_option => true)
|
59
|
+
# attr_encrypted :configuration
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
#
|
63
|
+
# Example
|
64
|
+
#
|
65
|
+
# class User
|
66
|
+
# attr_encrypted :email, :credit_card, :key => 'some secret key'
|
67
|
+
# attr_encrypted :configuration, :key => 'some other secret key', :marshal => true
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# @user = User.new
|
71
|
+
# @user.encrypted_email # returns nil
|
72
|
+
# @user.email = 'test@example.com'
|
73
|
+
# @user.encrypted_email # returns the encrypted version of 'test@example.com'
|
74
|
+
#
|
75
|
+
# @user.configuration = { :time_zone => 'UTC' }
|
76
|
+
# @user.encrypted_configuration # returns the encrypted version of configuration
|
77
|
+
#
|
78
|
+
# See README for more examples
|
79
|
+
def attr_encrypted(*attrs)
|
80
|
+
options = {
|
81
|
+
:prefix => 'encrypted_',
|
82
|
+
:suffix => '',
|
83
|
+
:encryptor => Huberry::Encryptor,
|
84
|
+
:encrypt_method => :encrypt,
|
85
|
+
:decrypt_method => :decrypt,
|
86
|
+
:encode => false,
|
87
|
+
:marshal => false,
|
88
|
+
:if => true,
|
89
|
+
:unless => false
|
90
|
+
}.merge(attr_encrypted_options).merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
91
|
+
options[:encode] = 'm*' if options[:encode] == true
|
92
|
+
|
93
|
+
attrs.each do |attribute|
|
94
|
+
encrypted_attribute_name = options[:attribute].nil? ? options[:prefix].to_s + attribute.to_s + options[:suffix].to_s : options[:attribute].to_s
|
95
|
+
|
96
|
+
encrypted_attributes[attribute.to_s] = encrypted_attribute_name
|
97
|
+
|
98
|
+
attr_accessor encrypted_attribute_name.to_sym unless instance_methods.include?(encrypted_attribute_name)
|
99
|
+
|
100
|
+
define_class_method "encrypt_#{attribute}" do |value|
|
101
|
+
if options[:if] && !options[:unless]
|
102
|
+
if value.nil?
|
103
|
+
encrypted_value = nil
|
104
|
+
else
|
105
|
+
value = Marshal.dump(value) if options[:marshal]
|
106
|
+
encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value)
|
107
|
+
encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
|
108
|
+
end
|
109
|
+
encrypted_value
|
110
|
+
else
|
111
|
+
value
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
define_class_method "decrypt_#{attribute}" do |encrypted_value|
|
116
|
+
if options[:if] && !options[:unless]
|
117
|
+
if encrypted_value.nil?
|
118
|
+
decrypted_value = nil
|
119
|
+
else
|
120
|
+
encrypted_value = encrypted_value.unpack(options[:encode]).to_s if options[:encode]
|
121
|
+
decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value))
|
122
|
+
decrypted_value = Marshal.load(decrypted_value) if options[:marshal]
|
123
|
+
end
|
124
|
+
decrypted_value
|
125
|
+
else
|
126
|
+
encrypted_value
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
define_method "#{attribute}" do
|
131
|
+
value = instance_variable_get("@#{attribute}")
|
132
|
+
encrypted_value = read_attribute(encrypted_attribute_name)
|
133
|
+
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
134
|
+
hash[option] = options[option]
|
135
|
+
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
136
|
+
hash
|
137
|
+
end
|
138
|
+
value = instance_variable_set("@#{attribute}", self.class.send("decrypt_#{attribute}".to_sym, encrypted_value)) if value.nil? && !encrypted_value.nil?
|
139
|
+
options.merge!(original_options)
|
140
|
+
value
|
141
|
+
end
|
142
|
+
|
143
|
+
define_method "#{attribute}=" do |value|
|
144
|
+
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
145
|
+
hash[option] = options[option]
|
146
|
+
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
147
|
+
hash
|
148
|
+
end
|
149
|
+
write_attribute(encrypted_attribute_name, self.class.send("encrypt_#{attribute}".to_sym, value))
|
150
|
+
options.merge!(original_options)
|
151
|
+
instance_variable_set("@#{attribute}", value)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Evaluates an option specified as a symbol representing an instance method or a proc
|
157
|
+
# If the option is not a symbol or proc then the original option is returned
|
158
|
+
def evaluate_attr_encrypted_option(option, object)
|
159
|
+
if option.is_a?(Symbol) && object.respond_to?(option)
|
160
|
+
object.send(option)
|
161
|
+
elsif option.respond_to?(:call)
|
162
|
+
option.call(object)
|
163
|
+
else
|
164
|
+
option
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Huberry
|
2
|
+
module AttrEncrypted
|
3
|
+
module DataMapper
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
extend ClassMethods
|
7
|
+
alias_method :read_attribute, :attribute_get
|
8
|
+
alias_method :write_attribute, :attribute_set
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
protected
|
14
|
+
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
15
|
+
# unless they've already been specified
|
16
|
+
def attr_encrypted(*attrs)
|
17
|
+
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
18
|
+
super *(attrs << options)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Huberry
|
2
|
+
module AttrEncrypted
|
3
|
+
module Object
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
extend ClassMethods
|
7
|
+
eattr_accessor :attr_encrypted_options, :encrypted_attributes
|
8
|
+
attr_encrypted_options = {}
|
9
|
+
encrypted_attributes = {}
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Wraps instance_variable_get
|
14
|
+
def read_attribute(attribute)
|
15
|
+
instance_variable_get("@#{attribute}")
|
16
|
+
end
|
17
|
+
|
18
|
+
# Wraps instance_variable_set
|
19
|
+
def write_attribute(attribute, value)
|
20
|
+
instance_variable_set("@#{attribute}", value)
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
# Checks if an attribute has been configured to be encrypted
|
25
|
+
#
|
26
|
+
# Example
|
27
|
+
#
|
28
|
+
# class User
|
29
|
+
# attr_accessor :name
|
30
|
+
# attr_encrypted :email
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# User.attr_encrypted?(:name) # false
|
34
|
+
# User.attr_encrypted?(:email) # true
|
35
|
+
def attr_encrypted?(attribute)
|
36
|
+
encrypted_attributes.keys.include?(attribute.to_s)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Copies existing encrypted attributes and options to the derived class
|
40
|
+
def inherited(base)
|
41
|
+
base.attr_encrypted_options = self.attr_encrypted_options.nil? ? {} : self.attr_encrypted_options.dup
|
42
|
+
base.encrypted_attributes = self.encrypted_attributes.nil? ? {} : self.encrypted_attributes.dup
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shuber-attr_encrypted
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Huber
|
@@ -41,10 +41,10 @@ extra_rdoc_files: []
|
|
41
41
|
files:
|
42
42
|
- CHANGELOG
|
43
43
|
- lib/attr_encrypted.rb
|
44
|
-
- lib/huberry/active_record.rb
|
45
|
-
- lib/huberry/class.rb
|
46
|
-
- lib/huberry/data_mapper.rb
|
47
|
-
- lib/huberry/object.rb
|
44
|
+
- lib/huberry/attr_encrypted/active_record.rb
|
45
|
+
- lib/huberry/attr_encrypted/class.rb
|
46
|
+
- lib/huberry/attr_encrypted/data_mapper.rb
|
47
|
+
- lib/huberry/attr_encrypted/object.rb
|
48
48
|
- MIT-LICENSE
|
49
49
|
- Rakefile
|
50
50
|
- README.markdown
|
@@ -1,48 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module ActiveRecord
|
3
|
-
def self.extended(base)
|
4
|
-
base.eigenclass_eval { alias_method_chain :method_missing, :attr_encrypted }
|
5
|
-
end
|
6
|
-
|
7
|
-
protected
|
8
|
-
|
9
|
-
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
10
|
-
# unless they've already been specified
|
11
|
-
def attr_encrypted(*attrs)
|
12
|
-
define_attribute_methods
|
13
|
-
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
14
|
-
super *(attrs << options)
|
15
|
-
end
|
16
|
-
|
17
|
-
# Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
|
18
|
-
# encrypted attributes
|
19
|
-
#
|
20
|
-
# NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
|
21
|
-
#
|
22
|
-
# This is useful for encrypting fields like email addresses. Your user's email addresses
|
23
|
-
# are encrypted in the database, but you can still look up a user by email for logging in
|
24
|
-
#
|
25
|
-
# Example
|
26
|
-
#
|
27
|
-
# class User < ActiveRecord::Base
|
28
|
-
# attr_encrypted :email, :key => 'secret key'
|
29
|
-
# end
|
30
|
-
#
|
31
|
-
# User.find_by_email_and_password('test@example.com', 'testing')
|
32
|
-
# # results in a call to
|
33
|
-
# User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
|
34
|
-
def method_missing_with_attr_encrypted(method, *args, &block)
|
35
|
-
if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
|
36
|
-
attribute_names = match.captures.last.split('_and_')
|
37
|
-
attribute_names.each_with_index do |attribute, index|
|
38
|
-
if attr_encrypted?(attribute)
|
39
|
-
args[index] = send("encrypt_#{attribute}", args[index])
|
40
|
-
attribute_names[index] = encrypted_attributes[attribute]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
|
44
|
-
end
|
45
|
-
method_missing_without_attr_encrypted(method, *args, &block)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/huberry/class.rb
DELETED
@@ -1,167 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Class
|
3
|
-
protected
|
4
|
-
# Generates attr_accessors that encrypt and decrypt attributes transparently
|
5
|
-
#
|
6
|
-
# Options (any other options you specify are passed to the encryptor's encrypt and decrypt methods)
|
7
|
-
#
|
8
|
-
# :attribute => The name of the referenced encrypted attribute. For example
|
9
|
-
# <tt>attr_accessor :email, :attribute => :ee</tt> would generate an
|
10
|
-
# attribute named 'ee' to store the encrypted email. This is useful when defining
|
11
|
-
# one attribute to encrypt at a time or when the :prefix and :suffix options
|
12
|
-
# aren't enough. Defaults to nil.
|
13
|
-
#
|
14
|
-
# :prefix => A prefix used to generate the name of the referenced encrypted attributes.
|
15
|
-
# For example <tt>attr_accessor :email, :password, :prefix => 'crypted_'</tt> would
|
16
|
-
# generate attributes named 'crypted_email' and 'crypted_password' to store the
|
17
|
-
# encrypted email and password. Defaults to 'encrypted_'.
|
18
|
-
#
|
19
|
-
# :suffix => A suffix used to generate the name of the referenced encrypted attributes.
|
20
|
-
# For example <tt>attr_accessor :email, :password, :prefix => '', :suffix => '_encrypted'</tt>
|
21
|
-
# would generate attributes named 'email_encrypted' and 'password_encrypted' to store the
|
22
|
-
# encrypted email. Defaults to ''.
|
23
|
-
#
|
24
|
-
# :key => The encryption key. This option may not be required if you're using a custom encryptor. If you pass
|
25
|
-
# a symbol representing an instance method then the :key option will be replaced with the result of the
|
26
|
-
# method before being passed to the encryptor. Objects that respond to :call are evaluated as well (including procs).
|
27
|
-
# Any other key types will be passed directly to the encryptor.
|
28
|
-
#
|
29
|
-
# :encode => If set to true, attributes will be encoded as well as encrypted. This is useful if you're
|
30
|
-
# planning on storing the encrypted attributes in a database. The default encoding is 'm*' (base64),
|
31
|
-
# however this can be overwritten by setting the :encode option to some other encoding string instead of
|
32
|
-
# just 'true'. See http://www.ruby-doc.org/core/classes/Array.html#M002245 for more encoding directives.
|
33
|
-
# Defaults to false unless you're using it with ActiveRecord or DataMapper.
|
34
|
-
#
|
35
|
-
# :marshal => If set to true, attributes will be marshaled as well as encrypted. This is useful if you're planning
|
36
|
-
# on encrypting something other than a string. Defaults to false unless you're using it with ActiveRecord
|
37
|
-
# or DataMapper.
|
38
|
-
#
|
39
|
-
# :encryptor => The object to use for encrypting. Defaults to Huberry::Encryptor.
|
40
|
-
#
|
41
|
-
# :encrypt_method => The encrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :encrypt.
|
42
|
-
#
|
43
|
-
# :decrypt_method => The decrypt method name to call on the <tt>:encryptor</tt> object. Defaults to :decrypt.
|
44
|
-
#
|
45
|
-
# :if => Attributes are only encrypted if this option evaluates to true. If you pass a symbol representing an instance
|
46
|
-
# method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well.
|
47
|
-
# Defaults to true.
|
48
|
-
#
|
49
|
-
# :unless => Attributes are only encrypted if this option evaluates to false. If you pass a symbol representing an instance
|
50
|
-
# method then the result of the method will be evaluated. Any objects that respond to :call are evaluated as well.
|
51
|
-
# Defaults to false.
|
52
|
-
#
|
53
|
-
# You can specify your own default options
|
54
|
-
#
|
55
|
-
# class User
|
56
|
-
# # now all attributes will be encoded and marshaled by default
|
57
|
-
# attr_encrypted_options.merge!(:encode => true, :marshal => true, :some_other_option => true)
|
58
|
-
# attr_encrypted :configuration
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# Example
|
63
|
-
#
|
64
|
-
# class User
|
65
|
-
# attr_encrypted :email, :credit_card, :key => 'some secret key'
|
66
|
-
# attr_encrypted :configuration, :key => 'some other secret key', :marshal => true
|
67
|
-
# end
|
68
|
-
#
|
69
|
-
# @user = User.new
|
70
|
-
# @user.encrypted_email # returns nil
|
71
|
-
# @user.email = 'test@example.com'
|
72
|
-
# @user.encrypted_email # returns the encrypted version of 'test@example.com'
|
73
|
-
#
|
74
|
-
# @user.configuration = { :time_zone => 'UTC' }
|
75
|
-
# @user.encrypted_configuration # returns the encrypted version of configuration
|
76
|
-
#
|
77
|
-
# See README for more examples
|
78
|
-
def attr_encrypted(*attrs)
|
79
|
-
options = {
|
80
|
-
:prefix => 'encrypted_',
|
81
|
-
:suffix => '',
|
82
|
-
:encryptor => Huberry::Encryptor,
|
83
|
-
:encrypt_method => :encrypt,
|
84
|
-
:decrypt_method => :decrypt,
|
85
|
-
:encode => false,
|
86
|
-
:marshal => false,
|
87
|
-
:if => true,
|
88
|
-
:unless => false
|
89
|
-
}.merge(attr_encrypted_options).merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
90
|
-
options[:encode] = 'm*' if options[:encode] == true
|
91
|
-
|
92
|
-
attrs.each do |attribute|
|
93
|
-
encrypted_attribute_name = options[:attribute].nil? ? options[:prefix].to_s + attribute.to_s + options[:suffix].to_s : options[:attribute].to_s
|
94
|
-
|
95
|
-
encrypted_attributes[attribute.to_s] = encrypted_attribute_name
|
96
|
-
|
97
|
-
attr_accessor encrypted_attribute_name.to_sym unless instance_methods.include?(encrypted_attribute_name)
|
98
|
-
|
99
|
-
define_class_method "encrypt_#{attribute}" do |value|
|
100
|
-
if options[:if] && !options[:unless]
|
101
|
-
if value.nil?
|
102
|
-
encrypted_value = nil
|
103
|
-
else
|
104
|
-
value = Marshal.dump(value) if options[:marshal]
|
105
|
-
encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value)
|
106
|
-
encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
|
107
|
-
end
|
108
|
-
encrypted_value
|
109
|
-
else
|
110
|
-
value
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
define_class_method "decrypt_#{attribute}" do |encrypted_value|
|
115
|
-
if options[:if] && !options[:unless]
|
116
|
-
if encrypted_value.nil?
|
117
|
-
decrypted_value = nil
|
118
|
-
else
|
119
|
-
encrypted_value = encrypted_value.unpack(options[:encode]).to_s if options[:encode]
|
120
|
-
decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value))
|
121
|
-
decrypted_value = Marshal.load(decrypted_value) if options[:marshal]
|
122
|
-
end
|
123
|
-
decrypted_value
|
124
|
-
else
|
125
|
-
encrypted_value
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
define_method "#{attribute}" do
|
130
|
-
value = instance_variable_get("@#{attribute}")
|
131
|
-
encrypted_value = read_attribute(encrypted_attribute_name)
|
132
|
-
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
133
|
-
hash[option] = options[option]
|
134
|
-
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
135
|
-
hash
|
136
|
-
end
|
137
|
-
value = instance_variable_set("@#{attribute}", self.class.send("decrypt_#{attribute}".to_sym, encrypted_value)) if value.nil? && !encrypted_value.nil?
|
138
|
-
options.merge!(original_options)
|
139
|
-
value
|
140
|
-
end
|
141
|
-
|
142
|
-
define_method "#{attribute}=" do |value|
|
143
|
-
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
144
|
-
hash[option] = options[option]
|
145
|
-
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
146
|
-
hash
|
147
|
-
end
|
148
|
-
write_attribute(encrypted_attribute_name, self.class.send("encrypt_#{attribute}".to_sym, value))
|
149
|
-
options.merge!(original_options)
|
150
|
-
instance_variable_set("@#{attribute}", value)
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
# Evaluates an option specified as a symbol representing an instance method or a proc
|
156
|
-
# If the option is not a symbol or proc then the original option is returned
|
157
|
-
def evaluate_attr_encrypted_option(option, object)
|
158
|
-
if option.is_a?(Symbol) && object.respond_to?(option)
|
159
|
-
object.send(option)
|
160
|
-
elsif option.respond_to?(:call)
|
161
|
-
option.call(object)
|
162
|
-
else
|
163
|
-
option
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
data/lib/huberry/data_mapper.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module DataMapper
|
3
|
-
def self.included(base)
|
4
|
-
base.class_eval do
|
5
|
-
extend ClassMethods
|
6
|
-
alias_method :read_attribute, :attribute_get
|
7
|
-
alias_method :write_attribute, :attribute_set
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
protected
|
13
|
-
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
14
|
-
# unless they've already been specified
|
15
|
-
def attr_encrypted(*attrs)
|
16
|
-
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
17
|
-
super *(attrs << options)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/huberry/object.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
module Huberry
|
2
|
-
module Object
|
3
|
-
def self.included(base)
|
4
|
-
base.class_eval do
|
5
|
-
extend ClassMethods
|
6
|
-
eattr_accessor :attr_encrypted_options, :encrypted_attributes
|
7
|
-
attr_encrypted_options = {}
|
8
|
-
encrypted_attributes = {}
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
# Wraps instance_variable_get
|
13
|
-
def read_attribute(attribute)
|
14
|
-
instance_variable_get("@#{attribute}")
|
15
|
-
end
|
16
|
-
|
17
|
-
# Wraps instance_variable_set
|
18
|
-
def write_attribute(attribute, value)
|
19
|
-
instance_variable_set("@#{attribute}", value)
|
20
|
-
end
|
21
|
-
|
22
|
-
module ClassMethods
|
23
|
-
# Checks if an attribute has been configured to be encrypted
|
24
|
-
#
|
25
|
-
# Example
|
26
|
-
#
|
27
|
-
# class User
|
28
|
-
# attr_accessor :name
|
29
|
-
# attr_encrypted :email
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# User.attr_encrypted?(:name) # false
|
33
|
-
# User.attr_encrypted?(:email) # true
|
34
|
-
def attr_encrypted?(attribute)
|
35
|
-
encrypted_attributes.keys.include?(attribute.to_s)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Copies existing encrypted attributes and options to the derived class
|
39
|
-
def inherited(base)
|
40
|
-
base.attr_encrypted_options = self.attr_encrypted_options.nil? ? {} : self.attr_encrypted_options.dup
|
41
|
-
base.encrypted_attributes = self.encrypted_attributes.nil? ? {} : self.encrypted_attributes.dup
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|