Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactored determination of the encryption mode to more easily allow …

…for extensions by other developers.

Added support for dynamic creation of the salt for an sha-encrypted string.
Encrypted strings for ActiveRecord are now stored in an attribute called crypted_* (where * is the name of the actual attribute).
Encrypted strings are now only generated immediately before validation rather than immediately upon assignment of the actual attribute.
Moved some of the initialization code out of init.rb.
Continued moving some code over to the new framework from the original sentry plugin.
  • Loading branch information...
commit 4de5055dd02c1dcb75120415e1fda34ee618c230 1 parent 48a2ff0
@obrie obrie authored
View
6 init.rb
@@ -1,5 +1 @@
-require 'encrypted_strings'
-
-class ::Integer #:nodoc:
- include PluginAWeek::CoreExtensions::String::EncryptedStrings
-end
+require 'encrypted_strings'
View
102 lib/encrypted_strings.rb
@@ -22,10 +22,12 @@
require 'openssl'
require 'base64'
-require File.join('encrypted_strings', 'encrypted_string')
-require File.join('encrypted_strings', 'symmetrically_encrypted_string')
-require File.join('encrypted_strings', 'asymmetrically_encrypted_string')
-require File.join('encrypted_strings', 'sha_encrypted_string')
+require File.join('encrypted_strings', 'core_ext', 'encrypted_string')
+require File.join('encrypted_strings', 'core_ext', 'symmetrically_encrypted_string')
+require File.join('encrypted_strings', 'core_ext', 'asymmetrically_encrypted_string')
+require File.join('encrypted_strings', 'core_ext', 'sha_encrypted_string')
+
+require File.join('encrypted_strings', 'active_record', 'encrypts')
class NoKeyError < StandardError
end
@@ -40,71 +42,59 @@ module PluginAWeek #:nodoc:
module CoreExtensions #:nodoc:
module String #:nodoc:
module EncryptedStrings
+ def self.included(base) #:nodoc:
+ base.class_eval do
+ alias_method :equals_without_encryption, :==
+ alias_method :==, :equals_with_encryption
+ end
+ end
+
#
#
- def encrypt(mode = :sha, options = {})
- options[:encrypt] = true
+ def encrypt(*args)
+ options = args.last.is_a?(::Hash) ? args.pop : {}
+ mode = (args.first || :sha).to_sym
- case mode
- when :sha
- SHAEncryptedString.new(self, options)
- when :asymmetric, :asymmetrical
- AsymmetricallyEncryptedString(self, options)
- when :symmetric, :symmetrical
- SymmetricallyEncryptedString.new(self, options)
- else
- raise ArgumentError, "Invalid encryption mode: #{mode}"
- end
+ send("encrypt_#{mode}", options)
+ end
+
+ def encrypt_sha(options = {})
+ create_encrypted_string(SHAEncryptedString, options)
end
- end
- end
- end
-
- module Encrypts #:nodoc:
- def self.included(base) #:nodoc:
- base.extend(MacroMethods)
- end
-
- module MacroMethods
- #
- #
- def encrypts(attr_name, options = {})
- options.reverse_merge!(
- :mode => :sha
- )
- klass = case options.delete(:mode)
- when :sha
- SHAEncryptedString
- when :asymmetric, :asymmetrically
- AsymmetricallyEncryptedString
- when :symmetric, :symmetrically
- SymmetricallyEncryptedString
+ #
+ #
+ def encrypt_asymmetrically(options = {})
+ create_encrypted_string(AsymmetricallyEncryptedString, options)
end
+ alias_method :encrypt_asymmetric, :encrypt_asymmetrically
- var_name = "@#{attr_name}"
+ #
+ #
+ def encrypt_symmetrically(options = {})
+ create_encrypted_string(SymmetricallyEncryptedString, options)
+ end
+ alias_method :encrypt_symmetric, :encrypt_symmetrically
- # Define the reader
- reader_options = options.dup
- reader_options[:encrypt] = false
- define_method(attr_name) do
- if (data = read_attribute(attr_name)) && !data.is_a?(klass)
- data = instance_variable_get(var_name) || instance_variable_set(var_name, klass.new(data, reader_options))
+ #
+ #
+ def equals_with_encryption(other)
+ if other.is_a?(EncryptedString) && self.class == ::String
+ other == self
+ else
+ equals_without_encryption(other)
end
-
- data
end
- # Define the writer
- define_method("#{attr_name}=") do |data|
- unless data.is_a?(EncryptedString)
- data = klass.new(data, options)
- end
-
- write_attribute(attr_name, data)
- instance_variable_set(var_name, klass.new(data, options))
+ private
+ def create_encrypted_string(klass, options) #:nodoc:
+ klass.new(self, options)
end
end
end
end
end
+
+::String.class_eval do
+ include PluginAWeek::CoreExtensions::String::EncryptedStrings
+end
View
101 lib/encrypted_strings/active_record/encrypts.rb
@@ -0,0 +1,101 @@
+module PluginAWeek #:nodoc:
+ module Encrypts #:nodoc:
+ def self.included(base) #:nodoc:
+ base.extend(MacroMethods)
+ end
+
+ module MacroMethods
+ #
+ def encrypts(attr_name, options = {})
+ encrypts_with(attr_name, SHAEncryptedString, options)
+ end
+ alias_method :encrypts_sha, :encrypts
+
+ #
+ def encrypts_asymmetrically(attr_name, options = {})
+ encrypts_with(attr_names, AsymmetricallyEncryptedString, options)
+ end
+ alias_method :encrypts_asmmetric, :encrypts_asymmetrically
+
+ #
+ def encrypts_symmetrically(attr_name, options = {})
+ encrypts_with(attr_names, SymmetricallyEncryptedString, options)
+ end
+ alias_method :encrypts_symmetric, :encrypts_symmetrically
+
+ private
+ def encrypts_with(attr_name, klass, options = {}) #:nodoc:
+ options.reverse_merge!(
+ :crypted_name => "crypted_#{attr_name}"
+ )
+ crypted_attr_name = options.delete(:crypted_name)
+ raise ArgumentError, 'Attribute name cannot be same as crypted name' if attr_name == crypted_attr_name
+
+ # Creator accessor for the virtual attribute
+ attr_accessor attr_name
+
+ # Define the reader when reading the crypted value from the db
+ var_name = "@#{crypted_attr_name}"
+ reader_options = options.dup
+ reader_options[:encrypt] = false
+ define_method(crypted_attr_name) do
+ if (data = instance_variable_get(var_name)).nil? && (data = read_attribute(crypted_attr_name)) && !data.blank? && !data.is_a?(klass)
+ data = instance_variable_set(var_name, create_encrypted_string(klass, data, reader_options))
+ end
+
+ data
+ end
+
+ # Set the value immediately before validation takes place
+ before_validation do |model|
+ value = model.send(attr_name)
+ return if value.blank?
+
+ unless value.is_a?(EncryptedString)
+ value = model.send(:create_encrypted_string, klass, value, options)
+ end
+
+ model.send("#{crypted_attr_name}=", value)
+ end
+
+ # After saving, be sure to reset the virtual attribute value
+ after_save do |model|
+ model.send("#{attr_name}=", nil)
+ model.send("#{attr_name}_confirmation=", nil) if model.respond_to?("#{attr_name}_confirmation=")
+ end
+
+ include PluginAWeek::Encrypts::InstanceMethods
+ end
+ end
+
+ module InstanceMethods #:nodoc:
+ private
+ def create_encrypted_string(klass, value, options)
+ if klass.respond_to?(:process_options)
+ options = options.dup
+ klass.process_options(self, options)
+ else
+ options
+ end
+
+ klass.new(value, options)
+ end
+ end
+ end
+end
+
+class SHAEncryptedString
+ def self.process_options(model, options) #:nodoc:
+ if (salt_attr_name = options[:salt]) && (salt_attr_name == true || salt_attr_name.is_a?(Symbol))
+ salt_attr_name = 'salt' if salt_attr_name == true
+
+ salt_value = model.send("create_#{salt_attr_name}")
+ model.send("#{salt_attr_name}=", salt_value)
+ options[:salt] = salt_value
+ end
+ end
+end
+
+ActiveRecord::Base.class_eval do
+ include PluginAWeek::Encrypts
+end
View
6 ...rypted_strings/asymmetrically_encrypted_string.rb → ...rings/core_ext/asymmetrically_encrypted_string.rb
@@ -2,16 +2,16 @@
#
class AsymmetricallyEncryptedString < EncryptedString
#
- @@default_private_key_file = nil
cattr_accessor :default_private_key_file
+ @@default_private_key_file = nil
#
- @@default_public_key_file = nil
cattr_accessor :default_public_key_file
+ @@default_public_key_file = nil
#
- @@default_symmetric_algorithm = nil
cattr_accessor :default_symmetric_algorithm
+ @@default_symmetric_algorithm = nil
attr_reader :private_key_file
attr_reader :public_key_file
View
0  lib/encrypted_strings/encrypted_string.rb → lib/encrypted_strings/core_ext/encrypted_string.rb
File renamed without changes
View
2  lib/encrypted_strings/sha_encrypted_string.rb → ...ncrypted_strings/core_ext/sha_encrypted_string.rb
@@ -1,8 +1,8 @@
require 'digest/sha1'
class SHAEncryptedString < EncryptedString
- @@salt = 'salt'
cattr_accessor :salt
+ @@salt = 'salt'
attr_accessor :salt
View
4 ...crypted_strings/symmetrically_encrypted_string.rb → ...trings/core_ext/symmetrically_encrypted_string.rb
@@ -2,12 +2,12 @@
#
class SymmetricallyEncryptedString < EncryptedString
#
- @@default_algorithm = 'DES-EDE3-CBC'
cattr_accessor :default_algorithm
+ @@default_algorithm = 'DES-EDE3-CBC'
#
- @@default_key = nil
cattr_accessor :default_key
+ @@default_key = nil
attr_accessor :algorithm
attr_accessor :key
View
79 lib/encrypted_strings/sentry.rb
@@ -1,79 +0,0 @@
-#module ActiveRecord # :nodoc:
-# module Sentry
-# def self.included(base) # :nodoc:
-# base.extend ClassMethods
-# end
-#
-# module ClassMethods
-# def generates_crypted(attr_name, options = {})
-# mode = options[:mode] || :sha
-# case mode
-# when :sha
-# generates_crypted_hash_of(attr_name)
-# when :asymmetric, :asymmetrical
-# asymmetrically_encrypts(attr_name)
-# when :symmetric, :symmetrical
-# symmetrically_encrypts(attr_name)
-# end
-# end
-#
-# def generates_crypted_hash_of(attribute)
-# before_validation ::Sentry::ShaSentry.new(attribute)
-# attr_accessor attribute
-# end
-#
-# def asymmetrically_encrypts(attr_name)
-# temp_sentry = ::Sentry::AsymmetricSentryCallback.new(attr_name)
-# before_validation temp_sentry
-# after_save temp_sentry
-#
-# define_method(attr_name) do |*optional|
-# send("#{attr_name}!", *optional) rescue nil
-# end
-#
-# define_method("#{attr_name}!") do |*optional|
-# return decrypted_values[attr_name] unless decrypted_values[attr_name].nil?
-# return nil if send("crypted_#{attr_name}").nil?
-# key = optional.shift
-# ::Sentry::AsymmetricSentry.decrypt_from_base64(send("crypted_#{attr_name}"), key)
-# end
-#
-# define_method("#{attr_name}=") do |value|
-# decrypted_values[attr_name] = value
-# nil
-# end
-#
-# private
-# define_method(:decrypted_values) do
-# @decrypted_values ||= {}
-# end
-# end
-#
-# def symmetrically_encrypts(attr_name)
-# temp_sentry = ::Sentry::SymmetricSentryCallback.new(attr_name)
-# before_validation temp_sentry
-# after_save temp_sentry
-#
-# define_method(attr_name) do
-# send("#{attr_name}!") rescue nil
-# end
-#
-# define_method("#{attr_name}!") do
-# return decrypted_values[attr_name] unless decrypted_values[attr_name].nil?
-# return nil if send("crypted_#{attr_name}").nil?
-# ::Sentry::SymmetricSentry.decrypt_from_base64(send("crypted_#{attr_name}"))
-# end
-#
-# define_method("#{attr_name}=") do |value|
-# decrypted_values[attr_name] = value
-# nil
-# end
-#
-# private
-# define_method(:decrypted_values) do
-# @decrypted_values ||= {}
-# end
-# end
-# end
-# end
-#end
View
2  tasks/encrypted_strings.rake
@@ -1,4 +1,4 @@
-require 'sentry'
+#require 'sentry'
desc "Creates a private/public key for asymmetric encryption: rake sentry_key PUB=/path/to/public.key PRIV=/path/to/priv.key [KEY=secret]"
task :encryption_key do
Please sign in to comment.
Something went wrong with that request. Please try again.