Module: AttrEncrypted
- Defined in:
- lib/attr_encrypted.rb,
lib/attr_encrypted/version.rb,
lib/attr_encrypted/adapters/sequel.rb,
lib/attr_encrypted/adapters/data_mapper.rb,
lib/attr_encrypted/adapters/active_record.rb
Overview
Adds attr_accessors that encrypt and decrypt an object’s attributes
Defined Under Namespace
Modules: Adapters, InstanceMethods, Version
Class Method Summary collapse
-
.extended(base) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#attr_encrypted(*attributes) ⇒ Object
(also: #attr_encryptor)
encode_salt: Defaults to true.
-
#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
. -
#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.
- #not_empty?(value) ⇒ Boolean
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')
302 303 304 305 306 307 308 |
# File 'lib/attr_encrypted.rb', line 302 def method_missing(method, *arguments, &block) if method.to_s =~ /^((en|de)crypt)_(.+)$/ && attr_encrypted?($3) send($1, $3, *arguments) else super end end |
Class Method Details
.extended(base) ⇒ Object
:nodoc:
7 8 9 10 11 12 13 |
# File 'lib/attr_encrypted.rb', line 7 def self.extended(base) # :nodoc: base.class_eval do include InstanceMethods attr_writer :attr_encrypted_options @attr_encrypted_options, @encrypted_attributes = {}, {} end end |
Instance Method Details
#attr_encrypted(*attributes) ⇒ Object Also known as: attr_encryptor
encode_salt: Defaults to true.
default_encoding: Defaults to 'm' (base64).
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.
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.
encrypt_method: The encrypt method name to call on the <tt>:encryptor</tt> object.
Defaults to 'encrypt'.
decrypt_method: The decrypt method name to call on the <tt>:encryptor</tt> object.
Defaults to 'decrypt'.
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.
mode: Selects encryption mode for attribute: choose <tt>:single_iv_and_salt</tt> for compatibility
with the old attr_encrypted API: the IV is derived from the encryption key by the underlying Encryptor class; salt is not used.
The <tt>:per_attribute_iv_and_salt</tt> mode uses a per-attribute IV and salt. The salt is used to derive a unique key per attribute.
A <tt>:per_attribute_iv</default> mode derives a unique IV per attribute; salt is not used.
Defaults to <tt>:per_attribute_iv</tt>.
allow_empty_value: Attributes which have nil or empty string values will not be encrypted unless this option
has a truthy value.
You can specify your own default options
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, 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
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 |
# File 'lib/attr_encrypted.rb', line 134 def attr_encrypted(*attributes) = attributes.last.is_a?(Hash) ? attributes.pop : {} = .dup.merge!().merge!() [:encode] = [:default_encoding] if [:encode] == true [:encode_iv] = [:default_encoding] if [:encode_iv] == true [:encode_salt] = [:default_encoding] if [:encode_salt] == true attributes.each do |attribute| encrypted_attribute_name = ([:attribute] ? [:attribute] : [[:prefix], attribute, [:suffix]].join).to_sym instance_methods_as_symbols = attribute_instance_methods_as_symbols if attribute_instance_methods_as_symbols_available? 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}=") iv_name = "#{encrypted_attribute_name}_iv".to_sym attr_reader iv_name unless instance_methods_as_symbols.include?(iv_name) attr_writer iv_name unless instance_methods_as_symbols.include?(:"#{iv_name}=") salt_name = "#{encrypted_attribute_name}_salt".to_sym attr_reader salt_name unless instance_methods_as_symbols.include?(salt_name) attr_writer salt_name unless instance_methods_as_symbols.include?(:"#{salt_name}=") end define_method(attribute) do 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
223 224 225 |
# File 'lib/attr_encrypted.rb', line 223 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
183 184 185 |
# File 'lib/attr_encrypted.rb', line 183 def @attr_encrypted_options ||= superclass..dup 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')
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
# File 'lib/attr_encrypted.rb', line 236 def decrypt(attribute, encrypted_value, = {}) = encrypted_attributes[attribute.to_sym].merge() if [:if] && ![:unless] && not_empty?(encrypted_value) encrypted_value = encrypted_value.unpack([:encode]).first if [:encode] value = [:encryptor].send([:decrypt_method], .merge!(value: encrypted_value)) if [:marshal] value = [:marshaler].send([:load_method], value) elsif defined?(Encoding) encoding = Encoding.default_internal || Encoding.default_external value = value.force_encoding(encoding.name) end 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]')
262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/attr_encrypted.rb', line 262 def encrypt(attribute, value, = {}) = encrypted_attributes[attribute.to_sym].merge() if [:if] && ![:unless] && ([:allow_empty_value] || not_empty?(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 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' } }
288 289 290 |
# File 'lib/attr_encrypted.rb', line 288 def encrypted_attributes @encrypted_attributes ||= superclass.encrypted_attributes.dup end |
#not_empty?(value) ⇒ Boolean
274 275 276 |
# File 'lib/attr_encrypted.rb', line 274 def not_empty?(value) !value.nil? && !(value.is_a?(String) && value.empty?) end |