shuber-attr_encrypted 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +3 -0
- data/README.markdown +5 -1
- data/lib/attr_encrypted.rb +5 -0
- data/lib/huberry/active_record.rb +1 -0
- data/lib/huberry/class.rb +12 -8
- data/lib/huberry/data_mapper.rb +21 -0
- data/lib/huberry/object.rb +0 -4
- data/test/attr_encrypted_test.rb +11 -0
- data/test/data_mapper_test.rb +48 -0
- data/test/test_helper.rb +2 -0
- metadata +3 -1
data/CHANGELOG
CHANGED
@@ -2,6 +2,9 @@
|
|
2
2
|
* Update comments and documentation
|
3
3
|
* Update README
|
4
4
|
* Add gemspec
|
5
|
+
* Add support for data mapper
|
6
|
+
* Attribute methods are now defined before calling attr_encrypted when using active record
|
7
|
+
* Add ability to specify your own encoding directives
|
5
8
|
|
6
9
|
2009-01-07 - Sean Huber (shuber@huberry.com)
|
7
10
|
* Initial commit
|
data/README.markdown
CHANGED
@@ -3,6 +3,8 @@ attr\_encrypted
|
|
3
3
|
|
4
4
|
Generates attr\_accessors that encrypt and decrypt attributes transparently
|
5
5
|
|
6
|
+
Works with any class including ActiveRecord and DataMapper
|
7
|
+
|
6
8
|
|
7
9
|
Installation
|
8
10
|
------------
|
@@ -215,12 +217,14 @@ This should help keep your classes clean and DRY.
|
|
215
217
|
### Encoding ###
|
216
218
|
|
217
219
|
You're probably going to be storing your encrypted attributes somehow (e.g. filesystem, database, etc) and may run into some issues trying to store a weird
|
218
|
-
encrypted string. I've had this problem myself using MySQL. You can simply pass the `:encode` option to automatically
|
220
|
+
encrypted string. I've had this problem myself using MySQL. You can simply pass the `:encode` option to automatically encode/decode when encrypting/decrypting.
|
219
221
|
|
220
222
|
class User
|
221
223
|
attr_encrypted :email, :key => 'some secret key', :encode => true
|
222
224
|
end
|
223
225
|
|
226
|
+
The default encoding is `m*` (base64). You can change this by setting `:encode => 'some encoding'`. See [Array#pack](http://www.ruby-doc.org/core/classes/Array.html#M002245) for more encoding options.
|
227
|
+
|
224
228
|
|
225
229
|
### Marshaling ###
|
226
230
|
|
data/lib/attr_encrypted.rb
CHANGED
@@ -7,4 +7,9 @@ Object.send :include, Huberry::Object
|
|
7
7
|
if defined?(ActiveRecord)
|
8
8
|
require 'huberry/active_record'
|
9
9
|
ActiveRecord::Base.extend Huberry::ActiveRecord
|
10
|
+
end
|
11
|
+
|
12
|
+
if defined?(DataMapper)
|
13
|
+
require 'huberry/data_mapper'
|
14
|
+
DataMapper::Resource.send :include, Huberry::DataMapper
|
10
15
|
end
|
@@ -9,6 +9,7 @@ module Huberry
|
|
9
9
|
# Calls attr_encrypted with the options <tt>:encode</tt> and <tt>:marshal</tt> set to true
|
10
10
|
# unless they've already been specified
|
11
11
|
def attr_encrypted(*attrs)
|
12
|
+
define_attribute_methods
|
12
13
|
options = { :encode => true, :marshal => true }.merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
13
14
|
super *(attrs << options)
|
14
15
|
end
|
data/lib/huberry/class.rb
CHANGED
@@ -26,12 +26,15 @@ module Huberry
|
|
26
26
|
# method before being passed to the encryptor. Proc objects are evaluated as well. Any other key types
|
27
27
|
# will be passed directly to the encryptor.
|
28
28
|
#
|
29
|
-
# :encode => If set to true, attributes will be
|
30
|
-
# planning on storing the encrypted attributes in a database.
|
31
|
-
#
|
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.
|
32
34
|
#
|
33
35
|
# :marshal => If set to true, attributes will be marshaled as well as encrypted. This is useful if you're planning
|
34
|
-
# on encrypting something other than a string. Defaults to false unless you're using it with ActiveRecord
|
36
|
+
# on encrypting something other than a string. Defaults to false unless you're using it with ActiveRecord
|
37
|
+
# or DataMapper.
|
35
38
|
#
|
36
39
|
# :encryptor => The object to use for encrypting. Defaults to Huberry::Encryptor.
|
37
40
|
#
|
@@ -75,13 +78,14 @@ module Huberry
|
|
75
78
|
:encode => false,
|
76
79
|
:marshal => false
|
77
80
|
}.merge(attr_encrypted_options).merge(attrs.last.is_a?(Hash) ? attrs.pop : {})
|
81
|
+
options[:encode] = 'm*' if options[:encode] == true
|
78
82
|
|
79
83
|
attrs.each do |attribute|
|
80
84
|
encrypted_attribute_name = options[:attribute].nil? ? options[:prefix].to_s + attribute.to_s + options[:suffix].to_s : options[:attribute].to_s
|
81
85
|
|
82
86
|
encrypted_attributes[attribute.to_s] = encrypted_attribute_name
|
83
87
|
|
84
|
-
attr_accessor encrypted_attribute_name.to_sym unless
|
88
|
+
attr_accessor encrypted_attribute_name.to_sym unless instance_methods.include?(encrypted_attribute_name)
|
85
89
|
|
86
90
|
define_class_method "encrypt_#{attribute}" do |value|
|
87
91
|
if value.nil?
|
@@ -89,7 +93,7 @@ module Huberry
|
|
89
93
|
else
|
90
94
|
value = Marshal.dump(value) if options[:marshal]
|
91
95
|
encrypted_value = options[:encryptor].send options[:encrypt_method], options.merge(:value => value)
|
92
|
-
encrypted_value = [encrypted_value].pack(
|
96
|
+
encrypted_value = [encrypted_value].pack(options[:encode]) if options[:encode]
|
93
97
|
end
|
94
98
|
encrypted_value
|
95
99
|
end
|
@@ -98,7 +102,7 @@ module Huberry
|
|
98
102
|
if encrypted_value.nil?
|
99
103
|
decrypted_value = nil
|
100
104
|
else
|
101
|
-
encrypted_value = encrypted_value.unpack(
|
105
|
+
encrypted_value = encrypted_value.unpack(options[:encode]).to_s if options[:encode]
|
102
106
|
decrypted_value = options[:encryptor].send(options[:decrypt_method], options.merge(:value => encrypted_value))
|
103
107
|
decrypted_value = Marshal.load(decrypted_value) if options[:marshal]
|
104
108
|
end
|
@@ -110,7 +114,7 @@ module Huberry
|
|
110
114
|
encrypted_value = read_attribute(encrypted_attribute_name)
|
111
115
|
original_key = options[:key]
|
112
116
|
options[:key] = self.class.send :evaluate_attr_encrypted_key, options[:key], self
|
113
|
-
value =
|
117
|
+
value = instance_variable_set("@#{attribute}", self.class.send("decrypt_#{attribute}".to_sym, encrypted_value)) if value.nil? && !encrypted_value.nil?
|
114
118
|
options[:key] = original_key
|
115
119
|
value
|
116
120
|
end
|
@@ -0,0 +1,21 @@
|
|
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
CHANGED
@@ -10,15 +10,11 @@ module Huberry
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Wraps instance_variable_get
|
13
|
-
#
|
14
|
-
# ActiveRecord overwrites this (if you're using it)
|
15
13
|
def read_attribute(attribute)
|
16
14
|
instance_variable_get("@#{attribute}")
|
17
15
|
end
|
18
16
|
|
19
17
|
# Wraps instance_variable_set
|
20
|
-
#
|
21
|
-
# ActiveRecord overwrites this (if you're using it)
|
22
18
|
def write_attribute(attribute, value)
|
23
19
|
instance_variable_set("@#{attribute}", value)
|
24
20
|
end
|
data/test/attr_encrypted_test.rb
CHANGED
@@ -18,6 +18,7 @@ class User
|
|
18
18
|
attr_encrypted :ssn, :key => :salt, :attribute => 'ssn_encrypted'
|
19
19
|
attr_encrypted :credit_card, :encryptor => SillyEncryptor, :encrypt_method => :silly_encrypt, :decrypt_method => :silly_decrypt, :some_arg => 'test'
|
20
20
|
attr_encrypted :with_encoding, :key => 'secret key', :encode => true
|
21
|
+
attr_encrypted :with_custom_encoding, :key => 'secret key', :encode => 'm'
|
21
22
|
attr_encrypted :with_marshaling, :key => 'secret key', :marshal => true
|
22
23
|
attr_accessor :salt
|
23
24
|
|
@@ -107,6 +108,16 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
107
108
|
assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m*').to_s)
|
108
109
|
end
|
109
110
|
|
111
|
+
def test_should_encrypt_with_custom_encoding
|
112
|
+
assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_should_decrypt_with_custom_encoding
|
116
|
+
encrypted = User.encrypt_with_encoding('test')
|
117
|
+
assert_equal 'test', User.decrypt_with_encoding(encrypted)
|
118
|
+
assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').to_s)
|
119
|
+
end
|
120
|
+
|
110
121
|
def test_should_encrypt_with_marshaling
|
111
122
|
@user = User.new
|
112
123
|
@user.with_marshaling = [1, 2, 3]
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
DataMapper.setup(:default, "sqlite3::memory:")
|
4
|
+
|
5
|
+
class Client
|
6
|
+
include DataMapper::Resource
|
7
|
+
|
8
|
+
property :id, Serial
|
9
|
+
property :encrypted_email, String
|
10
|
+
property :encrypted_credentials, Text
|
11
|
+
property :salt, String
|
12
|
+
|
13
|
+
attr_encrypted :email, :key => 'a secret key'
|
14
|
+
attr_encrypted :credentials, :key => Proc.new { |client| Huberry::Encryptor.encrypt(:value => client.salt, :key => 'some private key') }
|
15
|
+
|
16
|
+
def initialize(attrs = {})
|
17
|
+
super attrs
|
18
|
+
self.salt ||= Digest::SHA1.hexdigest((Time.now.to_i * rand(5)).to_s)
|
19
|
+
self.credentials ||= { :username => 'example', :password => 'test' }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
DataMapper.auto_migrate!
|
24
|
+
|
25
|
+
class DataMapperTest < Test::Unit::TestCase
|
26
|
+
|
27
|
+
def setup
|
28
|
+
Client.all.each(&:destroy)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_should_encrypt_email
|
32
|
+
@person = Client.new :email => 'test@example.com'
|
33
|
+
assert @person.save
|
34
|
+
assert_not_nil @person.encrypted_email
|
35
|
+
assert_not_equal @person.email, @person.encrypted_email
|
36
|
+
assert_equal @person.email, Client.first.email
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_marshal_and_encrypt_credentials
|
40
|
+
@person = Client.new
|
41
|
+
assert @person.save
|
42
|
+
assert_not_nil @person.encrypted_credentials
|
43
|
+
assert_not_equal @person.credentials, @person.encrypted_credentials
|
44
|
+
assert_equal @person.credentials, Client.first.credentials
|
45
|
+
assert Client.first.credentials.is_a?(Hash)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -5,9 +5,11 @@ require 'rubygems'
|
|
5
5
|
gem 'shuber-eigenclass', '>= 1.0.1'
|
6
6
|
gem 'shuber-encryptor'
|
7
7
|
gem 'activerecord'
|
8
|
+
gem 'datamapper'
|
8
9
|
|
9
10
|
require 'eigenclass'
|
10
11
|
require 'encryptor'
|
11
12
|
require 'active_record'
|
13
|
+
require 'datamapper'
|
12
14
|
|
13
15
|
require File.dirname(__FILE__) + '/../lib/attr_encrypted'
|
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.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sean Huber
|
@@ -43,6 +43,7 @@ files:
|
|
43
43
|
- lib/attr_encrypted.rb
|
44
44
|
- lib/huberry/active_record.rb
|
45
45
|
- lib/huberry/class.rb
|
46
|
+
- lib/huberry/data_mapper.rb
|
46
47
|
- lib/huberry/object.rb
|
47
48
|
- MIT-LICENSE
|
48
49
|
- Rakefile
|
@@ -80,3 +81,4 @@ summary: Generates attr_accessors that encrypt and decrypt attributes transparen
|
|
80
81
|
test_files:
|
81
82
|
- test/active_record_test.rb
|
82
83
|
- test/attr_encrypted_test.rb
|
84
|
+
- test/data_mapper_test.rb
|