attr_encrypted 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,4 +1,4 @@
1
- = attr_encrypted
1
+ = attr_encrypted {<img src="https://travis-ci.org/attr-encrypted/attr_encrypted.png" />}[https://travis-ci.org/attr-encrypted/attr_encrypted]
2
2
 
3
3
  Generates attr_accessors that encrypt and decrypt attributes transparently
4
4
 
@@ -299,4 +299,4 @@ Just like the default options for <tt>ActiveRecord</tt>, the <tt>:encode</tt> op
299
299
  * Add tests for it. This is important so I don't break it in a
300
300
  future version unintentionally.
301
301
  * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
302
- * Send me a pull request. Bonus points for topic branches.
302
+ * Send me a pull request. Bonus points for topic branches.
data/Rakefile CHANGED
@@ -2,9 +2,6 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
 
5
- desc 'Default: run unit tests.'
6
- task :default => :test
7
-
8
5
  desc 'Test the attr_encrypted gem.'
9
6
  Rake::TestTask.new(:test) do |t|
10
7
  t.libs << 'lib'
@@ -19,4 +16,18 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
19
16
  rdoc.options << '--line-numbers' << '--inline-source'
20
17
  rdoc.rdoc_files.include('README*')
21
18
  rdoc.rdoc_files.include('lib/**/*.rb')
22
- end
19
+ end
20
+
21
+ if RUBY_VERSION < '1.9.3'
22
+ require 'rcov/rcovtask'
23
+
24
+ task :rcov do
25
+ system "rcov -o coverage/rcov --exclude '^(?!lib)' " + FileList[ 'test/**/*_test.rb' ].join(' ')
26
+ end
27
+
28
+ desc 'Default: run unit tests under rcov.'
29
+ task :default => :rcov
30
+ else
31
+ desc 'Default: run unit tests.'
32
+ task :default => :test
33
+ end
@@ -69,6 +69,12 @@ module AttrEncrypted
69
69
  # method then the result of the method will be evaluated. Any objects that respond to <tt>:call</tt> are evaluated as well.
70
70
  # Defaults to false.
71
71
  #
72
+ # :mode => Selects encryption mode for attribute: choose <tt>:single_iv_and_salt</tt> for compatibility
73
+ # with the old attr_encrypted API: the default IV and salt of the underlying encryptor object
74
+ # is used; <tt>:per_attribute_iv_and_salt</tt> uses a per-attribute IV and salt attribute and
75
+ # is the recommended mode for new deployments.
76
+ # Defaults to <tt>:single_iv_and_salt</tt>.
77
+ #
72
78
  # You can specify your own default options
73
79
  #
74
80
  # class User
@@ -110,7 +116,8 @@ module AttrEncrypted
110
116
  :load_method => 'load',
111
117
  :encryptor => Encryptor,
112
118
  :encrypt_method => 'encrypt',
113
- :decrypt_method => 'decrypt'
119
+ :decrypt_method => 'decrypt',
120
+ :mode => :single_iv_and_salt
114
121
  }.merge!(attr_encrypted_options).merge!(attributes.last.is_a?(Hash) ? attributes.pop : {})
115
122
 
116
123
  options[:encode] = options[:default_encoding] if options[:encode] == true
@@ -122,11 +129,29 @@ module AttrEncrypted
122
129
  attr_reader encrypted_attribute_name unless instance_methods_as_symbols.include?(encrypted_attribute_name)
123
130
  attr_writer encrypted_attribute_name unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=")
124
131
 
132
+ if options[:mode] == :per_attribute_iv_and_salt
133
+ attr_reader (encrypted_attribute_name.to_s + "_iv").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_iv").to_sym )
134
+ attr_writer (encrypted_attribute_name.to_s + "_iv").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_iv").to_sym )
135
+
136
+ attr_reader (encrypted_attribute_name.to_s + "_salt").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_salt").to_sym )
137
+ attr_writer (encrypted_attribute_name.to_s + "_salt").to_sym unless instance_methods_as_symbols.include?((encrypted_attribute_name.to_s + "_salt").to_sym )
138
+ end
139
+
125
140
  define_method(attribute) do
141
+ if options[:mode] == :per_attribute_iv_and_salt
142
+ load_iv_for_attribute(attribute,encrypted_attribute_name, options[:algorithm])
143
+ load_salt_for_attribute(attribute,encrypted_attribute_name)
144
+ end
145
+
126
146
  instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", decrypt(attribute, send(encrypted_attribute_name)))
