active_crypto.rb
7.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
require "ezcrypto.rb"
module ActiveCrypto # :nodoc:
def self.append_features(base) #:nodoc:
super
base.extend(ClassMethods)
end
=begin rdoc
Usage is very simple. You will generally only need the two class methods listed here in your ActiveRecord class model.
== License
ActiveCrypto and EzCrypto are released under the MIT license.
== Support
To contact the author, send mail to pelleb@gmail.com
Also see my blogs at:
http://stakeventures.com and
http://neubia.com
This project was based on code used in my project StakeItOut, where you can securely share web services with your partners.
https://stakeitout.com
(C) 2005 Pelle Braendgaard
=end
module ClassMethods
@@session_keys={}
=begin rdoc
Turn encryption on for this record. List all encrypted attributes
class Document < ActiveRecord::Base
encrypt :title,:body
end
Options are:
<tt>key</tt> - to specify an external KeyHolder, which holds the key used for encrypting and decrypting
<tt>base64</tt> - set to true in order to base64 encode the encrypted attributes. defaults to false
class Document < ActiveRecord::Base
belongs_to :user
encrypt :title,:body,:key=>:user, :base64 => true
end
=end
def encrypt(*attributes)
include ActiveCrypto::Encrypted
before_save :encrypt_attributes
after_save :decrypt_attributes
options=attributes.last.is_a?(Hash) ? attributes.pop : {}
keyholder
if options and options[:key]
module_eval <<-"end;"
def session_key
(send :#{options[:key]} ).send :session_key
end
@@external_key=true
end;
end
base64_encode = (options and options[:base64])
module_eval <<-"end;"
def self.ezcrypto_base64?
#{base64_encode.to_s}
end
end;
self.encrypted_attributes=attributes
end
=begin rdoc
Creates support in this class for holding a key. Adds the following methods:
* enter_password(password,salt="onetwothree")
* set_session_key(key)
* session_key
Use it as follows:
class User < ActiveRecord::Base
has_many :documents
keyholder
end
=end
def keyholder()
include ActiveCrypto::AssociationKeyHolder
after_create :save_session_key
end
=begin rdoc
Clears the session_key array. Generally this is handled automatically as a filter in ActionController. Only use these if you need to
do something out of the ordinary.
=end
def clear_session_keys() #:nodoc:
@@session_keys.clear
end
=begin rdoc
Sets the session_keys array. Only use these if you need to
do something out of the ordinary, as it is handled
=end
def session_keys=(keys) #:nodoc:
@@session_keys=keys
end
def session_keys() #:nodoc:
@@session_keys
end
end
=begin rdoc
This module handles all standard key management features.
=end
module KeyHolder
=begin rdoc
Creates a key for object based on given password and an optional salt.
=end
def enter_password(password,salt="onetwothree")
set_session_key(EzCrypto::Key.with_password(password, salt))
end
=begin rdoc
Decodes the Base64 encoded key and uses it as it's session key
=end
def set_encoded_key(enc)
set_session_key(EzCrypto::Key.decode(enc))
end
=begin rdoc
Sets a session key for the object. This should be a EzCrypto::Key instance.
=end
def set_session_key(key)
@session_key=key
self.decrypt_attributes if self.class.include? Encrypted
end
=begin rdoc
Returns the session_key
=end
def session_key
@session_key
end
end
module AssociationKeyHolder
include ActiveCrypto::KeyHolder
def save_session_key
ActiveRecord::Base.session_keys[session_key_id]=@session_key if @session_key
end
=begin rdoc
Sets a session key for the object. This should be a EzCrypto::Key instance.
=end
def set_session_key(key)
if self.new_record?
@session_key=key
else
ActiveRecord::Base.session_keys[session_key_id]=key
end
decrypt_attributes if self.class.include? Encrypted #if respond_to?(:decrypt_attributes)
end
=begin rdoc
Returns the session_key
=end
def session_key
if self.new_record?
@session_key
else
ActiveRecord::Base.session_keys[session_key_id]
end
end
def session_key_id
"#{self.class.to_s}:#{id}"
end
end
module Encrypted #:nodoc:
def self.append_features(base) #:nodoc:
super
base.extend ClassAccessors
end
module ClassAccessors
def encrypted_attributes
@encrypted_attributes||=[]
end
def encrypted_attributes=(attrs)
@encrypted_attributes=attrs
end
end
protected
def encrypt_attributes
if !is_encrypted?
self.class.encrypted_attributes.each do |key|
value=read_attribute(key)
write_attribute(key,_encrypt(value)) if value
end
@is_encrypted=true
end
true
end
def decrypt_attributes
if is_encrypted?
self.class.encrypted_attributes.each do |key|
value=read_attribute(key)
write_attribute(key,_decrypt(value)) if value
end
@is_encrypted=false
end
true
end
def after_find
@is_encrypted=true
decrypt_attributes unless session_key.nil?
end
private
def is_encrypted?
@is_encrypted
end
def _decrypt(data)
if session_key.nil?
raise MissingKeyError
else
if data
self.class.ezcrypto_base64? ? session_key.decrypt64(data) : session_key.decrypt(data)
else
nil
end
end
end
def _encrypt(data)
if session_key.nil?
raise MissingKeyError
else
if data
self.class.ezcrypto_base64? ? session_key.encrypt64(data) : session_key.encrypt(data)
else
nil
end
end
end
end
module ActionController # :nodoc:
=begin rdoc
This includes some basic support in the ActionController for handling session keys. It creates two filters one before the action and one after.
These do the following:
If the users session already has a 'session_keys' value it loads it into the ActiveRecord::Base.session_keys class field. If not it
clears any existing session_keys.
Leaving the action it stores any session_keys in the corresponding session variable.
These filters are automatically enabled. You do not have to do anything.
To manually clear the session keys call clear_session_keys. This should be done for example as part of a session log off action.
=end
def self.append_features(base) #:nodoc:
super
base.send :prepend_before_filter, :load_session_keys
base.send :prepend_after_filter, :save_session_keys
end
=begin rdoc
Clears the session keys. Call this when a user logs of.
=end
def clear_session_keys
ActiveRecord::Base.clear_session_keys
end
private
def load_session_keys
if session['session_keys']
ActiveRecord::Base.session_keys=session['session_keys']
else
ActiveRecord::Base.clear_session_keys
end
end
def save_session_keys
if ActiveRecord::Base.session_keys.size>0
session['session_keys']=ActiveRecord::Base.session_keys
else
session['session_keys']=nil
end
end
end
class MissingKeyError < RuntimeError
end
end
ActiveRecord::Base.send :include, ActiveCrypto
require 'actionpack'
require 'action_controller'
ActionController::Base.send :include, ActiveCrypto::ActionController