attr_encryptor 1.0.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +108 -0
- data/README.md +10 -1
- data/Rakefile +1 -1
- data/attr_encryptor.gemspec +1 -1
- data/lib/attr_encryptor.rb +22 -15
- data/lib/attr_encryptor/adapters/active_record.rb +14 -7
- data/lib/attr_encryptor/version.rb +2 -2
- data/test/active_record_test.rb +12 -13
- data/test/attr_encrypted_test.rb +56 -19
- data/test/data_mapper_test.rb +3 -3
- data/test/sequel_test.rb +4 -5
- data/test/test_helper.rb +3 -1
- metadata +61 -40
- data/.rvmrc +0 -47
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 15fa84f14870e8333f343f5ce8d2e56b71e7c576
|
4
|
+
data.tar.gz: de53d4a0d42aa01b867d29834a056224d4173caf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 78abb59d5e439b54eb77f7eb4b786669457039a7dcc7724014caed61f941b664e0b2a2fb77f6e22f431c5d70b7fe1b4c38cb0580166725fe166092e4c5da8da5
|
7
|
+
data.tar.gz: ace36c9f963ab74233525b8a2e6ea9a3f41d3db9ec626b298305f30ddddcd94ac51f948fd1582c36a3c693275b4b936e7e916067c26bebd61478827c0031a6b8
|
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
attr_encrypted
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activemodel (4.0.0)
|
5
|
+
activesupport (= 4.0.0)
|
6
|
+
builder (~> 3.1.0)
|
7
|
+
activerecord (4.0.0)
|
8
|
+
activemodel (= 4.0.0)
|
9
|
+
activerecord-deprecated_finders (~> 1.0.2)
|
10
|
+
activesupport (= 4.0.0)
|
11
|
+
arel (~> 4.0.0)
|
12
|
+
activerecord-deprecated_finders (1.0.3)
|
13
|
+
activesupport (4.0.0)
|
14
|
+
i18n (~> 0.6, >= 0.6.4)
|
15
|
+
minitest (~> 4.2)
|
16
|
+
multi_json (~> 1.3)
|
17
|
+
thread_safe (~> 0.1)
|
18
|
+
tzinfo (~> 0.3.37)
|
19
|
+
addressable (2.3.5)
|
20
|
+
arel (4.0.0)
|
21
|
+
atomic (1.1.10)
|
22
|
+
bcrypt-ruby (3.1.1)
|
23
|
+
builder (3.1.4)
|
24
|
+
coderay (1.0.9)
|
25
|
+
data_objects (0.10.13)
|
26
|
+
addressable (~> 2.1)
|
27
|
+
datamapper (1.2.0)
|
28
|
+
dm-aggregates (~> 1.2.0)
|
29
|
+
dm-constraints (~> 1.2.0)
|
30
|
+
dm-core (~> 1.2.0)
|
31
|
+
dm-migrations (~> 1.2.0)
|
32
|
+
dm-serializer (~> 1.2.0)
|
33
|
+
dm-timestamps (~> 1.2.0)
|
34
|
+
dm-transactions (~> 1.2.0)
|
35
|
+
dm-types (~> 1.2.0)
|
36
|
+
dm-validations (~> 1.2.0)
|
37
|
+
dm-aggregates (1.2.0)
|
38
|
+
dm-core (~> 1.2.0)
|
39
|
+
dm-constraints (1.2.0)
|
40
|
+
dm-core (~> 1.2.0)
|
41
|
+
dm-core (1.2.1)
|
42
|
+
addressable (~> 2.3)
|
43
|
+
dm-do-adapter (1.2.0)
|
44
|
+
data_objects (~> 0.10.6)
|
45
|
+
dm-core (~> 1.2.0)
|
46
|
+
dm-migrations (1.2.0)
|
47
|
+
dm-core (~> 1.2.0)
|
48
|
+
dm-serializer (1.2.2)
|
49
|
+
dm-core (~> 1.2.0)
|
50
|
+
fastercsv (~> 1.5)
|
51
|
+
json (~> 1.6)
|
52
|
+
json_pure (~> 1.6)
|
53
|
+
multi_json (~> 1.0)
|
54
|
+
dm-sqlite-adapter (1.2.0)
|
55
|
+
dm-do-adapter (~> 1.2.0)
|
56
|
+
do_sqlite3 (~> 0.10.6)
|
57
|
+
dm-timestamps (1.2.0)
|
58
|
+
dm-core (~> 1.2.0)
|
59
|
+
dm-transactions (1.2.0)
|
60
|
+
dm-core (~> 1.2.0)
|
61
|
+
dm-types (1.2.2)
|
62
|
+
bcrypt-ruby (~> 3.0)
|
63
|
+
dm-core (~> 1.2.0)
|
64
|
+
fastercsv (~> 1.5)
|
65
|
+
json (~> 1.6)
|
66
|
+
multi_json (~> 1.0)
|
67
|
+
stringex (~> 1.4)
|
68
|
+
uuidtools (~> 2.1)
|
69
|
+
dm-validations (1.2.0)
|
70
|
+
dm-core (~> 1.2.0)
|
71
|
+
do_sqlite3 (0.10.13)
|
72
|
+
data_objects (= 0.10.13)
|
73
|
+
encryptor2 (1.0.0)
|
74
|
+
fastercsv (1.5.5)
|
75
|
+
i18n (0.6.4)
|
76
|
+
json (1.8.0)
|
77
|
+
json_pure (1.8.0)
|
78
|
+
metaclass (0.0.1)
|
79
|
+
method_source (0.8.1)
|
80
|
+
minitest (4.7.5)
|
81
|
+
mocha (0.14.0)
|
82
|
+
metaclass (~> 0.0.1)
|
83
|
+
multi_json (1.7.7)
|
84
|
+
pry (0.9.12.2)
|
85
|
+
coderay (~> 1.0.5)
|
86
|
+
method_source (~> 0.8)
|
87
|
+
slop (~> 3.4)
|
88
|
+
sequel (4.0.0)
|
89
|
+
slop (3.4.5)
|
90
|
+
sqlite3 (1.3.7)
|
91
|
+
stringex (1.5.1)
|
92
|
+
thread_safe (0.1.0)
|
93
|
+
atomic
|
94
|
+
tzinfo (0.3.37)
|
95
|
+
uuidtools (2.1.4)
|
96
|
+
|
97
|
+
PLATFORMS
|
98
|
+
ruby
|
99
|
+
|
100
|
+
DEPENDENCIES
|
101
|
+
activerecord
|
102
|
+
datamapper
|
103
|
+
dm-sqlite-adapter
|
104
|
+
encryptor2
|
105
|
+
mocha
|
106
|
+
pry
|
107
|
+
sequel
|
108
|
+
sqlite3
|
data/README.md
CHANGED
@@ -15,7 +15,7 @@ It works with ANY class, however, you get a few extra features when you're using
|
|
15
15
|
|
16
16
|
Encrypting attributes has never been easier:
|
17
17
|
|
18
|
-
###
|
18
|
+
### Your database
|
19
19
|
|
20
20
|
add a `encrypted_ssn`, `encrypted_ssn_salt`, `encrypted_ssn_iv`. All of
|
21
21
|
them will be populated automatically
|
@@ -23,6 +23,7 @@ them will be populated automatically
|
|
23
23
|
create_table :google_apps_admins do |t|
|
24
24
|
t.string :username
|
25
25
|
t.string :encrypted_password
|
26
|
+
t.string :encrypted_password_salt
|
26
27
|
t.string :encrypted_password_iv
|
27
28
|
t.string :domain
|
28
29
|
t.timestamps
|
@@ -243,6 +244,14 @@ encrypted string. I've had this problem myself using MySQL. You can simply pass
|
|
243
244
|
|
244
245
|
The default encoding is `m*` (base64). You can change this by setting `:encode => 'some encoding'`. See the `Array#pack` method at http://www.ruby-doc.org/core/classes/Array.html#M002245 for more encoding options.
|
245
246
|
|
247
|
+
### String Encoding/Charset
|
248
|
+
|
249
|
+
If you are trying to store UTF-8 (or other non-english charsets) strings in your encrypted parameters, you may get the decrypted string back as a US-ASCII or ASCII-8BIT string.
|
250
|
+
|
251
|
+
To fix this, add the `:charset => 'UTF-8'` option to your encrypted attribute definitions.
|
252
|
+
This option defaults to whatever your `Encoding.default_internal` is set to. `:charset => :default` will force it to use the default value.
|
253
|
+
|
254
|
+
This option only applies to encrypted strings and will be ignored when encrypting marshalled objects.
|
246
255
|
|
247
256
|
### Marshaling
|
248
257
|
|
data/Rakefile
CHANGED
data/attr_encryptor.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
|
17
17
|
s.author = 'Daniel Palacio'
|
18
18
|
s.email = 'danpal@gmail.com'
|
19
|
-
s.homepage = 'http://github.com/danpal/
|
19
|
+
s.homepage = 'http://github.com/danpal/attr_encryptor'
|
20
20
|
|
21
21
|
s.has_rdoc = false
|
22
22
|
s.rdoc_options = ['--line-numbers', '--inline-source', '--main', 'README.rdoc']
|
data/lib/attr_encryptor.rb
CHANGED
@@ -69,6 +69,8 @@ module AttrEncryptor
|
|
69
69
|
# :unless => Attributes are only encrypted if this option evaluates to false. If you pass a symbol representing an instance
|
70
70
|
# method then the result of the method will be evaluated. Any objects that respond to <tt>:call</tt> are evaluated as well.
|
71
71
|
# Defaults to false.
|
72
|
+
# :charset => Forces the decrypted string to be interpreted as the specified encoding. Does not change the underlying bits.
|
73
|
+
# Use :default to use Ruby's default encoding.
|
72
74
|
#
|
73
75
|
# You can specify your own default options
|
74
76
|
#
|
@@ -111,10 +113,12 @@ module AttrEncryptor
|
|
111
113
|
:load_method => 'load',
|
112
114
|
:encryptor => Encryptor,
|
113
115
|
:encrypt_method => 'encrypt',
|
114
|
-
:decrypt_method => 'decrypt'
|
116
|
+
:decrypt_method => 'decrypt',
|
117
|
+
:charset => :default
|
115
118
|
}.merge!(attr_encrypted_options).merge!(attributes.last.is_a?(Hash) ? attributes.pop : {})
|
116
119
|
|
117
120
|
options[:encode] = options[:default_encoding] if options[:encode] == true
|
121
|
+
options[:charset] = Encoding.default_internal if options[:charset] == :default
|
118
122
|
|
119
123
|
attributes.each do |attribute|
|
120
124
|
encrypted_attribute_name = (options[:attribute] ? options[:attribute] : [options[:prefix], attribute, options[:suffix]].join).to_sym
|
@@ -122,27 +126,27 @@ module AttrEncryptor
|
|
122
126
|
instance_methods_as_symbols = instance_methods.collect { |method| method.to_sym }
|
123
127
|
attr_reader encrypted_attribute_name unless instance_methods_as_symbols.include?(encrypted_attribute_name)
|
124
128
|
attr_writer encrypted_attribute_name unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
|
125
|
-
|
129
|
+
|
126
130
|
attr_reader (encrypted_attribute_name.to_s + "_iv").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_iv").to_sym )
|
127
131
|
attr_writer (encrypted_attribute_name.to_s + "_iv").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_iv").to_sym )
|
128
|
-
|
132
|
+
|
129
133
|
attr_reader (encrypted_attribute_name.to_s + "_salt").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_salt").to_sym )
|
130
134
|
attr_writer (encrypted_attribute_name.to_s + "_salt").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_salt").to_sym )
|
131
135
|
|
132
136
|
|
133
137
|
|
134
138
|
define_method(attribute) do
|
135
|
-
|
139
|
+
|
136
140
|
load_iv_for_attribute(attribute,encrypted_attribute_name, options[:algorithm])
|
137
|
-
load_salt_for_attribute(attribute,encrypted_attribute_name)
|
141
|
+
load_salt_for_attribute(attribute,encrypted_attribute_name)
|
138
142
|
|
139
143
|
instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", decrypt(attribute, send(encrypted_attribute_name)))
|
140
144
|
end
|
141
145
|
|
142
146
|
define_method("#{attribute}=") do |value|
|
143
147
|
load_iv_for_attribute(attribute, encrypted_attribute_name, options[:algorithm])
|
144
|
-
load_salt_for_attribute(attribute, encrypted_attribute_name)
|
145
|
-
|
148
|
+
load_salt_for_attribute(attribute, encrypted_attribute_name)
|
149
|
+
|
146
150
|
#this add's the iv and salt on the options for this instance
|
147
151
|
send("#{encrypted_attribute_name}=", encrypt(attribute, value))
|
148
152
|
instance_variable_set("@#{attribute}", value)
|
@@ -195,10 +199,16 @@ module AttrEncryptor
|
|
195
199
|
encrypted_value = encrypted_value.unpack(options[:encode]).first if options[:encode]
|
196
200
|
value = options[:encryptor].send(options[:decrypt_method], options.merge!(:value => encrypted_value))
|
197
201
|
value = options[:marshaler].send(options[:load_method], value) if options[:marshal]
|
198
|
-
value
|
202
|
+
return_value = value
|
199
203
|
else
|
200
|
-
encrypted_value
|
204
|
+
return_value = encrypted_value
|
201
205
|
end
|
206
|
+
|
207
|
+
if RUBY_VERSION > '1.9' && options[:charset].present? && return_value.present? && return_value.is_a?(String)
|
208
|
+
return_value.force_encoding(options[:charset])
|
209
|
+
end
|
210
|
+
|
211
|
+
return_value
|
202
212
|
end
|
203
213
|
|
204
214
|
# Encrypts a value for the attribute specified
|
@@ -292,10 +302,7 @@ module AttrEncryptor
|
|
292
302
|
def encrypt(attribute, value)
|
293
303
|
self.class.encrypt(attribute, value, evaluated_attr_encrypted_options_for(attribute))
|
294
304
|
end
|
295
|
-
|
296
|
-
def foo
|
297
305
|
|
298
|
-
end
|
299
306
|
protected
|
300
307
|
|
301
308
|
# Returns attr_encrypted options evaluated in the current object's scope for the attribute specified
|
@@ -319,7 +326,7 @@ module AttrEncryptor
|
|
319
326
|
def load_iv_for_attribute (attribute, encrypted_attribute_name, algorithm)
|
320
327
|
iv = send("#{encrypted_attribute_name.to_s + "_iv"}")
|
321
328
|
if(iv == nil)
|
322
|
-
begin
|
329
|
+
begin
|
323
330
|
algorithm = algorithm || "aes-256-cbc"
|
324
331
|
algo = OpenSSL::Cipher::Cipher.new(algorithm)
|
325
332
|
iv = [algo.random_iv].pack("m")
|
@@ -331,10 +338,10 @@ module AttrEncryptor
|
|
331
338
|
end
|
332
339
|
|
333
340
|
def load_salt_for_attribute(attribute, encrypted_attribute_name)
|
334
|
-
salt = send("#{encrypted_attribute_name.to_s + "_salt"}") || send("#{encrypted_attribute_name.to_s + "_salt"}=", Time.now.to_i.to_s)
|
341
|
+
salt = send("#{encrypted_attribute_name.to_s + "_salt"}") || send("#{encrypted_attribute_name.to_s + "_salt"}=", Digest::SHA256.hexdigest((Time.now.to_i * rand(1000)).to_s)[0..15])
|
335
342
|
self.class.encrypted_attributes[attribute.to_sym] = self.class.encrypted_attributes[attribute.to_sym].merge(:salt => salt)
|
336
343
|
end
|
337
|
-
|
344
|
+
|
338
345
|
|
339
346
|
end
|
340
347
|
end
|
@@ -4,19 +4,26 @@ if defined?(ActiveRecord::Base)
|
|
4
4
|
module ActiveRecord
|
5
5
|
def self.extended(base) # :nodoc:
|
6
6
|
base.class_eval do
|
7
|
+
class << self
|
8
|
+
alias_method_chain :attr_encrypted, :defined_attributes
|
9
|
+
alias_method_chain :attr_encryptor, :defined_attributes
|
10
|
+
end
|
11
|
+
|
7
12
|
attr_encrypted_options[:encode] = true
|
8
13
|
end
|
9
14
|
end
|
10
15
|
|
11
16
|
protected
|
12
17
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
# Ensures the attribute methods for db fields have been defined before calling the original
|
19
|
+
# <tt>attr_encrypted</tt> method
|
20
|
+
def attr_encrypted_with_defined_attributes(*attrs)
|
21
|
+
define_attribute_methods rescue nil
|
22
|
+
attr_encrypted_without_defined_attributes *attrs
|
23
|
+
attrs.reject { |attr| attr.is_a?(Hash) }.each { |attr| alias_method "#{attr}_before_type_cast", attr }
|
24
|
+
end
|
25
|
+
|
26
|
+
alias attr_encryptor_with_defined_attributes attr_encrypted_with_defined_attributes
|
20
27
|
end
|
21
28
|
end
|
22
29
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module AttrEncryptor
|
2
2
|
# Contains information about this gem's version
|
3
3
|
module Version
|
4
|
-
MAJOR =
|
4
|
+
MAJOR = 2
|
5
5
|
MINOR = 0
|
6
|
-
PATCH =
|
6
|
+
PATCH = 0
|
7
7
|
|
8
8
|
# Returns a version string by joining <tt>MAJOR</tt>, <tt>MINOR</tt>, and <tt>PATCH</tt> with <tt>'.'</tt>
|
9
9
|
#
|
data/test/active_record_test.rb
CHANGED
@@ -30,8 +30,8 @@ create_tables
|
|
30
30
|
ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
|
31
31
|
|
32
32
|
class Person < ActiveRecord::Base
|
33
|
-
attr_encrypted :email, :key =>
|
34
|
-
attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key =>
|
33
|
+
attr_encrypted :email, :key => SECRET_KEY
|
34
|
+
attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY) }, :marshal => true
|
35
35
|
|
36
36
|
|
37
37
|
after_initialize :initialize_salt_and_credentials
|
@@ -39,7 +39,7 @@ class Person < ActiveRecord::Base
|
|
39
39
|
protected
|
40
40
|
|
41
41
|
def initialize_salt_and_credentials
|
42
|
-
self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(
|
42
|
+
self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(1000)).to_s)[0..15]
|
43
43
|
self.credentials ||= { :username => 'example', :password => 'test' }
|
44
44
|
end
|
45
45
|
end
|
@@ -58,43 +58,42 @@ class ActiveRecordTest < Test::Unit::TestCase
|
|
58
58
|
def setup
|
59
59
|
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
60
60
|
create_tables
|
61
|
-
Account.create!(:key =>
|
61
|
+
Account.create!(:key => SECRET_KEY, :password => "password")
|
62
62
|
end
|
63
63
|
|
64
|
-
def
|
64
|
+
def test_should_encrypt_email
|
65
65
|
@person = Person.create :email => 'test@example.com'
|
66
66
|
assert_not_nil @person.encrypted_email
|
67
67
|
assert_not_equal @person.email, @person.encrypted_email
|
68
68
|
assert_equal @person.email, Person.find(:first).email
|
69
69
|
end
|
70
70
|
|
71
|
-
def
|
71
|
+
def test_should_marshal_and_encrypt_credentials
|
72
72
|
@person = Person.create
|
73
73
|
assert_not_nil @person.encrypted_credentials
|
74
74
|
assert_not_equal @person.credentials, @person.encrypted_credentials
|
75
75
|
assert_equal @person.credentials, Person.find(:first).credentials
|
76
76
|
end
|
77
77
|
|
78
|
-
def
|
78
|
+
def test_should_encode_by_default
|
79
79
|
assert Person.attr_encrypted_options[:encode]
|
80
80
|
end
|
81
81
|
|
82
|
-
def
|
82
|
+
def test_should_validate_presence_of_email
|
83
83
|
@person = PersonWithValidation.new
|
84
84
|
assert !@person.valid?
|
85
85
|
assert !@person.errors[:email].empty? || @person.errors.on(:email)
|
86
86
|
end
|
87
87
|
|
88
|
-
def
|
88
|
+
def test_should_encrypt_decrypt_with_iv
|
89
89
|
@person = Person.create :email => 'test@example.com'
|
90
90
|
@person2 = Person.find(@person.id)
|
91
91
|
assert_not_nil @person2.encrypted_email_iv
|
92
92
|
assert_equal 'test@example.com', @person2.email
|
93
93
|
end
|
94
|
-
|
94
|
+
|
95
95
|
def _test_should_create_an_account_regardless_of_arguments_order
|
96
|
-
Account.create!(:key =>
|
97
|
-
Account.create!(:password => "password" , :key =>
|
96
|
+
Account.create!(:key => SECRET_KEY, :password => "password")
|
97
|
+
Account.create!(:password => "password" , :key => SECRET_KEY)
|
98
98
|
end
|
99
|
-
|
100
99
|
end
|
data/test/attr_encrypted_test.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require File.expand_path('../test_helper', __FILE__)
|
2
3
|
|
3
4
|
class SillyEncryptor
|
@@ -11,22 +12,26 @@ class SillyEncryptor
|
|
11
12
|
end
|
12
13
|
|
13
14
|
class User
|
14
|
-
self.attr_encrypted_options[:key] = Proc.new { |user|
|
15
|
+
self.attr_encrypted_options[:key] = Proc.new { |user| SECRET_KEY } # default key
|
15
16
|
|
16
|
-
attr_encrypted :email, :without_encoding, :key =>
|
17
|
+
attr_encrypted :email, :without_encoding, :key => SECRET_KEY
|
17
18
|
attr_encrypted :password, :prefix => 'crypted_', :suffix => '_test'
|
18
|
-
attr_encrypted :ssn, :key => :
|
19
|
+
attr_encrypted :ssn, :key => :secret_key, :attribute => 'ssn_encrypted'
|
19
20
|
attr_encrypted :credit_card, :encryptor => SillyEncryptor, :encrypt_method => :silly_encrypt, :decrypt_method => :silly_decrypt, :some_arg => 'test'
|
20
|
-
attr_encrypted :with_encoding, :key =>
|
21
|
-
attr_encrypted :with_custom_encoding, :key =>
|
22
|
-
attr_encrypted :with_marshaling, :key =>
|
23
|
-
attr_encrypted :with_true_if, :key =>
|
24
|
-
attr_encrypted :with_false_if, :key =>
|
25
|
-
attr_encrypted :with_true_unless, :key =>
|
26
|
-
attr_encrypted :with_false_unless, :key =>
|
27
|
-
attr_encrypted :with_if_changed, :key =>
|
21
|
+
attr_encrypted :with_encoding, :key => SECRET_KEY, :encode => true
|
22
|
+
attr_encrypted :with_custom_encoding, :key => SECRET_KEY, :encode => 'm'
|
23
|
+
attr_encrypted :with_marshaling, :key => SECRET_KEY, :marshal => true
|
24
|
+
attr_encrypted :with_true_if, :key => SECRET_KEY, :if => true
|
25
|
+
attr_encrypted :with_false_if, :key => SECRET_KEY, :if => false
|
26
|
+
attr_encrypted :with_true_unless, :key => SECRET_KEY, :unless => true
|
27
|
+
attr_encrypted :with_false_unless, :key => SECRET_KEY, :unless => false
|
28
|
+
attr_encrypted :with_if_changed, :key => SECRET_KEY, :if => :should_encrypt
|
28
29
|
|
29
|
-
|
30
|
+
attr_encrypted :utf8, :key => SECRET_KEY, :charset => 'UTF-8'
|
31
|
+
attr_encrypted :default_enc, :key => SECRET_KEY
|
32
|
+
attr_encrypted :us_ascii, :key => SECRET_KEY, :charset => 'US-ASCII'
|
33
|
+
|
34
|
+
attr_encryptor :aliased, :key => SECRET_KEY
|
30
35
|
|
31
36
|
attr_accessor :salt
|
32
37
|
attr_accessor :should_encrypt
|
@@ -35,6 +40,10 @@ class User
|
|
35
40
|
self.salt = Time.now.to_i.to_s
|
36
41
|
self.should_encrypt = true
|
37
42
|
end
|
43
|
+
|
44
|
+
def secret_key
|
45
|
+
SECRET_KEY
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
class Admin < User
|
@@ -158,7 +167,8 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
158
167
|
assert_nil @user.ssn_encrypted
|
159
168
|
@user.ssn = 'testing'
|
160
169
|
assert_not_nil @user.ssn_encrypted
|
161
|
-
|
170
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt )
|
171
|
+
assert_equal encrypted, @user.ssn_encrypted
|
162
172
|
end
|
163
173
|
|
164
174
|
def test_should_evaluate_a_key_passed_as_a_proc
|
@@ -166,7 +176,8 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
166
176
|
assert_nil @user.crypted_password_test
|
167
177
|
@user.password = 'testing'
|
168
178
|
assert_not_nil @user.crypted_password_test
|
169
|
-
|
179
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
|
180
|
+
assert_equal encrypted, @user.crypted_password_test
|
170
181
|
end
|
171
182
|
|
172
183
|
def test_should_use_options_found_in_the_attr_encrypted_options_attribute
|
@@ -174,8 +185,9 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
174
185
|
assert_nil @user.crypted_password_test
|
175
186
|
@user.password = 'testing'
|
176
187
|
assert_not_nil @user.crypted_password_test
|
177
|
-
|
178
|
-
|
188
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
|
189
|
+
assert_equal encrypted, @user.crypted_password_test
|
190
|
+
end
|
179
191
|
|
180
192
|
def test_should_inherit_encrypted_attributes
|
181
193
|
assert_equal [User.encrypted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, Admin.encrypted_attributes.keys.collect { |key| key.to_s }.sort
|
@@ -216,7 +228,8 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
216
228
|
assert_nil @user.encrypted_with_true_if
|
217
229
|
@user.with_true_if = 'testing'
|
218
230
|
assert_not_nil @user.encrypted_with_true_if
|
219
|
-
|
231
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_true_if_iv.unpack("m").first, :salt => @user.encrypted_with_true_if_salt)
|
232
|
+
assert_equal encrypted, @user.encrypted_with_true_if
|
220
233
|
end
|
221
234
|
|
222
235
|
def test_should_not_encrypt_with_false_if
|
@@ -232,7 +245,8 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
232
245
|
assert_nil @user.encrypted_with_false_unless
|
233
246
|
@user.with_false_unless = 'testing'
|
234
247
|
assert_not_nil @user.encrypted_with_false_unless
|
235
|
-
|
248
|
+
encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.encrypted_with_false_unless_iv.unpack("m").first, :salt => @user.encrypted_with_false_unless_salt)
|
249
|
+
assert_equal encrypted, @user.encrypted_with_false_unless
|
236
250
|
end
|
237
251
|
|
238
252
|
def test_should_not_encrypt_with_true_unless
|
@@ -252,7 +266,7 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
252
266
|
@user.with_if_changed = "encrypt_stuff"
|
253
267
|
@user.stubs(:instance_variable_get).returns(nil)
|
254
268
|
@user.stubs(:instance_variable_set).raises("BadStuff")
|
255
|
-
assert_raise RuntimeError do
|
269
|
+
assert_raise RuntimeError do
|
256
270
|
@user.with_if_changed
|
257
271
|
end
|
258
272
|
|
@@ -269,6 +283,29 @@ class AttrEncryptorTest < Test::Unit::TestCase
|
|
269
283
|
assert_equal '3', User.decrypt_email(string_encrypted_email)
|
270
284
|
end
|
271
285
|
|
286
|
+
def test_should_force_utf8_charset_on_decrypted_string
|
287
|
+
utf8_str = 'thïs should bé UTF-8'.force_encoding('UTF-8')
|
288
|
+
encrypted_utf8 = User.encrypt_utf8(utf8_str)
|
289
|
+
decrypted_utf8 = User.decrypt_utf8(encrypted_utf8)
|
290
|
+
assert_equal decrypted_utf8.encoding, Encoding::UTF_8
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_should_force_default_encoding_on_decrypted_string
|
294
|
+
Encoding.default_internal = 'ASCII-8BIT' #Provide a default.
|
295
|
+
default_str = 'this is a default encoding'
|
296
|
+
encrypted_def = User.encrypt_default_enc(default_str)
|
297
|
+
decrypted_def = User.decrypt_default_enc(encrypted_def)
|
298
|
+
assert_equal decrypted_def.encoding, Encoding.default_internal
|
299
|
+
Encoding.default_internal = nil
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_should_force_ascii_charset_on_decrypted_string
|
303
|
+
ascii_str = 'this should be US-ASCII'.force_encoding('US-ASCII')
|
304
|
+
encrypted_ascii = User.encrypt_us_ascii(ascii_str)
|
305
|
+
decrypted_ascii = User.decrypt_us_ascii(encrypted_ascii)
|
306
|
+
assert_equal decrypted_ascii.encoding, Encoding::US_ASCII
|
307
|
+
end
|
308
|
+
|
272
309
|
def test_should_create_query_accessor
|
273
310
|
@user = User.new
|
274
311
|
assert !@user.email?
|
data/test/data_mapper_test.rb
CHANGED
@@ -13,9 +13,9 @@ class Client
|
|
13
13
|
property :encrypted_credentials, Text
|
14
14
|
property :encrypted_credentials_iv, Text
|
15
15
|
property :encrypted_credentials_salt, Text
|
16
|
-
|
17
|
-
attr_encrypted :email, :key =>
|
18
|
-
attr_encrypted :credentials, :key =>
|
16
|
+
|
17
|
+
attr_encrypted :email, :key => SECRET_KEY
|
18
|
+
attr_encrypted :credentials, :key => SECRET_KEY, :marshal => true
|
19
19
|
|
20
20
|
def initialize(attrs = {})
|
21
21
|
super attrs
|
data/test/sequel_test.rb
CHANGED
@@ -13,9 +13,9 @@ DB.create_table :humans do
|
|
13
13
|
column :encrypted_credentials_salt, String
|
14
14
|
end
|
15
15
|
|
16
|
-
class Human < Sequel::Model(:humans)
|
17
|
-
attr_encrypted :email, :key =>
|
18
|
-
attr_encrypted :credentials, :key =>
|
16
|
+
class Human < Sequel::Model(:humans)
|
17
|
+
attr_encrypted :email, :key => SECRET_KEY
|
18
|
+
attr_encrypted :credentials, :key => SECRET_KEY, :marshal => true
|
19
19
|
|
20
20
|
def after_initialize(attrs = {})
|
21
21
|
self.credentials ||= { :username => 'example', :password => 'test' }
|
@@ -29,7 +29,6 @@ class SequelTest < Test::Unit::TestCase
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_should_encrypt_email
|
32
|
-
require 'ruby-debug'
|
33
32
|
@human = Human.new :email => 'test@example.com'
|
34
33
|
assert @human.save
|
35
34
|
assert_not_nil @human.encrypted_email
|
@@ -39,7 +38,7 @@ class SequelTest < Test::Unit::TestCase
|
|
39
38
|
|
40
39
|
def test_should_marshal_and_encrypt_credentials
|
41
40
|
|
42
|
-
@human = Human.new
|
41
|
+
@human = Human.new :credentials => { :username => 'example', :password => 'test' }
|
43
42
|
assert @human.save
|
44
43
|
assert_not_nil @human.encrypted_credentials
|
45
44
|
assert_not_equal @human.credentials, @human.encrypted_credentials
|
data/test/test_helper.rb
CHANGED
@@ -5,10 +5,12 @@ gem 'activerecord', ENV['ACTIVE_RECORD_VERSION'] if ENV['ACTIVE_RECORD_VERSION']
|
|
5
5
|
require 'active_record'
|
6
6
|
require 'data_mapper'
|
7
7
|
require 'sequel'
|
8
|
-
require 'mocha'
|
8
|
+
require 'mocha/setup'
|
9
9
|
|
10
10
|
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
11
11
|
$:.unshift(File.dirname(__FILE__))
|
12
12
|
require 'attr_encryptor'
|
13
13
|
|
14
14
|
puts "\nTesting with ActiveRecord #{ActiveRecord::VERSION::STRING rescue ENV['ACTIVE_RECORD_VERSION']}"
|
15
|
+
|
16
|
+
SECRET_KEY = 4.times.map { Digest::SHA256.hexdigest((Time.now.to_i * rand(5)).to_s) }.join
|
metadata
CHANGED
@@ -1,93 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attr_encryptor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Daniel Palacio
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2013-07-17 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: encryptor2
|
16
|
-
requirement:
|
17
|
-
none: false
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: 1.0.0
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
|
-
version_requirements:
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
25
27
|
- !ruby/object:Gem::Dependency
|
26
28
|
name: activerecord
|
27
|
-
requirement:
|
28
|
-
none: false
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
|
-
- -
|
31
|
+
- - '>='
|
31
32
|
- !ruby/object:Gem::Version
|
32
33
|
version: 2.0.0
|
33
34
|
type: :development
|
34
35
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.0.0
|
36
41
|
- !ruby/object:Gem::Dependency
|
37
42
|
name: datamapper
|
38
|
-
requirement:
|
39
|
-
none: false
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
40
44
|
requirements:
|
41
|
-
- -
|
45
|
+
- - '>='
|
42
46
|
- !ruby/object:Gem::Version
|
43
47
|
version: '0'
|
44
48
|
type: :development
|
45
49
|
prerelease: false
|
46
|
-
version_requirements:
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
47
55
|
- !ruby/object:Gem::Dependency
|
48
56
|
name: mocha
|
49
|
-
requirement:
|
50
|
-
none: false
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
51
58
|
requirements:
|
52
|
-
- -
|
59
|
+
- - '>='
|
53
60
|
- !ruby/object:Gem::Version
|
54
61
|
version: '0'
|
55
62
|
type: :development
|
56
63
|
prerelease: false
|
57
|
-
version_requirements:
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: sequel
|
60
|
-
requirement:
|
61
|
-
none: false
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
62
72
|
requirements:
|
63
|
-
- -
|
73
|
+
- - '>='
|
64
74
|
- !ruby/object:Gem::Version
|
65
75
|
version: '0'
|
66
76
|
type: :development
|
67
77
|
prerelease: false
|
68
|
-
version_requirements:
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: dm-sqlite-adapter
|
71
|
-
requirement:
|
72
|
-
none: false
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
73
86
|
requirements:
|
74
|
-
- -
|
87
|
+
- - '>='
|
75
88
|
- !ruby/object:Gem::Version
|
76
89
|
version: '0'
|
77
90
|
type: :development
|
78
91
|
prerelease: false
|
79
|
-
version_requirements:
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
80
97
|
- !ruby/object:Gem::Dependency
|
81
98
|
name: sqlite3
|
82
|
-
requirement:
|
83
|
-
none: false
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
84
100
|
requirements:
|
85
|
-
- -
|
101
|
+
- - '>='
|
86
102
|
- !ruby/object:Gem::Version
|
87
103
|
version: '0'
|
88
104
|
type: :development
|
89
105
|
prerelease: false
|
90
|
-
version_requirements:
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
91
111
|
description: Generates attr_accessors that encrypt and decrypt attributes transparently
|
92
112
|
email: danpal@gmail.com
|
93
113
|
executables: []
|
@@ -95,7 +115,10 @@ extensions: []
|
|
95
115
|
extra_rdoc_files: []
|
96
116
|
files:
|
97
117
|
- .gitignore
|
98
|
-
- .
|
118
|
+
- .ruby-gemset
|
119
|
+
- .ruby-version
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
99
122
|
- MIT-LICENSE
|
100
123
|
- README.md
|
101
124
|
- Rakefile
|
@@ -111,8 +134,9 @@ files:
|
|
111
134
|
- test/debug_order.rb
|
112
135
|
- test/sequel_test.rb
|
113
136
|
- test/test_helper.rb
|
114
|
-
homepage: http://github.com/danpal/
|
137
|
+
homepage: http://github.com/danpal/attr_encryptor
|
115
138
|
licenses: []
|
139
|
+
metadata: {}
|
116
140
|
post_install_message:
|
117
141
|
rdoc_options:
|
118
142
|
- --line-numbers
|
@@ -122,22 +146,20 @@ rdoc_options:
|
|
122
146
|
require_paths:
|
123
147
|
- lib
|
124
148
|
required_ruby_version: !ruby/object:Gem::Requirement
|
125
|
-
none: false
|
126
149
|
requirements:
|
127
|
-
- -
|
150
|
+
- - '>='
|
128
151
|
- !ruby/object:Gem::Version
|
129
152
|
version: '0'
|
130
153
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
|
-
none: false
|
132
154
|
requirements:
|
133
|
-
- -
|
155
|
+
- - '>='
|
134
156
|
- !ruby/object:Gem::Version
|
135
157
|
version: '0'
|
136
158
|
requirements: []
|
137
159
|
rubyforge_project:
|
138
|
-
rubygems_version:
|
160
|
+
rubygems_version: 2.0.3
|
139
161
|
signing_key:
|
140
|
-
specification_version:
|
162
|
+
specification_version: 4
|
141
163
|
summary: Encrypt and decrypt attributes
|
142
164
|
test_files:
|
143
165
|
- test/active_record_test.rb
|
@@ -146,4 +168,3 @@ test_files:
|
|
146
168
|
- test/debug_order.rb
|
147
169
|
- test/sequel_test.rb
|
148
170
|
- test/test_helper.rb
|
149
|
-
has_rdoc: false
|
data/.rvmrc
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
#!/usr/bin/env bash
|
2
|
-
|
3
|
-
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
-
# development environment upon cd'ing into the directory
|
5
|
-
|
6
|
-
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional.
|
7
|
-
environment_id="ruby-1.9.2-p290@attr_encrypted"
|
8
|
-
|
9
|
-
#
|
10
|
-
# Uncomment following line if you want options to be set only for given project.
|
11
|
-
#
|
12
|
-
# PROJECT_JRUBY_OPTS=( --1.9 )
|
13
|
-
|
14
|
-
#
|
15
|
-
# First we attempt to load the desired environment directly from the environment
|
16
|
-
# file. This is very fast and efficient compared to running through the entire
|
17
|
-
# CLI and selector. If you want feedback on which environment was used then
|
18
|
-
# insert the word 'use' after --create as this triggers verbose mode.
|
19
|
-
#
|
20
|
-
if [[ -d "${rvm_path:-$HOME/.rvm}/environments" \
|
21
|
-
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
22
|
-
then
|
23
|
-
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
24
|
-
|
25
|
-
if [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]]
|
26
|
-
then
|
27
|
-
. "${rvm_path:-$HOME/.rvm}/hooks/after_use"
|
28
|
-
fi
|
29
|
-
else
|
30
|
-
# If the environment file has not yet been created, use the RVM CLI to select.
|
31
|
-
if ! rvm --create "$environment_id"
|
32
|
-
then
|
33
|
-
echo "Failed to create RVM environment '${environment_id}'."
|
34
|
-
exit 1
|
35
|
-
fi
|
36
|
-
fi
|
37
|
-
|
38
|
-
#
|
39
|
-
# If you use an RVM gemset file to install a list of gems (*.gems), you can have
|
40
|
-
# it be automatically loaded. Uncomment the following and adjust the filename if
|
41
|
-
# necessary.
|
42
|
-
#
|
43
|
-
# filename=".gems"
|
44
|
-
# if [[ -s "$filename" ]] ; then
|
45
|
-
# rvm gemset import "$filename" | grep -v already | grep -v listed | grep -v complete | sed '/^$/d'
|
46
|
-
# fi
|
47
|
-
|