127
147
  end
128
148
 
129
149
  define_method("#{attribute}=") do |value|
150
+ if options[:mode] == :per_attribute_iv_and_salt
151
+ load_iv_for_attribute(attribute, encrypted_attribute_name, options[:algorithm])
152
+ load_salt_for_attribute(attribute, encrypted_attribute_name)
153
+ end
154
+
130
155
  send("#{encrypted_attribute_name}=", encrypt(attribute, value))
131
156
  instance_variable_set("@#{attribute}", value)
132
157
  end
@@ -300,6 +325,25 @@ module AttrEncrypted
300
325
  option
301
326
  end
302
327
  end
328
+
329
+ def load_iv_for_attribute (attribute, encrypted_attribute_name, algorithm)
330
+ iv = send("#{encrypted_attribute_name.to_s + "_iv"}")
331
+ if(iv == nil)
332
+ begin
333
+ algorithm = algorithm || "aes-256-cbc"
334
+ algo = OpenSSL::Cipher::Cipher.new(algorithm)
335
+ iv = [algo.random_iv].pack("m")
336
+ send("#{encrypted_attribute_name.to_s + "_iv"}=", iv)
337
+ rescue RuntimeError
338
+ end
339
+ end
340
+ self.class.encrypted_attributes[attribute.to_sym] = self.class.encrypted_attributes[attribute.to_sym].merge(:iv => iv.unpack("m").first) if (iv && !iv.empty?)
341
+ end
342
+
343
+ def load_salt_for_attribute(attribute, encrypted_attribute_name)
344
+ 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])
345
+ self.class.encrypted_attributes[attribute.to_sym] = self.class.encrypted_attributes[attribute.to_sym].merge(:salt => salt)
346
+ end
303
347
  end
304
348
  end
305
349
 
@@ -6,6 +6,14 @@ if defined?(ActiveRecord::Base)
6
6
  base.class_eval do
7
7
  attr_encrypted_options[:encode] = true
8
8
  class << self; alias_method_chain :method_missing, :attr_encrypted; end
9
+
10
+ def assign_attributes_with_attr_encrypted(*args)
11
+ attributes = args.shift
12
+ encrypted_attributes = self.class.encrypted_attributes.keys
13
+ assign_attributes_without_attr_encrypted attributes.except(*encrypted_attributes), *args
14
+ assign_attributes_without_attr_encrypted attributes.slice(*encrypted_attributes), *args
15
+ end
16
+ alias_method_chain :assign_attributes, :attr_encrypted
9
17
  end
10
18
  end
11
19
 
@@ -16,6 +24,7 @@ if defined?(ActiveRecord::Base)
16
24
  def attr_encrypted(*attrs)
17
25
  define_attribute_methods rescue nil
18
26
  super
27
+ undefine_attribute_methods
19
28
  attrs.reject { |attr| attr.is_a?(Hash) }.each { |attr| alias_method "#{attr}_before_type_cast", attr }
20
29
  end
21
30
 
@@ -2,8 +2,8 @@ module AttrEncrypted
2
2
  # Contains information about this gem's version
3
3
  module Version
4
4
  MAJOR = 1
5
- MINOR = 2
6
- PATCH = 1
5
+ MINOR = 3
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
  #
@@ -1,62 +1,71 @@
1
- # -*- encoding: utf-8 -*-
2
1
  require File.expand_path('../test_helper', __FILE__)
3
2
 
4
3
  ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
5
4
 
6
- def create_people_table
5
+ def create_tables
7
6
  silence_stream(STDOUT) do
8
7
  ActiveRecord::Schema.define(:version => 1) do
9
8
  create_table :people do |t|
10
9
  t.string :encrypted_email
11
10
  t.string :password
12
11
  t.string :encrypted_credentials
13
- t.string :salt
12
+ t.binary :salt
13
+ t.string :encrypted_email_salt
14
+ t.string :encrypted_credentials_salt
15
+ t.string :encrypted_email_iv
16
+ t.string :encrypted_credentials_iv
17
+ end
18
+ create_table :accounts do |t|
19
+ t.string :encrypted_password
20
+ t.string :encrypted_password_iv
21
+ t.string :encrypted_password_salt
14
22
  end
15
23
  end
16
24
  end
17
25
  end
18
26
 
19
27
  # The table needs to exist before defining the class
