shuber-attr_encrypted 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +11 -0
- data/README.markdown +4 -4
- data/lib/attr_encrypted.rb +2 -10
- data/lib/huberry/attr_encrypted/adapters/active_record.rb +58 -0
- data/lib/huberry/attr_encrypted/adapters/data_mapper.rb +25 -0
- data/lib/huberry/attr_encrypted/class.rb +12 -11
- data/lib/huberry/attr_encrypted/object.rb +40 -36
- metadata +4 -4
- data/lib/huberry/attr_encrypted/active_record.rb +0 -50
- data/lib/huberry/attr_encrypted/data_mapper.rb +0 -23
data/CHANGELOG
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
2009-01-12 - Sean Huber (shuber@huberry.com)
|
2
|
+
* Remove read_attribute and write_attribute methods - unnecessary
|
3
|
+
* ActiveRecord and DataMapper extensions are now "adapters"
|
4
|
+
* Check for existing reader and writer methods separately before creating default ones for encrypted attributes
|
5
|
+
|
6
|
+
2009-01-11 - Sean Huber (shuber@huberry.com)
|
7
|
+
* Update README
|
8
|
+
|
9
|
+
2009-01-10 - Sean Huber (shuber@huberry.com)
|
10
|
+
* Update README
|
11
|
+
|
1
12
|
2009-01-08 - Sean Huber (shuber@huberry.com)
|
2
13
|
* Update comments and documentation
|
3
14
|
* Update README
|
data/README.markdown
CHANGED
@@ -3,7 +3,7 @@ attr\_encrypted
|
|
3
3
|
|
4
4
|
Generates attr\_accessors that encrypt and decrypt attributes transparently
|
5
5
|
|
6
|
-
Works with
|
6
|
+
Works with ANY class including ActiveRecord and DataMapper
|
7
7
|
|
8
8
|
|
9
9
|
Installation
|
@@ -97,7 +97,7 @@ If your class has an instance method that determines the encryption key to use,
|
|
97
97
|
|
98
98
|
#### Procs as keys ####
|
99
99
|
|
100
|
-
You can pass a proc object as the `:key` option as well:
|
100
|
+
You can pass a proc/lambda object as the `:key` option as well:
|
101
101
|
|
102
102
|
class User
|
103
103
|
attr_encrypted :email, :key => proc { |user| ... }
|
@@ -109,12 +109,12 @@ You can pass a proc object as the `:key` option as well:
|
|
109
109
|
There may be times that you want to only encrypt when certain conditions are met. For example maybe you're using rails and you don't want to encrypt
|
110
110
|
attributes when you're in development mode. You can specify conditions like this:
|
111
111
|
|
112
|
-
class User
|
112
|
+
class User < ActiveRecord::Base
|
113
113
|
attr_encrypted :email, :key => 'a secret key', :unless => Rails.env.development?
|
114
114
|
end
|
115
115
|
|
116
116
|
You can specify both `:if` and `:unless` options. If you pass a symbol representing an instance method then the result of the method will be evaluated.
|
117
|
-
Any objects that respond to
|
117
|
+
Any objects that respond to `:call` are evaluated as well.
|
118
118
|
|
119
119
|
|
120
120
|
### Custom encryptor ###
|
data/lib/attr_encrypted.rb
CHANGED
@@ -2,14 +2,6 @@ require 'huberry/attr_encrypted/class'
|
|
2
2
|
Class.send :include, Huberry::AttrEncrypted::Class
|
3
3
|
|
4
4
|
require 'huberry/attr_encrypted/object'
|
5
|
-
Object.
|
5
|
+
Object.extend Huberry::AttrEncrypted::Object
|
6
6
|
|
7
|
-
|
8
|
-
require 'huberry/attr_encrypted/active_record'
|
9
|
-
ActiveRecord::Base.extend Huberry::AttrEncrypted::ActiveRecord
|
10
|
-
end
|
11
|
-
|
12
|
-
if defined?(DataMapper)
|
13
|
-
require 'huberry/attr_encrypted/data_mapper'
|
14
|
-
DataMapper::Resource.send :include, Huberry::AttrEncrypted::DataMapper
|
15
|
-
end
|
7
|
+
Dir[File.join(File.dirname(__FILE__), 'huberry', 'attr_encrypted', 'adapters', '*.rb')].each { |file| require file }
|
@@ -0,0 +1,58 @@
|
|
1
|
+
if defined?(ActiveRecord)
|
2
|
+
module Huberry
|
3
|
+
module AttrEncrypted
|
4
|
+
module Adapters
|
5
|
+
module ActiveRecord
|
6
|
+
def self.extended(base)
|
7
|
+
base.eigenclass_eval { alias_method_chain :method_missing, :attr_encrypted }
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
13
|
+
# unless they've already been specified
|
14
|
+
#
|
15
|
+
# Also defines the attribute methods for db fields if they don't exist yet
|
16
|
+
def attr_encrypted(*attrs)
|
17
|
+
define_attribute_methods
|
18
|
+
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
19
|
+
super *(attrs << options)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
|
23
|
+
# encrypted attributes
|
24
|
+
#
|
25
|
+
# NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
|
26
|
+
#
|
27
|
+
# This is useful for encrypting fields like email addresses. Your user's email addresses
|
28
|
+
# are encrypted in the database, but you can still look up a user by email for logging in
|
29
|
+
#
|
30
|
+
# Example
|
31
|
+
#
|
32
|
+
# class User < ActiveRecord::Base
|
33
|
+
# attr_encrypted :email, :key => 'secret key'
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# User.find_by_email_and_password('test@example.com', 'testing')
|
37
|
+
# # results in a call to
|
38
|
+
# User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
|
39
|
+
def method_missing_with_attr_encrypted(method, *args, &block)
|
40
|
+
if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
|
41
|
+
attribute_names = match.captures.last.split('_and_')
|
42
|
+
attribute_names.each_with_index do |attribute, index|
|
43
|
+
if attr_encrypted?(attribute)
|
44
|
+
args[index] = send("encrypt_#{attribute}", args[index])
|
45
|
+
attribute_names[index] = encrypted_attributes[attribute]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
|
49
|
+
end
|
50
|
+
method_missing_without_attr_encrypted(method, *args, &block)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
ActiveRecord::Base.extend Huberry::AttrEncrypted::Adapters::ActiveRecord
|
58
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
if defined?(DataMapper)
|
2
|
+
module Huberry
|
3
|
+
module AttrEncrypted
|
4
|
+
module Adapters
|
5
|
+
module DataMapper
|
6
|
+
def self.included(base)
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
protected
|
12
|
+
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
13
|
+
# unless they've already been specified
|
14
|
+
def attr_encrypted(*attrs)
|
15
|
+
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
16
|
+
super *(attrs << options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
DataMapper::Resource.send :include, Huberry::AttrEncrypted::Adapters::DataMapper
|
25
|
+
end
|
@@ -89,14 +89,15 @@ module Huberry
|
|
89
89
|
:unless => false
|
90
90
|
}.merge(attr_encrypted_options).merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
91
91
|
options[:encode] = 'm*' if options[:encode] == true
|
92
|
-
|
92
|
+
|
93
93
|
attrs.each do |attribute|
|
94
94
|
encrypted_attribute_name = options[:attribute].nil? ? options[:prefix].to_s + attribute.to_s + options[:suffix].to_s : options[:attribute].to_s
|
95
|
-
|
95
|
+
|
96
96
|
encrypted_attributes[attribute.to_s] = encrypted_attribute_name
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
|
98
|
+
attr_reader encrypted_attribute_name.to_sym unless instance_methods.include?(encrypted_attribute_name)
|
99
|
+
attr_writer encrypted_attribute_name.to_sym unless instance_methods.include?("#{encrypted_attribute_name}=")
|
100
|
+
|
100
101
|
define_class_method "encrypt_#{attribute}" do |value|
|
101
102
|
if options[:if] && !options[:unless]
|
102
103
|
if value.nil?
|
@@ -111,7 +112,7 @@ module Huberry
|
|
111
112
|
value
|
112
113
|
end
|
113
114
|
end
|
114
|
-
|
115
|
+
|
115
116
|
define_class_method "decrypt_#{attribute}" do |encrypted_value|
|
116
117
|
if options[:if] && !options[:unless]
|
117
118
|
if encrypted_value.nil?
|
@@ -126,10 +127,10 @@ module Huberry
|
|
126
127
|
encrypted_value
|
127
128
|
end
|
128
129
|
end
|
129
|
-
|
130
|
+
|
130
131
|
define_method "#{attribute}" do
|
131
132
|
value = instance_variable_get("@#{attribute}")
|
132
|
-
encrypted_value =
|
133
|
+
encrypted_value = send(encrypted_attribute_name.to_s)
|
133
134
|
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
134
135
|
hash[option] = options[option]
|
135
136
|
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
@@ -139,20 +140,20 @@ module Huberry
|
|
139
140
|
options.merge!(original_options)
|
140
141
|
value
|
141
142
|
end
|
142
|
-
|
143
|
+
|
143
144
|
define_method "#{attribute}=" do |value|
|
144
145
|
original_options = [:key, :if, :unless].inject({}) do |hash, option|
|
145
146
|
hash[option] = options[option]
|
146
147
|
options[option] = self.class.send :evaluate_attr_encrypted_option, options[option], self
|
147
148
|
hash
|
148
149
|
end
|
149
|
-
|
150
|
+
send("#{encrypted_attribute_name}=".to_sym, self.class.send("encrypt_#{attribute}".to_sym, value))
|
150
151
|
options.merge!(original_options)
|
151
152
|
instance_variable_set("@#{attribute}", value)
|
152
153
|
end
|
153
154
|
end
|
154
155
|
end
|
155
|
-
|
156
|
+
|
156
157
|
# Evaluates an option specified as a symbol representing an instance method or a proc
|
157
158
|
# If the option is not a symbol or proc then the original option is returned
|
158
159
|
def evaluate_attr_encrypted_option(option, object)
|
@@ -1,46 +1,50 @@
|
|
1
1
|
module Huberry
|
2
2
|
module AttrEncrypted
|
3
3
|
module Object
|
4
|
-
def self.
|
5
|
-
base.
|
6
|
-
|
7
|
-
eattr_accessor :attr_encrypted_options, :encrypted_attributes
|
8
|
-
attr_encrypted_options = {}
|
9
|
-
encrypted_attributes = {}
|
10
|
-
end
|
4
|
+
def self.extended(base)
|
5
|
+
base.attr_encrypted_options = {}
|
6
|
+
base.instance_variable_set('@encrypted_attributes', {})
|
11
7
|
end
|
12
|
-
|
13
|
-
#
|
14
|
-
|
15
|
-
|
8
|
+
|
9
|
+
# Default options to use with calls to <tt>attr_encrypted</tt>.
|
10
|
+
#
|
11
|
+
# It will inherit existing options from its parent class
|
12
|
+
def attr_encrypted_options
|
13
|
+
@attr_encrypted_options ||= superclass.attr_encrypted_options.nil? ? {} : superclass.attr_encrypted_options.dup
|
14
|
+
end
|
15
|
+
|
16
|
+
# Sets default options to use with calls to <tt>attr_encrypted</tt>.
|
17
|
+
def attr_encrypted_options=(options)
|
18
|
+
@attr_encrypted_options = options
|
16
19
|
end
|
17
|
-
|
18
|
-
#
|
19
|
-
|
20
|
-
|
20
|
+
|
21
|
+
# Contains a hash of encrypted attributes with virtual attribute names as keys and real attribute
|
22
|
+
# names as values
|
23
|
+
#
|
24
|
+
# Example
|
25
|
+
#
|
26
|
+
# class User
|
27
|
+
# attr_encrypted :email
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# User.encrypted_attributes # { 'email' => 'encrypted_email' }
|
31
|
+
def encrypted_attributes
|
32
|
+
@encrypted_attributes ||= superclass.encrypted_attributes.nil? ? {} : superclass.encrypted_attributes.dup
|
21
33
|
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
34
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
35
|
+
# Checks if an attribute has been configured to be encrypted
|
36
|
+
#
|
37
|
+
# Example
|
38
|
+
#
|
39
|
+
# class User
|
40
|
+
# attr_accessor :name
|
41
|
+
# attr_encrypted :email
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# User.attr_encrypted?(:name) # false
|
45
|
+
# User.attr_encrypted?(:email) # true
|
46
|
+
def attr_encrypted?(attribute)
|
47
|
+
encrypted_attributes.keys.include?(attribute.to_s)
|
44
48
|
end
|
45
49
|
end
|
46
50
|
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.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Huber
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-12 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -41,9 +41,9 @@ extra_rdoc_files: []
|
|
41
41
|
files:
|
42
42
|
- CHANGELOG
|
43
43
|
- lib/attr_encrypted.rb
|
44
|
-
- lib/huberry/attr_encrypted/active_record.rb
|
44
|
+
- lib/huberry/attr_encrypted/adapters/active_record.rb
|
45
|
+
- lib/huberry/attr_encrypted/adapters/data_mapper.rb
|
45
46
|
- lib/huberry/attr_encrypted/class.rb
|
46
|
-
- lib/huberry/attr_encrypted/data_mapper.rb
|
47
47
|
- lib/huberry/attr_encrypted/object.rb
|
48
48
|
- MIT-LICENSE
|
49
49
|
- Rakefile
|
@@ -1,50 +0,0 @@
|
|
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
|
@@ -1,23 +0,0 @@
|
|
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
|