attr_encryptor 1.0.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|