20
- create_people_table
28
+ create_tables
21
29
 
22
30
  ActiveRecord::MissingAttributeError = ActiveModel::MissingAttributeError unless defined?(ActiveRecord::MissingAttributeError)
23
31
 
24
32
  class Person < ActiveRecord::Base
25
- attr_encrypted :email, :key => 'a secret key'
26
- attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => 'some private key') }, :marshal => true
33
+ self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
34
+ attr_encrypted :email, :key => SECRET_KEY
35
+ attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => SECRET_KEY) }, :marshal => true
27
36
 
28
- ActiveSupport::Deprecation.silenced = true
29
- def after_initialize; end
30
- ActiveSupport::Deprecation.silenced = false
31
37
 
32
38
  after_initialize :initialize_salt_and_credentials
33
39
 
34
40
  protected
35
41
 
36
- def initialize_salt_and_credentials
37
- self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(5)).to_s)
38
- self.credentials ||= { :username => 'example', :password => 'test' }
39
- rescue ActiveRecord::MissingAttributeError
40
- end
42
+ def initialize_salt_and_credentials
43
+ self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(1000)).to_s)[0..15]
44
+ self.credentials ||= { :username => 'example', :password => 'test' }
45
+ end
41
46
  end
42
47
 
43
48
  class PersonWithValidation < Person
44
49
  validates_presence_of :email
45
- validates_uniqueness_of :encrypted_email
50
+ end
51
+
52
+ class Account < ActiveRecord::Base
53
+ attr_accessor :key
54
+ attr_encrypted :password, :key => Proc.new {|account| account.key}
55
+ end
56
+
57
+ class PersonWithSerialization < ActiveRecord::Base
58
+ self.table_name = 'people'
59
+ attr_encrypted :email, :key => 'a secret key'
60
+ serialize :password
46
61
  end
47
62
 
48
63
  class ActiveRecordTest < Test::Unit::TestCase
49
64
 
50
65
  def setup
51
66
  ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
52
- create_people_table
53
- end
54
-
55
- def test_should_decrypt_with_correct_encoding
56
- if defined?(Encoding)
57
- @person = Person.create :email => 'test@example.com'
58
- assert_equal 'UTF-8', Person.find(:first).email.encoding.name
59
- end
67
+ create_tables
68
+ Account.create!(:key => SECRET_KEY, :password => "password")
60
69
  end
61
70
 
62
71
  def test_should_encrypt_email
@@ -73,28 +82,6 @@ class ActiveRecordTest < Test::Unit::TestCase
73
82
  assert_equal @person.credentials, Person.find(:first).credentials
74
83
  end
75
84
 
76
- def test_should_find_by_email
77
- @person = Person.create(:email => 'test@example.com')
78
- assert_equal @person, Person.find_by_email('test@example.com')
79
- end
80
-
81
- def test_should_find_by_email_and_password
82
- Person.create(:email => 'test@example.com', :password => 'invalid')
83
- @person = Person.create(:email => 'test@example.com', :password => 'test')
84
- assert_equal @person, Person.find_by_email_and_password('test@example.com', 'test')
85
- end
86
-
87
- def test_should_scope_by_email
88
- @person = Person.create(:email => 'test@example.com')
89
- assert_equal @person, Person.scoped_by_email('test@example.com').find(:first) rescue NoMethodError
90
- end
91
-
92
- def test_should_scope_by_email_and_password
93
- Person.create(:email => 'test@example.com', :password => 'invalid')
94
- @person = Person.create(:email => 'test@example.com', :password => 'test')
95
- assert_equal @person, Person.scoped_by_email_and_password('test@example.com', 'test').find(:first) rescue NoMethodError
96
- end
97
-
98
85
  def test_should_encode_by_default
99
86
  assert Person.attr_encrypted_options[:encode]
100
87
  end
@@ -105,12 +92,21 @@ class ActiveRecordTest < Test::Unit::TestCase
105
92
  assert !@person.errors[:email].empty? || @person.errors.on(:email)
106
93
  end
107
94
 
