Module: AttrEncryption
- Defined in:
- lib/attr_encryption.rb,
lib/attr_encryption/adapters/active_record.rb
Overview
Adds attr_accessors that encrypt and decrypt an object’s attributes
Defined Under Namespace
Modules: Adapters, InstanceMethods
Class Method Summary collapse
-
.extended(base) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#attr_encrypted(*attributes) ⇒ Object
(also: #attr_encryptor)
Generates attr_accessors that encrypt and decrypt attributes transparently.
-
#attr_encrypted?(attribute) ⇒ Boolean
Checks if an attribute is configured with
attr_encrypted. -
#attr_encrypted_options ⇒ Object
Default options to use with calls to
attr_encrypted. -
#cleanse_value(value, options) ⇒ Object
Cleans up the value to ensure we don’t get empty strings the db when we should be getting nils.
-
#decrypt(attribute, encrypted_value, options = {}) ⇒ Object
Decrypts a value for the attribute specified.
-
#encrypt(attribute, value, options = {}) ⇒ Object
Encrypts a value for the attribute specified.
-
#encrypted_attributes ⇒ Object
Contains a hash of encrypted attributes with virtual attribute names as keys and their corresponding options as values.
-
#method_missing(method, *arguments, &block) ⇒ Object
Forwards calls to :encrypt_#attribute or :decrypt_#attribute to the corresponding encrypt or decrypt method if attribute was configured with attr_encrypted.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *arguments, &block) ⇒ Object
Forwards calls to :encrypt_#attribute or :decrypt_#attribute to the corresponding encrypt or decrypt method if attribute was configured with attr_encrypted
Example
class User
attr_encrypted :email, :key => 'my secret key'
end
User.encrypt_email('SOME_ENCRYPTED_EMAIL_STRING')
261 262 263 264 265 266 267 |
# File 'lib/attr_encryption.rb', line 261 def method_missing(method, *arguments, &block) if method.to_s =~ /\A((en|de)crypt)_(.+)\z/ && attr_encrypted?($3) send($1, $3, *arguments) else super end end |
Class Method Details
.extended(base) ⇒ Object
:nodoc:
9 10 11 12 13 14 15 |
# File 'lib/attr_encryption.rb', line 9 def self.extended(base) # :nodoc: base.class_eval do include InstanceMethods attr_writer :attr_encrypted_options , @encrypted_attributes = {}, {} end end |
Instance Method Details
#attr_encrypted(*attributes) ⇒ Object Also known as: attr_encryptor
Generates attr_accessors that encrypt and decrypt attributes transparently
Options (any other options you specify are passed to the encryptor’s encrypt and decrypt methods)
:attribute => The name of the referenced encrypted attribute. For example
<tt>attr_accessor :email, :attribute => :ee</tt> would generate an
attribute named 'ee' to store the encrypted email. This is useful when defining
one attribute to encrypt at a time or when the :prefix and :suffix options
aren't enough. Defaults to nil.
:type => The data type of the value to be encrypted/decrypted. Can be 'date', 'datetime', 'binary' or 'text'.
When encrypting, all values will use their string value (value.to_s). When decrypting,
the type of the value will determine what is returned. For example:
type = 'date': Date.parse(decrypted_value)
type = 'time': DateTime.parse(decrypted_value)
type = 'binary': decrypted_value
type = 'text': decrypted_value.force_encoding('utf-8')
:prefix => A prefix used to generate the name of the referenced encrypted attributes.
For example <tt>attr_accessor :email, :password, :prefix => 'crypted_'</tt> would
generate attributes named 'crypted_email' and 'crypted_password' to store the
encrypted email and password. Defaults to ''.
:suffix => A suffix used to generate the name of the referenced encrypted attributes.
For example <tt>attr_accessor :email, :password, :suffix => '_encrypted'</tt>
would generate attributes named 'email_encrypted' and 'password_encrypted' to store the
encrypted email. Defaults to '_enc'.
:preencrypt => The symbol a method that should be run on an attribute immediately prior
to marshalling and encrypting. This could be used for things like stripping white-space
from values or other sorts of pre-processing. Defaults to nil.
:key => The encryption key. This option may not be required if you're using a custom encryptor. If you pass
a symbol representing an instance method then the :key option will be replaced with the result of the
method before being passed to the encryptor. Objects that respond to :call are evaluated as well (including procs).
Any other key types will be passed directly to the encryptor. TODO (DJS): We'll see if we need this.
:encode => If set to true, attributes will be encoded as well as encrypted. This is useful if you're
planning on storing the encrypted attributes in a database. The default encoding is 'm' (base64),
however this can be overwritten by setting the :encode option to some other encoding string instead of
just 'true'. See http://www.ruby-doc.org/core/classes/Array.html#M002245 for more encoding directives.
Defaults to false unless you're using it with ActiveRecord, DataMapper, or Sequel. TODO(DJS): We'll see if we need this.
:default_encoding => Defaults to 'm' (base64). TODO(DJS): Hmmm. See above
:marshal => If set to true, attributes will be marshaled as well as encrypted. This is useful if you're planning
on encrypting something other than a string. Defaults to false unless you're using it with ActiveRecord
or DataMapper. TODO(DJS): Don't want to use this by default in our encryption since we want to be able to query...
:marshaler => The object to use for marshaling. Defaults to Marshal.
:dump_method => The dump method name to call on the <tt>:marshaler</tt> object to. Defaults to 'dump'.
:load_method => The load method name to call on the <tt>:marshaler</tt> object. Defaults to 'load'.
:encryptor => The object to use for encrypting. Defaults to Encryptor. TODO(DJS): Need to changed this to indicate our encryptor
:encrypt_method => The encrypt method name to call on the <tt>:encryptor</tt> object. Defaults to 'encrypt'. TODO(DJS): Verify this.
:decrypt_method => The decrypt method name to call on the <tt>:encryptor</tt> object. Defaults to 'decrypt'. TODO(DJS): Verify this.
:if => Attributes are only encrypted if this option evaluates to true. If you pass a symbol representing an instance
method then the result of the method will be evaluated. Any objects that respond to <tt>:call</tt> are evaluated as well.
Defaults to true.
:unless => Attributes are only encrypted if this option evaluates to false. If you pass a symbol representing an instance
method then the result of the method will be evaluated. Any objects that respond to <tt>:call</tt> are evaluated as well.
Defaults to false.
You can specify your own default options
TODO(DJS): Need to rework the examples.
class User
# now all attributes will be encoded and marshaled by default
.merge!(:encode => true, :marshal => true, :some_other_option => true)
attr_encrypted :configuration, :key => 'my secret key'
end
Example
class User
attr_encrypted :email, :credit_card, :key => 'some secret key'
attr_encrypted :configuration, :key => 'some other secret key', :marshal => true
end
@user = User.new
@user.encrypted_email # nil
@user.email? # false
@user.email = '[email protected]'
@user.email? # true
@user.encrypted_email # returns the encrypted version of '[email protected]'
@user.configuration = { :time_zone => 'UTC' }
@user.encrypted_configuration # returns the encrypted version of configuration
See README for more examples
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 |
# File 'lib/attr_encryption.rb', line 115 def attr_encrypted(*attributes) = { :prefix => '', :suffix => '_enc', :if => true, :unless => false, :encode => false, :key => $encryption_key, :type => 'text', :default_encoding => 'm', :preenrypt => nil, :marshal => false, :marshaler => Marshal, :dump_method => 'dump', :load_method => 'load', :encryptor => MySQLEncryptor.instance, :encrypt_method => 'encrypt', :decrypt_method => 'decrypt' }.merge!().merge!(attributes.last.is_a?(Hash) ? attributes.pop : {}) [:encode] = [:default_encoding] if [:encode] == true attributes.each do |attribute| encrypted_attribute_name = ([:attribute] ? [:attribute] : [[:prefix], attribute, [:suffix]].join).to_sym instance_methods_as_symbols = instance_methods.collect { |method| method.to_sym } attr_reader encrypted_attribute_name unless instance_methods_as_symbols.include?(encrypted_attribute_name) attr_writer encrypted_attribute_name unless instance_methods_as_symbols.include?(:"#{encrypted_attribute_name}=") define_method(attribute) do cached_value = instance_variable_get("@#{attribute}") value = cached_value && [:type] == 'date' && cached_value.is_a?(Date) ? cached_value : nil value || instance_variable_set("@#{attribute}", decrypt(attribute, send(encrypted_attribute_name))) # instance_variable_get("@#{attribute}") || instance_variable_set("@#{attribute}", decrypt(attribute, send(encrypted_attribute_name))) end define_method("#{attribute}=") do |value| send("#{encrypted_attribute_name}=", encrypt(attribute, value)) instance_variable_set("@#{attribute}", value) end define_method("#{attribute}?") do value = send(attribute) value.respond_to?(:empty?) ? !value.empty? : !!value end encrypted_attributes[attribute.to_sym] = .merge(:attribute => encrypted_attribute_name) end end |
#attr_encrypted?(attribute) ⇒ Boolean
Checks if an attribute is configured with attr_encrypted
Example
class User
attr_accessor :name
attr_encrypted :email
end
User.attr_encrypted?(:name) # false
User.attr_encrypted?(:email) # true
184 185 186 |
# File 'lib/attr_encryption.rb', line 184 def attr_encrypted?(attribute) encrypted_attributes.has_key?(attribute.to_sym) end |
#attr_encrypted_options ⇒ Object
Default options to use with calls to attr_encrypted
It will inherit existing options from its superclass
169 170 171 |
# File 'lib/attr_encryption.rb', line 169 def ||= superclass..dup end |
#cleanse_value(value, options) ⇒ Object
Cleans up the value to ensure we don’t get empty strings the db when we should be getting nils.
232 233 234 235 |
# File 'lib/attr_encryption.rb', line 232 def cleanse_value(value, ) return nil if value.is_a?(String) && value.empty? && [:type] == 'date' value end |
#decrypt(attribute, encrypted_value, options = {}) ⇒ Object
Decrypts a value for the attribute specified
Example
class User
attr_encrypted :email
end
email = User.decrypt(:email, 'SOME_ENCRYPTED_EMAIL_STRING')
197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/attr_encryption.rb', line 197 def decrypt(attribute, encrypted_value, = {}) = encrypted_attributes[attribute.to_sym].merge() if [:if] && ![:unless] && !encrypted_value.nil? && !(encrypted_value.is_a?(String) && encrypted_value.empty?) encrypted_value = encrypted_value.unpack([:encode]).first if [:encode] value = [:encryptor].send([:decrypt_method], .merge!(:value => encrypted_value)) value = [:marshaler].send([:load_method], value) if [:marshal] value else encrypted_value end end |
#encrypt(attribute, value, options = {}) ⇒ Object
Encrypts a value for the attribute specified
Example
class User
attr_encrypted :email
end
encrypted_email = User.encrypt(:email, '[email protected]')
218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/attr_encryption.rb', line 218 def encrypt(attribute, value, = {}) = encrypted_attributes[attribute.to_sym].merge() if [:if] && ![:unless] && !value.nil? && !(value.is_a?(String) && value.empty?) value = [:preencrypt] ? (value.is_a?(String) ? value.send([:preencrypt]) : value) : value value = [:marshal] ? [:marshaler].send([:dump_method], value) : value.to_s encrypted_value = [:encryptor].send([:encrypt_method], .merge!(:value => value)) encrypted_value = [encrypted_value].pack([:encode]) if [:encode] encrypted_value else cleanse_value value, end end |
#encrypted_attributes ⇒ Object
Contains a hash of encrypted attributes with virtual attribute names as keys and their corresponding options as values
Example
class User
attr_encrypted :email, :key => 'my secret key'
end
User.encrypted_attributes # { :email => { :attribute => 'encrypted_email', :key => 'my secret key' } }
247 248 249 |
# File 'lib/attr_encryption.rb', line 247 def encrypted_attributes @encrypted_attributes ||= superclass.encrypted_attributes.dup end |