shuber-attr_encrypted 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|