108
- def test_should_validate_uniqueness_of_email
109
- @person = PersonWithValidation.new :email => 'test@example.com'
110
- assert @person.save
111
- @person2 = PersonWithValidation.new :email => @person.email
112
- assert !@person2.valid?
113
- assert !@person2.errors[:encrypted_email].empty? || @person2.errors.on(:encrypted_email)
95
+ def test_should_encrypt_decrypt_with_iv
96
+ @person = Person.create :email => 'test@example.com'
97
+ @person2 = Person.find(@person.id)
98
+ assert_not_nil @person2.encrypted_email_iv
99
+ assert_equal 'test@example.com', @person2.email
100
+ end
101
+
102
+ def test_should_ensure_attributes_can_be_deserialized
103
+ @person = PersonWithSerialization.new :email => 'test@example.com', :password => %w(an array of strings)
104
+ @person.save
105
+ assert_equal @person.password, %w(an array of strings)
114
106
  end
115
107
 
116
- end
108
+ def test_should_create_an_account_regardless_of_arguments_order
109
+ Account.create!(:key => SECRET_KEY, :password => "password")
110
+ Account.create!(:password => "password" , :key => SECRET_KEY)
111
+ end
112
+ end
@@ -1,4 +1,4 @@
1
- # -*- encoding: utf-8 -*-
1
+ # encoding: UTF-8
2
2
  require File.expand_path('../test_helper', __FILE__)
3
3
 
4
4
  class SillyEncryptor
@@ -12,22 +12,23 @@ class SillyEncryptor
12
12
  end
13
13
 
14
14
  class User
15
- self.attr_encrypted_options[:key] = Proc.new { |user| user.class.to_s } # default key
15
+ self.attr_encrypted_options[:key] = Proc.new { |user| SECRET_KEY } # default key
16
+ self.attr_encrypted_options[:mode] = :per_attribute_iv_and_salt
16
17
 
17
- attr_encrypted :email, :without_encoding, :key => 'secret key'
18
+ attr_encrypted :email, :without_encoding, :key => SECRET_KEY
18
19
  attr_encrypted :password, :prefix => 'crypted_', :suffix => '_test'
19
- attr_encrypted :ssn, :key => :salt, :attribute => 'ssn_encrypted'
20
+ attr_encrypted :ssn, :key => :secret_key, :attribute => 'ssn_encrypted'
20
21
  attr_encrypted :credit_card, :encryptor => SillyEncryptor, :encrypt_method => :silly_encrypt, :decrypt_method => :silly_decrypt, :some_arg => 'test'
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
22
+ attr_encrypted :with_encoding, :key => SECRET_KEY, :encode => true
23
+ attr_encrypted :with_custom_encoding, :key => SECRET_KEY, :encode => 'm'
24
+ attr_encrypted :with_marshaling, :key => SECRET_KEY, :marshal => true
25
+ attr_encrypted :with_true_if, :key => SECRET_KEY, :if => true
26
+ attr_encrypted :with_false_if, :key => SECRET_KEY, :if => false
27
+ attr_encrypted :with_true_unless, :key => SECRET_KEY, :unless => true
28
+ attr_encrypted :with_false_unless, :key => SECRET_KEY, :unless => false
29
+ attr_encrypted :with_if_changed, :key => SECRET_KEY, :if => :should_encrypt
29
30
 
30
- attr_encryptor :aliased, :key => 'secret_key'
31
+ attr_encryptor :aliased, :key => SECRET_KEY
31
32
 
32
33
  attr_accessor :salt
33
34
  attr_accessor :should_encrypt
@@ -36,6 +37,10 @@ class User
36
37
  self.salt = Time.now.to_i.to_s
37
38
  self.should_encrypt = true
38
39
  end
40
+
41
+ def secret_key
42
+ SECRET_KEY
43
+ end
39
44
  end
40
45
 
41
46
  class Admin < User
@@ -134,35 +139,20 @@ class AttrEncryptedTest < Test::Unit::TestCase
134
139
  assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
135
140
  end
136
141
 
137
- def test_should_decrypt_utf8_with_encoding
138
- encrypted = User.encrypt_with_encoding("test\xC2\xA0utf-8\xC2\xA0text")
139
- assert_equal "test\xC2\xA0utf-8\xC2\xA0text", User.decrypt_with_encoding(encrypted)
140
- assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
141
- end
142
-
143
142
  def test_should_encrypt_with_custom_encoding
144
- assert_equal User.encrypt_with_custom_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
143
+ assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
145
144
  end
146
145
 
147
146
  def test_should_decrypt_with_custom_encoding
148
- encrypted = User.encrypt_with_custom_encoding('test')
149
- assert_equal 'test', User.decrypt_with_custom_encoding(encrypted)
150
- assert_equal User.decrypt_with_custom_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
147
+ encrypted = User.encrypt_with_encoding('test')
148
+ assert_equal 'test', User.decrypt_with_encoding(encrypted)
149
+ assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m').first)
151
150
  end
152
151
 
153
152
  def test_should_encrypt_with_marshaling
154
153
  @user = User.new
155
154
  @user.with_marshaling = [1, 2, 3]
156
155
  assert_not_nil @user.encrypted_with_marshaling
157
- assert_equal User.encrypt_with_marshaling([1, 2, 3]), @user.encrypted_with_marshaling
158
- end
159
-
160
- def test_should_decrypt_with_marshaling
161
- encrypted = User.encrypt_with_marshaling([1, 2, 3])
162
- @user = User.new
163
- assert_nil @user.with_marshaling
164
- @user.encrypted_with_marshaling = encrypted
165
- assert_equal [1, 2, 3], @user.with_marshaling
166
156
  end
167
157
 
168
158
  def test_should_use_custom_encryptor_and_crypt_method_names_and_arguments
@@ -174,7 +164,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
174
164
  assert_nil @user.ssn_encrypted
175
165
  @user.ssn = 'testing'
176
166
  assert_not_nil @user.ssn_encrypted
177
- assert_equal Encryptor.encrypt(:value => 'testing', :key => @user.salt), @user.ssn_encrypted
167
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.ssn_encrypted_iv.unpack("m").first, :salt => @user.ssn_encrypted_salt )
168
+ assert_equal encrypted, @user.ssn_encrypted
178
169
  end
179
170
 
180
171
  def test_should_evaluate_a_key_passed_as_a_proc
@@ -182,7 +173,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
182
173
  assert_nil @user.crypted_password_test
183
174
  @user.password = 'testing'
184
175
  assert_not_nil @user.crypted_password_test
185
- assert_equal Encryptor.encrypt(:value => 'testing', :key => 'User'), @user.crypted_password_test
176
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
177
+ assert_equal encrypted, @user.crypted_password_test
186
178
  end
187
179
 
188
180
  def test_should_use_options_found_in_the_attr_encrypted_options_attribute
@@ -190,7 +182,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
190
182
  assert_nil @user.crypted_password_test
191
183
  @user.password = 'testing'
192
184
  assert_not_nil @user.crypted_password_test
193
- assert_equal Encryptor.encrypt(:value => 'testing', :key => 'User'), @user.crypted_password_test
185
+ encrypted = Encryptor.encrypt(:value => 'testing', :key => SECRET_KEY, :iv => @user.crypted_password_test_iv.unpack("m").first, :salt => @user.crypted_password_test_salt)
186
+ assert_equal encrypted, @user.crypted_password_test
194
187
  end
195
188
 
196
189
  def test_should_inherit_encrypted_attributes
@@ -232,7 +225,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
232
225
  assert_nil @user.encrypted_with_true_if
233
226
  @user.with_true_if = 'testing'
234
227
  assert_not_nil @user.encrypted_with_true_if
235
- assert_equal Encryptor.encrypt(:value => 'testing', :key => 'secret key'), @user.encrypted_with_true_if
228
+ 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)
229
+ assert_equal encrypted, @user.encrypted_with_true_if
236
230
  end
237
231
 
238
232
  def test_should_not_encrypt_with_false_if
@@ -248,7 +242,8 @@ class AttrEncryptedTest < Test::Unit::TestCase
248
242
  assert_nil @user.encrypted_with_false_unless
249
243
  @user.with_false_unless = 'testing'
250
244
  assert_not_nil @user.encrypted_with_false_unless
251
- assert_equal Encryptor.encrypt(:value => 'testing', :key => 'secret key'), @user.encrypted_with_false_unless
245
+ 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)
246
+ assert_equal encrypted, @user.encrypted_with_false_unless
252
247
  end
253
248
 
254
249
  def test_should_not_encrypt_with_true_unless
@@ -268,7 +263,7 @@ class AttrEncryptedTest < Test::Unit::TestCase
268
263
  @user.with_if_changed = "encrypt_stuff"
269
264
  @user.stubs(:instance_variable_get).returns(nil)
270
265
  @user.stubs(:instance_variable_set).raises("BadStuff")
271
- assert_raise RuntimeError do
266
+ assert_raise RuntimeError do
272
267
  @user.with_if_changed
273
268
  end
274
269